Skip to content

Commit 06b5042

Browse files
committed
filters: Add label query filter
The filter allows filtering by project-specific and global labels. It also supports negative filtering. Prepending a label name with a minus sign excludes patches with that label from search results.
1 parent 4c84d26 commit 06b5042

2 files changed

Lines changed: 88 additions & 3 deletions

File tree

‎patchwork/filters.py‎

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from patchwork.models import Person
1414
from patchwork.models import Series
1515
from patchwork.models import State
16+
from patchwork.models import exclude_submissions_by_labels
17+
from patchwork.models import filter_submissions_by_labels
1618

1719

1820
class Filter(object):
@@ -511,13 +513,70 @@ def set_status(self, *args, **kwargs):
511513
self.forced = True
512514

513515

516+
class LabelsFilter(Filter):
517+
name = 'Labels'
518+
param = 'labels'
519+
520+
def __init__(self, filters):
521+
super(LabelsFilter, self).__init__(filters)
522+
self.labels = None
523+
524+
@property
525+
def condition(self):
526+
return self.labels
527+
528+
@property
529+
def key(self):
530+
return self.labels
531+
532+
@key.setter
533+
def key(self, key):
534+
key = key.strip()
535+
if not key:
536+
return
537+
538+
self.labels = key
539+
self.applied = True
540+
541+
@property
542+
def kwargs(self):
543+
return None
544+
545+
def q(self, queryset):
546+
label_names = self.labels.split(' ')
547+
if len(label_names) == 0:
548+
return queryset
549+
550+
labels_pos, labels_neg = [], []
551+
for label in label_names:
552+
if not label.startswith('-'):
553+
labels_pos.append(label)
554+
else:
555+
labels_neg.append(label[1:])
556+
557+
queryset = exclude_submissions_by_labels(queryset, labels_neg)
558+
queryset = filter_submissions_by_labels(queryset, labels_pos)
559+
return queryset
560+
561+
@property
562+
def form(self):
563+
value = ''
564+
if self.labels:
565+
value = escape(self.labels)
566+
return mark_safe(
567+
'<input type="text" id="labels_input" name="%s" class="form-control" value="%s">'
568+
% (self.param, value)
569+
)
570+
571+
514572
FILTERS = [
515573
SeriesFilter,
516574
SubmitterFilter,
517575
StateFilter,
518576
SearchFilter,
519577
ArchiveFilter,
520578
DelegateFilter,
579+
LabelsFilter,
521580
]
522581

523582

@@ -547,14 +606,22 @@ def available_filters(self):
547606

548607
def apply(self, queryset):
549608
kwargs = collections.OrderedDict()
609+
q_filters = []
550610
for f in self._filters:
551611
if f.applied:
552-
kwargs.update(f.kwargs)
612+
if f.kwargs is not None:
613+
kwargs.update(f.kwargs)
614+
else:
615+
q_filters.append(f)
553616

554-
if not kwargs:
617+
if not kwargs and not q_filters:
555618
return queryset
556619

557-
return queryset.filter(**kwargs)
620+
queryset = queryset.filter(**kwargs)
621+
for f in q_filters:
622+
queryset = f.q(queryset)
623+
624+
return queryset
558625

559626
def querystring(self, remove=None):
560627
params = self.params

‎patchwork/models.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,24 @@ class Meta:
498498
abstract = True
499499

500500

501+
def exclude_submissions_by_labels(queryset, labels):
502+
if len(labels) > 0:
503+
queryset = queryset.exclude(labels__name__in=labels)
504+
505+
return queryset
506+
507+
508+
def filter_submissions_by_labels(queryset, labels):
509+
if len(labels) > 0:
510+
queryset = (
511+
queryset.filter(labels__name__in=labels)
512+
.annotate(num_labels=models.Count('labels', distinct=True))
513+
.filter(num_labels__gte=len(labels))
514+
)
515+
516+
return queryset
517+
518+
501519
class Cover(SubmissionMixin):
502520
def get_absolute_url(self):
503521
return reverse(

0 commit comments

Comments
 (0)