[damned-lies] Adapt form customisations to Django 1.11



commit 58f8fe1c92416e0eea7655755cbf2484fbb27324
Author: Claude Paroz <claude 2xlibre net>
Date:   Tue Apr 4 23:35:33 2017 +0200

    Adapt form customisations to Django 1.11

 README                  |    7 ++---
 requirements.txt        |    2 +-
 vertimus/forms.py       |   65 +++++++++++++++++++++-------------------------
 vertimus/tests/tests.py |   51 ++++++++++++++++++++++++-------------
 4 files changed, 67 insertions(+), 58 deletions(-)
---
diff --git a/README b/README
index daa6cbd..f9a5767 100644
--- a/README
+++ b/README
@@ -8,7 +8,7 @@ You can find the Data model in the /docs directory.
 Requirements
 ============
 
-1 - Django > 1.8.X
+1 - Django > 1.11.X
 
 2 - Python 3.4 (minimal)
     pillow (python-pillow) for hackergotchi checks.
@@ -17,7 +17,7 @@ Requirements
 3 - gettext, intltool, gnome-doc-utils (for stats generation), itstool
 
 4 - [Optional] Django Debug Toolbar
-    git clone git://github.com/dcramer/django-debug-toolbar.git
+    git clone git://github.com/jazzband/django-debug-toolbar.git
     Define USE_DEBUG_TOOLBAR to True in local_settings.py to use it.
 
 5 - [Optional] python-openid and django-openid-auth (see OpenID support
@@ -25,8 +25,7 @@ Requirements
 
 6 - [Optional] python-pyicu for correct sorting in various languages
 
-7 - [Optional] translate-toolkit >= 1.9.0-beta2 (--keeptranslations option for
-    pogrep) for reduced po files.
+7 - [Optional] translate-toolkit >= 2.0 for reduced po files.
 
 Installing all requirements using pip:
 
diff --git a/requirements.txt b/requirements.txt
index 3b28c28..eaf5798 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-django>=1.10
+django>=1.11
 django-debug-toolbar
 markdown
 pyicu
diff --git a/vertimus/forms.py b/vertimus/forms.py
index ebcfbd6..839ba2f 100644
--- a/vertimus/forms.py
+++ b/vertimus/forms.py
@@ -5,55 +5,49 @@ from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
 from django.utils.html import format_html
 from django.utils.translation import ugettext, ugettext_lazy as _
-from vertimus.models import Action, ActionCI
+from vertimus.models import Action, ActionCI, ActionSeparator
 from stats.models import Person
 from stats.utils import po_file_stats
 
 
-class ActionWidget(forms.Select):
-    """ Custom widget to disable the separator option (containing "--------") """
-    def render_options(self, selected_choices):
-        if selected_choices == ['']:
-            selected_choices = []
-        options = super().render_options(selected_choices)
-        options = options.replace('<option value="">--------</option>',
-                                  '<option disabled="disabled">--------</option>')
-        return options
-
-
-class AuthorWidget(forms.Select):
-    """ Custom widget to disable people without full name or email """
-    def render_options(self, selected_choices):
-        output = ["<option value=\"\">--------</option>"]
-        field = self.choices.field
-        for pers in field.queryset.all():
-            val, label = field.prepare_value(pers), field.label_from_instance(pers)
-            selected_html = ''
-            if label == pers.username:
-                selected_html = ' disabled'
-                label = ugettext("%(name)s (full name missing)") % {'name': label}
-            elif not pers.email:
-                selected_html = ' disabled'
-                label = ugettext("%(name)s (email missing)") % {'name': label}
-            elif val in selected_choices:
-                selected_html = ' selected'
-            output.append(format_html('<option value="{0}"{1}>{2}</option>',
-                val, selected_html, label))
-        return '\n'.join(output)
+class DisabledLabel(str):
+    """String subclass to mark some label as disabled."""
+
+
+class DisablableSelect(forms.Select):
+    """Custom widget to allow a Select option to be disabled."""
+    def create_option(self, *args, **kwargs):
+        context = super().create_option(*args, **kwargs)
+        if isinstance(context['label'], DisabledLabel):
+            context['attrs']['disabled'] = True
+            if context['selected']:
+                context['selected'] = False
+                del context['attrs']['selected']
+        return context
+
+
+class AuthorChoiceField(forms.ModelChoiceField):
+    widget = DisablableSelect
+
+    def label_from_instance(self, obj):
+        if str(obj) == obj.username:
+            return DisabledLabel(ugettext("%(name)s (full name missing)") % {'name': obj.username})
+        elif not obj.email:
+            return DisabledLabel(ugettext("%(name)s (email missing)") % {'name': str(obj)})
+        return str(obj)
 
 
 class ActionForm(forms.Form):
     action = forms.ChoiceField(label=_("Action"),
 #        help_text="Choose an action you want to apply",
         choices=(),
-        widget=ActionWidget)
+        widget=DisablableSelect)
     comment = forms.CharField(label=_("Comment"),
 #        help_text="Leave a comment to explain your action",
         max_length=5000,
         required=False,
         widget=forms.Textarea(attrs={'rows':8, 'cols':70}))
