]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/filters.py
Add 'Action Required' meta-state filter
[patchwork] / apps / patchwork / filters.py
1 # Patchwork - automated patch tracking system
2 # Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
3 #
4 # This file is part of the Patchwork package.
5 #
6 # Patchwork is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # Patchwork is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Patchwork; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21 from patchwork.models import Person, State
22 from django.utils.safestring import mark_safe
23 from django.utils.html import escape
24 from django.contrib.auth.models import User
25
26 class Filter(object):
27     def __init__(self, filters):
28         self.filters = filters
29         self.applied = False
30         self.forced = False
31
32     def name(self):
33         """The 'name' of the filter, to be displayed in the filter UI"""
34         return self.name
35
36     def condition(self):
37         """The current condition of the filter, to be displayed in the
38            filter UI"""
39         return self.key
40
41     def key(self):
42         """The key for this filter, to appear in the querystring. A key of
43            None will remove the param=ley pair from the querystring."""
44         return None
45
46     def set_status(self, *kwargs):
47         """Views can call this to force a specific filter status. For example,
48            a user's todo page needs to setup the delegate filter to show
49            that user's delegated patches"""
50         pass
51
52     def parse(self, dict):
53         if self.param not in dict.keys():
54             return
55         self._set_key(dict[self.param])
56
57     def url_without_me(self):
58         return self.filters.querystring_without_filter(self)
59
60     def form_function(self):
61         return 'function(form) { return "unimplemented" }'
62
63     def form(self):
64         if self.forced:
65             return mark_safe('<input type="hidden" value="%s">%s' % (self.param,
66                         self.condition()))
67             return self.condition()
68         return self._form()
69
70     def kwargs(self):
71         return {}
72
73     def __str__(self):
74         return '%s: %s' % (self.name, self.kwargs())
75
76
77 class SubmitterFilter(Filter):
78     param = 'submitter'
79     def __init__(self, filters):
80         super(SubmitterFilter, self).__init__(filters)
81         self.name = 'Submitter'
82         self.person = None
83         self.person_match = None
84
85     def _set_key(self, str):
86         self.person = None
87         self.person_match = None
88         submitter_id = None
89         try:
90             submitter_id = int(str)
91         except ValueError:
92             pass
93         except:
94             return
95
96         if submitter_id:
97             self.person = Person.objects.get(id = int(str))
98             self.applied = True
99             return
100
101
102         people = Person.objects.filter(name__icontains = str)
103
104         if not people:
105             return
106
107         self.person_match = str
108         self.applied = True
109
110     def kwargs(self):
111         if self.person:
112             user = self.person.user
113             if user:
114                 return {'submitter__in':
115                     Person.objects.filter(user = user).values('pk').query}
116             return {'submitter': self.person}
117
118         if self.person_match:
119             return {'submitter__name__icontains': self.person_match}
120         return {}
121
122     def condition(self):
123         if self.person:
124             return self.person.name
125         elif self.person_match:
126             return self.person_match
127         return ''
128
129     def _form(self):
130         name = ''
131         if self.person:
132             name = self.person.name
133         return mark_safe(('<input onKeyUp="submitter_field_change(this)" ' +
134                 'name="submitter" id="submitter_input" ' +
135                         'value="%s">&nbsp;' % escape(name)) +
136                 '<select id="submitter_select" ' +
137                 'disabled="true"></select>')
138
139     def key(self):
140         if self.person:
141             return self.person.id
142         return self.person_match
143
144 class StateFilter(Filter):
145     param = 'state'
146     action_req_key = '!'
147
148     def __init__(self, filters):
149         super(StateFilter, self).__init__(filters)
150         self.name = 'State'
151         self.state = None
152         self.action_req = None
153
154     def _set_key(self, str):
155         self.action_req = None
156         self.state = None
157
158         if str == self.action_req_key:
159             self.action_req = True
160             self.applied = True
161             return
162
163         try:
164             self.state = State.objects.get(id=int(str))
165         except:
166             return
167
168         self.applied = True
169
170     def kwargs(self):
171         if self.action_req == True:
172             return {'state__in': \
173                         State.objects.filter(action_required = True) \
174                             .values('pk').query}
175         if self.state is not None:
176             return {'state': self.state}
177
178     def condition(self):
179         if self.state:
180             return self.state.name
181         elif self.action_req == True:
182             return 'Action Required'
183         return None
184
185
186     def key(self):
187         if self.action_req == True:
188             return self.action_req_key
189         if self.state is None:
190             return None
191         return self.state.id
192
193     def _form(self):
194         str = '<select name="%s">' % self.param
195         str += '<option value="">any</option>'
196         if self.action_req_key:
197             str += '<option value="%s">Action Required</option>' % \
198                    self.action_req_key
199         for state in State.objects.all():
200             selected = ''
201             if self.state and self.state == state:
202                 selected = ' selected="true"'
203
204             str += '<option value="%d" %s>%s</option>' % \
205                 (state.id, selected, state.name)
206         str += '</select>'
207         return mark_safe(str);
208
209     def form_function(self):
210         return 'function(form) { return form.x.value }'
211
212 class SearchFilter(Filter):
213     param = 'q'
214     def __init__(self, filters):
215         super(SearchFilter, self).__init__(filters)
216         self.name = 'Search'
217         self.param = 'q'
218         self.search = None
219
220     def _set_key(self, str):
221         str = str.strip()
222         if str == '':
223             return
224         self.search = str
225         self.applied = True
226
227     def kwargs(self):
228         return {'name__icontains': self.search}
229
230     def condition(self):
231         return self.search
232
233     def key(self):
234         return self.search
235
236     def _form(self):
237         value = ''
238         if self.search:
239             value = escape(self.search)
240         return mark_safe('<input name="%s" value="%s">' %\
241                 (self.param, value))
242
243     def form_function(self):
244         return mark_safe('function(form) { return form.x.value }')
245
246 class ArchiveFilter(Filter):
247     param = 'archive'
248     def __init__(self, filters):
249         super(ArchiveFilter, self).__init__(filters)
250         self.name = 'Archived'
251         self.archive_state = False
252         self.applied = True
253         self.param_map = {
254             True: 'true',
255             False: '',
256             None:  'both'
257         }
258         self.description_map = {
259             True: 'Yes',
260             False: 'No',
261             None: 'Both'
262         }
263
264     def _set_key(self, str):
265         self.archive_state = False
266         self.applied = True
267         for (k, v) in self.param_map.iteritems():
268             if str == v:
269                 self.archive_state = k
270         if self.archive_state == None:
271             self.applied = False
272
273     def kwargs(self):
274         if self.archive_state == None:
275             return {}
276         return {'archived': self.archive_state}
277
278     def condition(self):
279         return self.description_map[self.archive_state]
280
281     def key(self):
282         if self.archive_state == False:
283             return None
284         return self.param_map[self.archive_state]
285
286     def _form(self):
287         s = ''
288         for b in [False, True, None]:
289             label = self.description_map[b]
290             selected = ''
291             if self.archive_state == b:
292                 selected = 'checked="true"'
293             s += ('<input type="radio" name="%(param)s" ' + \
294                    '%(selected)s value="%(value)s">%(label)s' + \
295                    '&nbsp;&nbsp;&nbsp;&nbsp;') % \
296                     {'label': label,
297                      'param': self.param,
298                      'selected': selected,
299                      'value': self.param_map[b]
300                     }
301         return mark_safe(s)
302
303     def url_without_me(self):
304         qs = self.filters.querystring_without_filter(self)
305         if qs != '?':
306             qs += '&'
307         return qs + 'archive=both'
308
309
310 class DelegateFilter(Filter):
311     param = 'delegate'
312     AnyDelegate = 1
313
314     def __init__(self, filters):
315         super(DelegateFilter, self).__init__(filters)
316         self.name = 'Delegate'
317         self.param = 'delegate'
318
319         # default to applied, but no delegate - this will result in patches with
320         # no delegate
321         self.delegate = None
322         self.applied = True
323
324     def _set_key(self, str):
325         if str == "*":
326             self.applied = False
327             self.delegate = None
328             return
329
330         applied = False
331         try:
332             self.delegate = User.objects.get(id = str)
333             self.applied = True
334         except:
335             pass
336
337     def kwargs(self):
338         if not self.applied:
339             return {}
340         return {'delegate': self.delegate}
341
342     def condition(self):
343         if self.delegate:
344             return self.delegate.get_profile().name()
345         return 'Nobody'
346
347     def _form(self):
348         delegates = User.objects.filter(userprofile__maintainer_projects =
349                 self.filters.project)
350
351         str = '<select name="delegate">'
352
353         selected = ''
354         if not self.applied:
355             selected = 'selected'
356
357         str += '<option %s value="*">------</option>' % selected
358
359         selected = ''
360         if self.delegate is None:
361             selected = 'selected'
362
363         str += '<option %s value="">Nobody</option>' % selected
364
365         for d in delegates:
366             selected = ''
367             if d == self.delegate:
368                 selected = ' selected'
369
370             str += '<option %s value="%s">%s</option>' % (selected,
371                     d.id, d.get_profile().name())
372         str += '</select>'
373
374         return mark_safe(str)
375
376     def key(self):
377         if self.delegate:
378             return self.delegate.id
379         if self.applied:
380             return None
381         return '*'
382
383     def url_without_me(self):
384         qs = self.filters.querystring_without_filter(self)
385         if qs != '?':
386             qs += '&'
387         return qs + ('%s=*' % self.param)
388
389     def set_status(self, *args, **kwargs):
390         if 'delegate' in kwargs:
391             self.applied = self.forced = True
392             self.delegate = kwargs['delegate']
393         if self.AnyDelegate in args:
394             self.applied = False
395             self.forced = True
396
397 filterclasses = [SubmitterFilter, \
398                  StateFilter,
399                  SearchFilter,
400                  ArchiveFilter,
401                  DelegateFilter]
402
403 class Filters:
404
405     def __init__(self, request):
406         self._filters = map(lambda c: c(self), filterclasses)
407         self.dict = request.GET
408         self.project = None
409
410         for f in self._filters:
411             f.parse(self.dict)
412
413     def set_project(self, project):
414         self.project = project
415
416     def filter_conditions(self):
417         kwargs = {}
418         for f in self._filters:
419             if f.applied:
420                 kwargs.update(f.kwargs())
421         return kwargs
422
423     def apply(self, queryset):
424         kwargs = self.filter_conditions()
425         if not kwargs:
426             return queryset
427         return queryset.filter(**kwargs)
428
429     def params(self):
430         return [ (f.param, f.key()) for f in self._filters \
431                 if f.key() is not None ]
432
433     def querystring(self, remove = None):
434         params = dict(self.params())
435
436         for (k, v) in self.dict.iteritems():
437             if k not in params:
438                 params[k] = v[0]
439
440         if remove is not None:
441             if remove.param in params.keys():
442                 del params[remove.param]
443
444         return '?' + '&'.join(['%s=%s' % x for x in params.iteritems()])
445
446     def querystring_without_filter(self, filter):
447         return self.querystring(filter)
448
449     def applied_filters(self):
450         return filter(lambda x: x.applied, self._filters)
451
452     def available_filters(self):
453         return self._filters
454
455     def set_status(self, filterclass, *args, **kwargs):
456         for f in self._filters:
457             if isinstance(f, filterclass):
458                 f.set_status(*args, **kwargs)
459                 return