]> git.ipfire.org Git - thirdparty/patchwork.git/commitdiff
Add delegate filter autocomplete support
authorStephen Finucane <stephen.finucane@intel.com>
Mon, 24 Aug 2015 10:05:47 +0000 (11:05 +0100)
committerStephen Finucane <stephen.finucane@intel.com>
Mon, 25 Jan 2016 16:23:51 +0000 (16:23 +0000)
It would be helpful to provide autocomplete functionality for the
delegate filter field, similar to that provided for the submitter
field. This will provide a way to handle larger delegate lists without
overloading the user with options.

Add this functionality through use of Selectize, which is already used
to provide similar functionality for filtering of "submitters".

Signed-off-by: Stephen Finucane <stephen.finucane@intel.com>
patchwork/filters.py
patchwork/templates/patchwork/filters.html
patchwork/urls.py
patchwork/views/api.py

index 4ee399823fc9c236b95496ba35ea4af775aa5015..eaa3e2b4c7ebc0629f0bcb67851df3e7f93148b8 100644 (file)
@@ -330,82 +330,64 @@ class ArchiveFilter(Filter):
 
 class DelegateFilter(Filter):
     param = 'delegate'
-    no_delegate_key = '-'
-    no_delegate_str = 'Nobody'
-    AnyDelegate = 1
 
     def __init__(self, filters):
         super(DelegateFilter, self).__init__(filters)
         self.name = 'Delegate'
-        self.param = 'delegate'
         self.delegate = None
+        self.delegate_match = None
 
-    def _set_key(self, str):
-        if str == self.no_delegate_key:
-            self.applied = True
-            self.delegate = None
+    def _set_key(self, key):
+        self.delegate = None
+        self.delegate_match = None
+        delegate_id = None
+
+        key = key.strip()
+        if not key:
             return
 
         try:
-            self.delegate = User.objects.get(id=str)
-            self.applied = True
-        except:
+            delegate_id = int(key)
+        except ValueError:
             pass
+        except:
+            return
 
-    def kwargs(self):
-        if not self.applied:
-            return {}
-        return {'delegate': self.delegate}
-
-    def condition(self):
-        if self.delegate:
-            return self.delegate.profile.name()
-        return self.no_delegate_str
-
-    def _form(self):
-        delegates = User.objects.filter(
-            profile__maintainer_projects=self.filters.project)
-
-        str = '<select name="delegate" class="form-control">'
-
-        selected = ''
-        if not self.applied:
-            selected = 'selected'
+        if delegate_id:
+            self.delegate = User.objects.get(id=int(key))
+            self.applied = True
+            return
 
-        str += '<option %s value="">------</option>' % selected
+        delegates = User.objects.filter(username__icontains=key)
+        if not delegates:
+            return
 
-        selected = ''
-        if self.applied and self.delegate is None:
-            selected = 'selected'
+        self.delegate_match = key
+        self.applied = True
 
-        str += '<option %s value="%s">%s</option>' % \
-            (selected, self.no_delegate_key, self.no_delegate_str)
+    def kwargs(self):
+        if self.delegate:
+            return {'delegate': self.delegate}
 
-        for d in delegates:
-            selected = ''
-            if d == self.delegate:
-                selected = ' selected'
+        if self.delegate_match:
+            return {'delegate__username__icontains': self.delegate_match}
+        return {}
 
-            str += '<option %s value="%s">%s</option>' % (
-                selected, d.id, d.profile.name())
-        str += '</select>'
+    def condition(self):
+        if self.delegate:
+            return str(self.delegate)
+        elif self.delegate_match:
+            return self.delegate_match
+        return ''
 
-        return mark_safe(str)
+    def _form(self):
+        return mark_safe('<input type="text" name="delegate" '
+                         'id="delegate_input" class="form-control">')
 
     def key(self):
         if self.delegate:
             return self.delegate.id
-        if self.applied:
-            return self.no_delegate_key
-        return None
-
-    def set_status(self, *args, **kwargs):
-        if 'delegate' in kwargs:
-            self.applied = self.forced = True
-            self.delegate = kwargs['delegate']
-        if self.AnyDelegate in args:
-            self.applied = False
-            self.forced = True
+        return self.delegate_match
 
 filterclasses = [SubmitterFilter,
                  StateFilter,
index 3abfe24975ba5b027d354dca7f1df6b41ad82e8d..f5c830b1ad940b71fb81cb76ed852a18976d61e7 100644 (file)
@@ -82,6 +82,48 @@ $(document).ready(function() {
         }
     });
 });
