]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/filters.py
Remember state filter settings
[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     any_key = '*'
147     action_req_str = 'Action Required'
148
149     def __init__(self, filters):
150         super(StateFilter, self).__init__(filters)
151         self.name = 'State'
152         self.state = None
153         self.applied = True
154
155     def _set_key(self, str):
156         self.state = None
157
158         if str == self.any_key:
159             self.applied = False
160             return
161
162         try:
163             self.state = State.objects.get(id=int(str))
164         except:
165             return
166
167         self.applied = True
168
169     def kwargs(self):
170         if self.state is not None:
171             return {'state': self.state}
172         else:
173             return {'state__in': \
174                         State.objects.filter(action_required = True) \
175                             .values('pk').query}
176
177     def condition(self):
178         if self.state:
179             return self.state.name
180         return self.action_req_str
181
182     def key(self):
183         if self.state is not None:
184             return self.state.id
185         if not self.applied:
186             return '*'
187         return None
188
189     def _form(self):
190         str = '<select name="%s">' % self.param
191
192         selected = ''
193         if not self.applied:
194             selected = 'selected'
195         str += '<option %s value="%s">any</option>' % (selected, self.any_key)
196
197         selected = ''
198         if self.applied and self.state == None:
199             selected = 'selected'
200         str += '<option %s value="">%s</option>' % \
201                (selected, self.action_req_str)
202
203         for state in State.objects.all():
204             selected = ''
205             if self.state and self.state == state:
206                 selected = ' selected="true"'
207
208             str += '<option value="%d" %s>%s</option>' % \
209                 (state.id, selected, state.name)
210         str += '</select>'
211         return mark_safe(str);
212
213     def form_function(self):
214         return 'function(form) { return form.x.value }'
215
216     def url_without_me(self):
217         qs = self.filters.querystring_without_filter(self)
218         if qs != '?':
219             qs += '&'
220         return qs + '%s=%s' % (self.param, self.any_key)
221
222 class SearchFilter(Filter):
223     param = 'q'
224     def __init__(self, filters):
225         super(SearchFilter, self).__init__(filters)
226         self.name = 'Search'
227         self.param = 'q'
228         self.search = None
229
230     def _set_key(self, str):
231         str = str.strip()
232         if str == '':
233             return
234         self.search = str
235         self.applied = True
236
237     def kwargs(self):
238         return {'name__icontains': self.search}
239
240     def condition(self):
241         return self.search
242
243     def key(self):
244         return self.search
245
246     def _form(self):
247         value = ''
248         if self.search:
249             value = escape(self.search)
250         return mark_safe('<input name="%s" value="%s">' %\
251                 (self.param, value))
252
253     def form_function(self):
254         return mark_safe('function(form) { return form.x.value }')
255
256 class ArchiveFilter(Filter):
257     param = 'archive'
258     def __init__(self, filters):
259         super(ArchiveFilter, self).__init__(filters)
260         self.name = 'Archived'
261         self.archive_state = False
262         self.applied = True
263         self.param_map = {
264             True: 'true',
265             False: '',
266             None:  'both'
267         }
268         self.description_map = {
269             True: 'Yes',
270             False: 'No',
271             None: 'Both'
272         }
273
274     def _set_key(self, str):
275         self.archive_state = False
276         self.applied = True
277         for (k, v) in self.param_map.iteritems():
278             if str == v:
279                 self.archive_state = k
280         if self.archive_state == None:
281             self.applied = False
282
283     def kwargs(self):
284         if self.archive_state == None:
285             return {}
286         return {'archived': self.archive_state}
287
288     def condition(self):
289         return self.description_map[self.archive_state]
290
291     def key(self):
292         if self.archive_state == False:
293             return None
294         return self.param_map[self.archive_state]
295
296     def _form(self):
297         s = ''
298         for b in [False, True, None]:
299             label = self.description_map[b]
300             selected = ''
301             if self.archive_state == b:
302                 selected = 'checked="true"'
303             s += ('<input type="radio" name="%(param)s" ' + \
304                    '%(selected)s value="%(value)s">%(label)s' + \
305                    '&nbsp;&nbsp;&nbsp;&nbsp;') % \
306                     {'label': label,
307                      'param': self.param,
308                      'selected': selected,
309                      'value': self.param_map[b]
310                     }
311         return mark_safe(s)
312
313     def url_without_me(self):
314         qs = self.filters.querystring_without_filter(self)
315         if qs != '?':
316             qs += '&'
317         return qs + 'archive=both'
318
319
320 class DelegateFilter(Filter):
321     param = 'delegate'
322     no_delegate_key = '-'
323     no_delegate_str = 'Nobody'
324     AnyDelegate = 1
325
326     def __init__(self, filters):
327         super(DelegateFilter, self).__init__(filters)
328         self.name = 'Delegate'
329         self.param = 'delegate'
330         self.delegate = None
331
332     def _set_key(self, str):
333         if str == self.no_delegate_key:
334             self.applied = True
335             self.delegate = None
336             return
337
338         applied = False
339         try:
340             self.delegate = User.objects.get(id = str)
341             self.applied = True
342         except:
343             pass
344
345     def kwargs(self):
346         if not self.applied:
347             return {}
348         return {'delegate': self.delegate}
349
350     def condition(self):
351         if self.delegate:
352             return self.delegate.get_profile().name()
353         return self.no_delegate_str
354
355     def _form(self):
356         delegates = User.objects.filter(userprofile__maintainer_projects =
357                 self.filters.project)
358
359         str = '<select name="delegate">'
360
361         selected = ''
362         if not self.applied:
363             selected = 'selected'
364
365         str += '<option %s value="">------</option>' % selected
366
367         selected = ''
368         if self.applied and self.delegate is None:
369             selected = 'selected'
370
371         str += '<option %s value="%s">%s</option>' % \
372                 (selected, self.no_delegate_key, self.no_delegate_str)
373
374         for d in delegates:
375             selected = ''
376             if d == self.delegate:
377                 selected = ' selected'
378
379             str += '<option %s value="%s">%s</option>' % (selected,
380                     d.id, d.get_profile().name())
381         str += '</select>'
382
383         return mark_safe(str)
384
385     def key(self):
386         if self.delegate:
387             return self.delegate.id
388         if self.applied:
389             return self.no_delegate_key
390         return None
391
392     def set_status(self, *args, **kwargs):
393         if 'delegate' in kwargs:
394             self.applied = self.forced = True
395             self.delegate = kwargs['delegate']
396         if self.AnyDelegate in args:
397             self.applied = False
398             self.forced = True
399
400 filterclasses = [SubmitterFilter, \
401                  StateFilter,
402                  SearchFilter,
403                  ArchiveFilter,
404                  DelegateFilter]
405
406 class Filters:
407
408     def __init__(self, request):
409         self._filters = map(lambda c: c(self), filterclasses)
410         self.dict = request.GET
411         self.project = None
412
413         for f in self._filters:
414             f.parse(self.dict)
415
416     def set_project(self, project):
417         self.project = project
418
419     def filter_conditions(self):
420         kwargs = {}
421         for f in self._filters:
422             if f.applied:
423                 kwargs.update(f.kwargs())
424         return kwargs
425
426     def apply(self, queryset):
427         kwargs = self.filter_conditions()
428         if not kwargs:
429             return queryset
430         return queryset.filter(**kwargs)
431
432     def params(self):
433         return [ (f.param, f.key()) for f in self._filters \
434                 if f.key() is not None ]
435
436     def querystring(self, remove = None):
437         params = dict(self.params())
438
439         for (k, v) in self.dict.iteritems():
440             if k not in params:
441                 params[k] = v[0]
442
443         if remove is not None:
444             if remove.param in params.keys():
445                 del params[remove.param]
446
447         return '?' + '&'.join(['%s=%s' % x for x in params.iteritems()])
448
449     def querystring_without_filter(self, filter):
450         return self.querystring(filter)
451
452     def applied_filters(self):
453         return filter(lambda x: x.applied, self._filters)
454
455     def available_filters(self):
456         return self._filters
457
458     def set_status(self, filterclass, *args, **kwargs):
459         for f in self._filters:
460             if isinstance(f, filterclass):
461                 f.set_status(*args, **kwargs)
462                 return