9
9
from django .http import QueryDict
10
10
11
11
from indigo_api .models import Task , TaskLabel , Country , TaskFile , Work
12
- from indigo_app .forms .works import WorkFilterForm
12
+ from indigo_app .forms .works import WorkFilterForm , NegatableModelMultipleChoiceField
13
13
14
14
15
15
class TaskForm (forms .ModelForm ):
@@ -137,10 +137,10 @@ class Meta:
137
137
138
138
139
139
class TaskFilterForm (WorkFilterForm ):
140
- labels = forms . ModelMultipleChoiceField (label = _ ("Labels" ), queryset = TaskLabel .objects , to_field_name = 'slug' )
140
+ labels = NegatableModelMultipleChoiceField (label = _ ("Labels" ), queryset = TaskLabel .objects , to_field_name = 'slug' )
141
141
state = forms .MultipleChoiceField (label = _ ('State' ), choices = Task .SIMPLE_STATE_CHOICES )
142
142
assigned_to = forms .MultipleChoiceField (label = _ ('Assigned to' ), choices = [])
143
- submitted_by = forms . ModelMultipleChoiceField (label = _ ('Submitted by' ), queryset = User .objects )
143
+ submitted_by = NegatableModelMultipleChoiceField (label = _ ('Submitted by' ), queryset = User .objects )
144
144
type = forms .MultipleChoiceField (label = _ ('Task type' ), choices = Task .CODES )
145
145
sortby = forms .ChoiceField (choices = [
146
146
('-created_at' , _ ('Created at (newest first)' )), ('created_at' , _ ('Created at (oldest first)' )),
@@ -161,7 +161,19 @@ def __init__(self, country, locality, data, *args, **kwargs):
161
161
super ().__init__ (country , params , * args , ** kwargs )
162
162
163
163
# ensure assigned_to supports unassigned
164
- self .fields ['assigned_to' ].choices = [('-' , _ ('(Not assigned)' ))] + [(str (u .pk ), u .username ) for u in User .objects .all ()]
164
+ users = User .objects .all ()
165
+ self .fields ['assigned_to' ].choices = (
166
+ [
167
+ ('0' , _ ('(Not assigned)' )),
168
+ ('-0' , _ ('(Not assigned)' )),
169
+ ] + [
170
+ (str (u .pk ), u .username )
171
+ for u in users
172
+ ] + [
173
+ (f'-{ u .pk } ' , u .username )
174
+ for u in users
175
+ ]
176
+ )
165
177
166
178
self .locality = locality
167
179
if country :
@@ -182,29 +194,23 @@ def filter_queryset(self, queryset, exclude=None):
182
194
183
195
if exclude != 'type' :
184
196
if self .cleaned_data .get ('type' ):
185
- queryset = queryset . filter ( code__in = self .cleaned_data [' type' ] )
197
+ queryset = self . apply_values_filter ( self .cleaned_data [" type" ], queryset , "code" )
186
198
187
199
if exclude != 'labels' :
188
200
if self .cleaned_data .get ('labels' ):
189
- queryset = queryset . filter ( labels__in = self .cleaned_data [' labels' ] )
201
+ queryset = self . apply_model_choices_filter ( self .cleaned_data [" labels" ], queryset , "labels" )
190
202
191
203
if exclude != 'state' :
192
204
if self .cleaned_data .get ('state' ):
193
- queryset = queryset . filter ( state__in = self .cleaned_data [' state' ] )
205
+ queryset = self . apply_values_filter ( self .cleaned_data [" state" ], queryset , "state" )
194
206
195
207
if exclude != 'assigned_to' :
196
208
if self .cleaned_data .get ('assigned_to' ):
197
- options = [x for x in self .cleaned_data ['assigned_to' ] if x != '-' ]
198
- q = Q ()
199
- if options :
200
- q |= Q (assigned_to__in = options )
201
- if '-' in self .cleaned_data ['assigned_to' ]:
202
- q |= Q (assigned_to__isnull = True )
203
- queryset = queryset .filter (q )
209
+ queryset = self .apply_assigned_to_filter (self .cleaned_data ["assigned_to" ], queryset )
204
210
205
211
if exclude != 'submitted_by' :
206
212
if self .cleaned_data .get ('submitted_by' ):
207
- queryset = queryset . filter ( submitted_by_user__in = self .cleaned_data [' submitted_by' ] )
213
+ queryset = self . apply_model_choices_filter ( self .cleaned_data [" submitted_by" ], queryset , "submitted_by_user" )
208
214
209
215
if exclude != 'taxonomy_topic' :
210
216
if self .cleaned_data .get ('taxonomy_topic' ):
@@ -218,6 +224,27 @@ def filter_queryset(self, queryset, exclude=None):
218
224
219
225
return queryset
220
226
227
+ def apply_assigned_to_filter (self , values , queryset ):
228
+ # this is the same as the default apply_values_filter, but we need to handle unassigned
229
+ values = self .cleaned_data ['assigned_to' ]
230
+ includes = [x for x in values if not x .startswith ('-' )]
231
+ excludes = [x [1 :] for x in values if x .startswith ('-' )]
232
+
233
+ def make_q (items ):
234
+ q = Q (assigned_to__in = items )
235
+ # unassigned
236
+ if '0' in items :
237
+ q |= Q (assigned_to__isnull = True )
238
+ return q
239
+
240
+ if includes :
241
+ queryset = queryset .filter (make_q (includes ))
242
+
243
+ if excludes :
244
+ queryset = queryset .exclude (make_q (excludes ))
245
+
246
+ return queryset
247
+
221
248
def task_facets (self , queryset , places_toc ):
222
249
facets = []
223
250
self .facet_state (facets , queryset )
@@ -240,22 +267,26 @@ def facet_labels(self, facets, qs):
240
267
241
268
def facet_state (self , facets , qs ):
242
269
qs = self .filter_queryset (qs , exclude = 'state' )
243
- counts = qs .values ('state' ).annotate (count = Count ('pk' )).order_by ()
270
+ counts = {
271
+ c ['state' ]: c ['count' ]
272
+ for c in qs .values ('state' ).annotate (count = Count ('pk' )).order_by ()
273
+ }
274
+ # we always want to show all states, even if they are zero
244
275
items = [
245
- (c [ 'state' ], c [ 'count' ] )
246
- for c in counts
276
+ (s , counts . get ( s , 0 ) )
277
+ for s in Task . STATES
247
278
]
248
- items .sort (key = lambda x : Task .STATES .index (x [0 ]))
249
279
facets .append (self .facet ("state" , "checkbox" , items ))
250
280
251
281
for item in facets [- 1 ].items :
252
- item .icon = f'task-icon-{ item .value } text-{ item .value } small'
282
+ v = item .value [1 :] if item .negated else item .value
283
+ item .icon = f'task-icon-{ v } text-{ v } small'
253
284
254
285
def facet_assigned_to (self , facets , qs ):
255
286
qs = self .filter_queryset (qs , exclude = 'assigned_to' )
256
287
counts = qs .values ('assigned_to' ).annotate (count = Count ('pk' )).order_by ()
257
288
items = [
258
- (str (c ['assigned_to' ] or '- ' ), c ['count' ])
289
+ (str (c ['assigned_to' ] or '0 ' ), c ['count' ])
259
290
for c in counts
260
291
]
261
292
items .sort (key = lambda x : x [0 ])
0 commit comments