+
+
+$(document).ready(function() {
+    $('#delegate_input').selectize({
+        valueField: 'pk',
+        labelField: 'name',
+        searchField: ['name'],
+        plugins: ['enter_key_submit'],
+        maxItems: 1,
+        persist: false,
+        onInitialize: function() {
+            this.on('submit', function() {
+                if (!this.items.length)
+                    this.$input.val(this.lastValue);
+                this.$input.closest('form').submit();
+            }, this);
+        },
+        render: {
+            option: function(item, escape) {
+                if (item.email)
+                    return '<div>' + escape(item.name) + ' &lt;' +
+                                     escape(item.email) + '&gt;' + '</div>';
+                return '<div>' + escape(item.name) + '</div>';
+            },
+            item: function(item, escape) {
+                return '<div>' + escape(item.name) + '</div>';
+            }
+        },
+        load: function(query, callback) {
+            req = $.ajax({
+                url: '{% url 'api-delegates' %}?q=' +
+                      encodeURIComponent(query) + '&l=10',
+                error: function() {
+                    callback();
+                },
+                success: function(res) {
+                    callback(res);
+                }
+            });
+        }
+    });
+});
 </script>
 
 <div class="filters">
index 67167c181aaec3a01956930b910e456c7e5a0164..550e5675cdcbd01e19cf65fbf8caa3ff5e7a10ab 100644 (file)
@@ -109,6 +109,7 @@ urlpatterns = [
 
     # submitter autocomplete
     url(r'^submitter/$', api_views.submitters, name='api-submitters'),
+    url(r'^delegate/$', api_views.delegates, name='api-delegates'),
 
     # email setup
     url(r'^mail/$', mail_views.settings, name='mail-settings'),
index 91f4f3d0e06b7cb8a2c7e566857604c46c4a24ee..4d79f931e5f0dc39540f99d22fee3a7b9e2a82e3 100644 (file)
@@ -22,18 +22,20 @@ import json
 from django.db.models import Q
 from django.http import HttpResponse
 
-from patchwork.models import Person
+from patchwork.models import Person, User
 
 
-def submitters(request):
+MINIMUM_CHARACTERS = 3
+
+
+def _handle_request(request, queryset_fn, formatter):
     search = request.GET.get('q', '')
     limit = request.GET.get('l', None)
 
-    if len(search) <= 3:
-        return HttpResponse(content_type="application/json")
+    if len(search) < MINIMUM_CHARACTERS:
+        return HttpResponse(content_type='application/json')
 
-    queryset = Person.objects.filter(Q(name__icontains=search) |
-                                     Q(email__icontains=search))
+    queryset = queryset_fn(search)
     if limit is not None:
         try:
             limit = int(limit)
@@ -44,11 +46,37 @@ def submitters(request):
         queryset = queryset[:limit]
 
     data = []
-    for submitter in queryset:
-        item = {}
-        item['pk'] = submitter.id
-        item['name'] = submitter.name
-        item['email'] = submitter.email
-        data.append(item)
-
-    return HttpResponse(json.dumps(data), content_type="application/json")
+    for item in queryset:
+        data.append(formatter(item))
+
+    return HttpResponse(json.dumps(data), content_type='application/json')
+
+
+def submitters(request):
+    def queryset(search):
+        return Person.objects.filter(Q(name__icontains=search) |
+                                     Q(email__icontains=search))
+
+    def formatter(submitter):
+        return {
+            'pk': submitter.id,
+            'name': submitter.name,
+            'email': submitter.email,
+        }
+
+    return _handle_request(request, queryset, formatter)
+
+
+def delegates(request):
+    def queryset(search):
+        return User.objects.filter(Q(username__icontains=search) |
+                                   Q(first_name__icontains=search) |
+                                   Q(last_name__icontains=search))
+
+    def formatter(user):
+        return {
+            'pk': user.id,
+            'name': str(user),
+        }
+
+    return _handle_request(request, queryset, formatter)