-    author = forms.ModelChoiceField(label=_("Commit author"), empty_label=None,
-        queryset=Person.objects.none(), widget=AuthorWidget, required=False)
+    author = AuthorChoiceField(label=_("Commit author"), queryset=Person.objects.none(), required=False)
     sync_master = forms.BooleanField(
         label=_("Sync with master"),
         help_text=_("Try to cherry-pick the commit to the master branch"),
@@ -65,9 +59,10 @@ class ActionForm(forms.Form):
     def __init__(self, state, actions, has_mailing_list, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.actions = actions
-        self.fields['action'].choices = [
-            (act.name, act.description) for act in actions
-        ]
+        self.fields['action'].choices = [(
+            act.name,
+            DisabledLabel(act.description) if isinstance(act, ActionSeparator) else act.description
+        ) for act in actions]
         self.fields['action'].help_link = reverse('help', args=['vertimus_workflow', 1])
         if state and ActionCI in map(type, self.actions):
             self.fields['author'].queryset = state.involved_persons()
diff --git a/vertimus/tests/tests.py b/vertimus/tests/tests.py
index 3c10b19..4ef6964 100644
--- a/vertimus/tests/tests.py
+++ b/vertimus/tests/tests.py
@@ -214,6 +214,29 @@ class VertimusTest(TeamsAndRolesTests):
             action_names = [a.name for a in state.get_available_actions(p)]
             self.assertEqual(action_names, ['AA', 'WC', None, 'IC', 'AA'])
 
+    def test_action_menu(self):
+        state = StateNone(branch=self.b, domain=self.d, language=self.l)
+        state.save()
+        form = ActionForm(state, state.get_available_actions(self.pt), False)
+        self.assertHTMLEqual(
+            str(form['action']),
+            '<select id="id_action" name="action">'
+            '<option value="RT">Reserve for translation</option>'
+            '<option value="WC">Write a comment</option>'
+            '</select>'
+        )
+        form = ActionForm(state, state.get_available_actions(self.pcoo), False)
+        self.assertHTMLEqual(
+            str(form['action']),
+            '<select id="id_action" name="action">'
+            '<option value="RT">Reserve for translation</option>'
+            '<option value="WC">Write a comment</option>'
+            '<option value="" disabled>--------</option>'
+            '<option value="IC">Inform of submission</option>'
+            '<option value="AA">Archive the actions</option>'
+            '</select>'
+        )
+
     def test_action_wc(self):
         state = StateNone(branch=self.b, domain=self.d, language=self.l)
         state.save()
@@ -370,25 +393,17 @@ class VertimusTest(TeamsAndRolesTests):
         self.assertIn(ActionCI, map(type, state.get_available_actions(self.pcoo)))
 
         form = ActionForm(state, state.get_available_actions(self.pcoo), True)
-        self.assertEqual(len(form.fields['author'].choices), 4)
-        choices = list(form.fields['author'].choices)
-        self.assertEqual(
-            [choices[0][1], choices[1][1]],
-            ['John Reviewer', 'John Translator']
-        )
+        self.assertEqual(len(form.fields['author'].choices), 5)
         self.assertEqual(form.fields['author'].initial, self.pr)
-        rendered_form = form.as_p()
-        self.assertIn(
-            '<option value="%d" disabled>ûsername (full name missing)</option>' % pers_no_full_name.pk,
-            rendered_form
-        )
-        self.assertIn(
-            '<option value="%d" disabled>Sven Brkc (email missing)</option>' % pers_no_email.pk,
-            rendered_form
-        )
-        self.assertIn(
-            '<option value="%d">John Translator</option>' % self.pt.pk,
-            rendered_form
+        self.assertHTMLEqual(
+            str(form['author']),
+            '<select id="id_author" name="author">'
+            '<option value="">---------</option>'
+            '<option selected value="%d">John Reviewer</option>'
+            '<option value="%d">John Translator</option>'
+            '<option disabled value="%d">Sven Brkc (email missing)</option>'
+            '<option disabled value="%d">ûsername (full name missing)</option>'
+            '</select>' % (self.pr.pk, self.pt.pk, pers_no_email.pk, pers_no_full_name.pk)
         )
 
     def test_action_ci_no_fullname(self):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]