Add basic filtering support
authorMagnus Hagander <magnus@hagander.net>
Fri, 19 Jul 2013 16:48:37 +0000 (18:48 +0200)
committerMagnus Hagander <magnus@hagander.net>
Fri, 19 Jul 2013 16:48:37 +0000 (18:48 +0200)
pgcommitfest/commitfest/forms.py
pgcommitfest/commitfest/models.py
pgcommitfest/commitfest/templates/commitfest.html
pgcommitfest/commitfest/views.py

index 9838ce610a39e7992fbbeb4becf71b6fb3ea78c2..8f2e33cab2431f74744212eb9cffec16edbb44b4 100644 (file)
@@ -1,13 +1,35 @@
 from django import forms
 from django.forms import ValidationError
+from django.db.models import Q
+from django.contrib.auth.models import User
 
 from selectable.forms.widgets import AutoCompleteSelectMultipleWidget
 
-from models import Patch, MailThread
+from models import Patch, MailThread, PatchOnCommitFest
 from lookups import UserLookup
 from widgets import ThreadPickWidget
 from ajax import _archivesAPI
 
+class CommitFestFilterForm(forms.Form):
+       text = forms.CharField(max_length=50, required=False)
+       status = forms.ChoiceField(required=False)
+       author = forms.ChoiceField(required=False)
+       reviewer = forms.ChoiceField(required=False)
+
+       def __init__(self, cf, *args, **kwargs):
+               super(CommitFestFilterForm, self).__init__(*args, **kwargs)
+
+               c = [(-1, '* All')] + list(PatchOnCommitFest._STATUS_CHOICES)
+               self.fields['status'] = forms.ChoiceField(choices=c, required=False)
+
+               q = Q(patch_author__commitfests=cf) | Q(patch_reviewer__commitfests=cf)
+               userchoices = [(-1, '* All'), (-2, '* None'), ] + [(u.id, '%s %s (%s)' % (u.first_name, u.last_name, u.username)) for u in User.objects.filter(q).distinct()]
+               self.fields['author'] = forms.ChoiceField(choices=userchoices, required=False)
+               self.fields['reviewer'] = forms.ChoiceField(choices=userchoices, required=False)
+
+               for f in ('status', 'author', 'reviewer',):
+                       self.fields[f].widget.attrs = {'class': 'input-medium'}
+
 class PatchForm(forms.ModelForm):
        class Meta:
                model = Patch
index 44dd8842103bdae6c5f8e9fe35dd056c2b31e182..e4326db8a8cc89ed8125c1d2a178e4739d0c05b5 100644 (file)
@@ -68,8 +68,8 @@ class Patch(models.Model):
        # Mailthreads are OneToMany in the other direction
        #mailthreads_set = ...
 
-       authors = models.ManyToManyField(User, related_name='author', blank=True)
-       reviewers = models.ManyToManyField(User, related_name='reviewer', blank=True)
+       authors = models.ManyToManyField(User, related_name='patch_author', blank=True)
+       reviewers = models.ManyToManyField(User, related_name='patch_reviewer', blank=True)
 
        committer = models.ForeignKey(Committer, blank=True, null=True)
 
index 19f2739fb21dce0d8ccd86c9ef20a5a078245a4c..946560bc394c4d6bbb7102d146364207d80454d1 100644 (file)
@@ -1,6 +1,36 @@
 {%extends "base.html"%}
 {%load commitfest %}
 {%block contents%}
+
+<div class="accordion" id="filteraccordion">
+ <div class="accordion-group">
+  <div class="accordion-heading">
+    <a class="accordion-toggle" data-toggle="collapse" data-parent="#filteraccordion" href="#collapseFilters"><h4>Filters</h4></a>
+  </div>
+  <div id="collapseFilters" class="accordion-body collapse {%if has_filter%}in{%endif%}">
+    <form id="filterform" method="GET" action=".">
+      <table class="table table-condensed">
+       <thead>
+         <tr>
+{%for f in form%}
+            <td>{{f.label}}</td>
+{%endfor%}
+           <td></td>
+         </tr>
+       </thead>
+       <tbody>
+         <tr>
+{%for f in form%}
+            <td>{{f}}</td>
+{%endfor%}
+           <td><input type="submit" class="btn" value="Filter"></td>
+         </tr>
+       </tbody>
+      </table>
+    </form>
+  </div>
+</div>
+
 {%for p in patches %}
 {%ifchanged p.is_open%}
 {%if not forloop.first%}
index 5e77d6e7b5c8459289d6a4082b23c77e69713de9..ede222ed3c0e813ab48a3db23a3c35df0c793c55 100644 (file)
@@ -2,6 +2,7 @@ from django.shortcuts import render_to_response, get_object_or_404
 from django.http import HttpResponseRedirect, Http404
 from django.template import RequestContext
 from django.db import transaction
+from django.db.models import Q
 from django.contrib import messages
 from django.contrib.auth.decorators import login_required
 
@@ -12,7 +13,7 @@ from email.utils import formatdate, make_msgid
 from mailqueue.util import send_mail
 
 from models import CommitFest, Patch, PatchOnCommitFest, PatchHistory, Committer
-from forms import PatchForm, NewPatchForm, CommentForm
+from forms import PatchForm, NewPatchForm, CommentForm, CommitFestFilterForm
 from ajax import doAttachThread
 
 def home(request):
@@ -30,16 +31,45 @@ def home(request):
 def commitfest(request, cfid):
        # Find ourselves
        cf = get_object_or_404(CommitFest, pk=cfid)
-       patches = cf.patch_set.all().select_related().extra(select={
+
+       # Build a dynamic filter based on the filtering options entered
+       q = Q()
+       if request.GET.has_key('status') and request.GET['status'] != "-1":
+               q = q & Q(patchoncommitfest__status=int(request.GET['status']))
+       if request.GET.has_key('author') and request.GET['author'] != "-1":
+               if request.GET['author'] == '-2':
+                       q = q & Q(authors=None)
+               else:
+                       q = q & Q(authors__id=int(request.GET['author']))
+       if request.GET.has_key('reviewer') and request.GET['reviewer'] != "-1":
+               if request.GET['reviewer'] == '-2':
+                       q = q & Q(reviewers=None)
+               else:
+                       q = q & Q(reviewers__id=int(request.GET['reviewer']))
+
+       if request.GET.has_key('text') and request.GET['text'] != '':
+               q = q & Q(name__icontains=request.GET['text'])
+
+       # Not sure if this is correct?
+       has_filter = len(q.children) > 0
+       if not has_filter and request.GET:
+               # Redirect to get rid of the ugly url
+               return HttpResponseRedirect('/%s/' % cf.id)
+
+       patches = cf.patch_set.filter(q).select_related().extra(select={
                'status':'commitfest_patchoncommitfest.status',
                'author_names':"SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_authors cpa ON cpa.user_id=auth_user.id WHERE cpa.patch_id=commitfest_patch.id",
                'reviewer_names':"SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_reviewers cpr ON cpr.user_id=auth_user.id WHERE cpr.patch_id=commitfest_patch.id",
                'is_open':'commitfest_patchoncommitfest.status IN (%s)' % ','.join([str(x) for x in PatchOnCommitFest.OPEN_STATUSES]),
        }).order_by('-is_open', 'topic__topic', 'created')
 
+       form = CommitFestFilterForm(cf, request.GET)
+
        return render_to_response('commitfest.html', {
                'cf': cf,
+               'form': form,
                'patches': patches,
+               'has_filter': has_filter,
                'title': 'Commitfest %s' % cf.name,
                }, context_instance=RequestContext(request))