[PATCH v2 08/10] views: Add patch labels to web UI

Stephen Finucane stephen at that.guru
Sun Oct 14 23:45:39 AEDT 2018


Nothing too complicated here. The forms we have for this need some
serious cleanup/beautification, but that's a problem for another day.

Signed-off-by: Stephen Finucane <stephen at that.guru>
---
 patchwork/forms.py                            | 18 +++++++++++++-
 .../patchwork/partials/patch-list.html        |  8 +++++++
 patchwork/templates/patchwork/submission.html |  7 ++++++
 patchwork/templatetags/patch.py               | 24 +++++++++++++++++++
 patchwork/views/__init__.py                   |  3 +++
 5 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/patchwork/forms.py b/patchwork/forms.py
index ad696a22..89aa8061 100644
--- a/patchwork/forms.py
+++ b/patchwork/forms.py
@@ -13,6 +13,7 @@ from django.db.utils import ProgrammingError
 
 from patchwork.models import Bundle
 from patchwork.models import Patch
+from patchwork.models import Label
 from patchwork.models import State
 from patchwork.models import UserProfile
 
@@ -123,10 +124,14 @@ class PatchForm(forms.ModelForm):
         super(PatchForm, self).__init__(instance=instance, *args, **kwargs)
         self.fields['delegate'] = forms.ModelChoiceField(
             queryset=_get_delegate_qs(project, instance), required=False)
+        self.fields['labels'] = forms.ModelMultipleChoiceField(
+            queryset=Label.objects.filter(
+                Q(project=project) | Q(project=None)),
+            required=False)
 
     class Meta:
         model = Patch
-        fields = ['state', 'archived', 'delegate']
+        fields = ['state', 'archived', 'delegate', 'labels']
 
 
 class OptionalModelChoiceField(forms.ModelChoiceField):
@@ -179,6 +184,10 @@ class MultiplePatchForm(forms.Form):
         super(MultiplePatchForm, self).__init__(*args, **kwargs)
         self.fields['delegate'] = OptionalModelChoiceField(
             queryset=_get_delegate_qs(project=project), required=False)
+        self.fields['labels'] = forms.ModelMultipleChoiceField(
+            queryset=Label.objects.filter(
+                Q(project=project) | Q(project=None)),
+            required=False)
 
     def save(self, instance, commit=True):
         opts = instance.__class__._meta
@@ -202,6 +211,13 @@ class MultiplePatchForm(forms.Form):
 
         if commit:
             instance.save()
+
+        for f in opts.many_to_many:
+            if f.name not in data:
+                continue
+
+            getattr(instance, f.name).add(*data[f.name])
+
         return instance
 
 
diff --git a/patchwork/templates/patchwork/partials/patch-list.html b/patchwork/templates/patchwork/partials/patch-list.html
index 2abb9ec2..5f67cd68 100644
--- a/patchwork/templates/patchwork/partials/patch-list.html
+++ b/patchwork/templates/patchwork/partials/patch-list.html
@@ -192,6 +192,7 @@ $(document).ready(function() {
     <a href="{% url 'patch-detail' patch_id=patch.id %}">
      {{ patch.name|default:"[no subject]"|truncatechars:100 }}
     </a>
+    {{ patch|patch_labels }}
    </td>
    <td>
     {% with patch.series.all.0 as series %}
@@ -247,6 +248,13 @@ $(document).ready(function() {
        {{ patchform.archived.errors }}
       </td>
      </tr>
+     <tr>
+      <th>Label:</th>
+      <td>
+       {{ patchform.labels }}
+       {{ patchform.labels.errors }}
+      </td>
+     </tr>
      <tr>
       <td></td>
       <td>
diff --git a/patchwork/templates/patchwork/submission.html b/patchwork/templates/patchwork/submission.html
index eb5e3583..3a9b1e72 100644
--- a/patchwork/templates/patchwork/submission.html
+++ b/patchwork/templates/patchwork/submission.html
@@ -154,6 +154,13 @@ function toggle_div(link_id, headers_id)
        {{ patchform.archived.errors }}
       </td>
      </tr>
+     <tr>
+      <th>Labels:</th>
+      <td>
+       {{ patchform.labels }}
+       {{ patchform.labels.errors }}
+      </td>
+     </tr>
      <tr>
       <td></td>
       <td>
diff --git a/patchwork/templatetags/patch.py b/patchwork/templatetags/patch.py
index 5d387a49..c397b75b 100644
--- a/patchwork/templatetags/patch.py
+++ b/patchwork/templatetags/patch.py
@@ -41,6 +41,30 @@ def patch_checks(patch):
         ' '.join([str(counts[state]) for state in required])))
 
 
+ at register.filter(name='patch_labels')
+def patch_labels(patch):
+
+    def text_color(hex_color):
+        """Generate the ideal text color given a background color.
+
+        From https://www.w3.org/TR/AERT/#color-contrast
+        """
+        red, green, blue = [
+            int(hex_color.lstrip('#')[i:i + 2], 16) for i in (0, 2, 4)]
+        brightness = (red * 299 + green * 587 + blue * 114) / 1000
+
+        return '#000' if brightness >= 123 else '#fff'
+
+    output = []
+    for label in patch.labels.all():
+        style = 'background-color: %s; color: %s' % (
+            label.color, text_color(label.color))
+        output.append('<span class="label" style="%s">%s</span>' % (
+            style, label.name))
+
+    return mark_safe(''.join(output))
+
+
 @register.filter
 @stringfilter
 def msgid(value):
diff --git a/patchwork/views/__init__.py b/patchwork/views/__init__.py
index 178b3d41..e7d03270 100644
--- a/patchwork/views/__init__.py
+++ b/patchwork/views/__init__.py
@@ -11,6 +11,7 @@ from patchwork.filters import Filters
 from patchwork.forms import MultiplePatchForm
 from patchwork.models import Bundle
 from patchwork.models import BundlePatch
+from patchwork.models import Label
 from patchwork.models import Patch
 from patchwork.models import Project
 from patchwork.models import Check
@@ -284,6 +285,8 @@ def generic_list(request, project, view, view_args=None, filter_settings=None,
             'context', 'user_id', 'patch_id', 'state', 'date')))
     patches = patches.prefetch_related(
         Prefetch('series', queryset=Series.objects.only('name')))
+    patches = patches.prefetch_related(
+        Prefetch('labels', queryset=Label.objects.only('name')))
 
     paginator = Paginator(request, patches)
 
-- 
2.17.1



More information about the Patchwork mailing list