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.
Signed-off-by: Franciszek Stachura <[email protected]> --- I'm not sure if this new "q" method is the best possible interface, but I'm pretty sure the current interface is not flexible enough. Is there any reason why filters return a dict that is unpacked into Django ORM filter call? Wouldn't it make more sense to just pass the queryset to each filter? --- patchwork/filters.py | 73 ++++++++++++++++++++++++++++++++++++++++++-- patchwork/models.py | 18 +++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/patchwork/filters.py b/patchwork/filters.py index 99aad8c3..9195cbce 100644 --- a/patchwork/filters.py +++ b/patchwork/filters.py @@ -13,6 +13,8 @@ from django.utils.safestring import mark_safe from patchwork.models import Person from patchwork.models import Series from patchwork.models import State +from patchwork.models import exclude_submissions_by_labels +from patchwork.models import filter_submissions_by_labels class Filter(object): @@ -511,6 +513,62 @@ class DelegateFilter(Filter): self.forced = True +class LabelsFilter(Filter): + name = 'Labels' + param = 'labels' + + def __init__(self, filters): + super(LabelsFilter, self).__init__(filters) + self.labels = None + + @property + def condition(self): + return self.labels + + @property + def key(self): + return self.labels + + @key.setter + def key(self, key): + key = key.strip() + if not key: + return + + self.labels = key + self.applied = True + + @property + def kwargs(self): + return None + + def q(self, queryset): + label_names = self.labels.split(' ') + if len(label_names) == 0: + return queryset + + labels_pos, labels_neg = [], [] + for label in label_names: + if not label.startswith('-'): + labels_pos.append(label) + else: + labels_neg.append(label[1:]) + + queryset = exclude_submissions_by_labels(queryset, labels_neg) + queryset = filter_submissions_by_labels(queryset, labels_pos) + return queryset + + @property + def form(self): + value = '' + if self.labels: + value = escape(self.labels) + return mark_safe( + '<input type="text" id="labels_input" name="%s" class="form-control" value="%s">' + % (self.param, value) + ) + + FILTERS = [ SeriesFilter, SubmitterFilter, @@ -518,6 +576,7 @@ FILTERS = [ SearchFilter, ArchiveFilter, DelegateFilter, + LabelsFilter, ] @@ -547,14 +606,22 @@ class Filters: def apply(self, queryset): kwargs = collections.OrderedDict() + q_filters = [] for f in self._filters: if f.applied: - kwargs.update(f.kwargs) + if f.kwargs is not None: + kwargs.update(f.kwargs) + else: + q_filters.append(f) - if not kwargs: + if not kwargs and not q_filters: return queryset - return queryset.filter(**kwargs) + queryset = queryset.filter(**kwargs) + for f in q_filters: + queryset = f.q(queryset) + + return queryset def querystring(self, remove=None): params = self.params diff --git a/patchwork/models.py b/patchwork/models.py index e122ca3d..28bfe680 100644 --- a/patchwork/models.py +++ b/patchwork/models.py @@ -498,6 +498,24 @@ class SubmissionMixin(FilenameMixin, EmailMixin, models.Model): abstract = True +def exclude_submissions_by_labels(queryset, labels): + if len(labels) > 0: + queryset = queryset.exclude(labels__name__in=labels) + + return queryset + + +def filter_submissions_by_labels(queryset, labels): + if len(labels) > 0: + queryset = ( + queryset.filter(labels__name__in=labels) + .annotate(num_labels=models.Count('labels', distinct=True)) + .filter(num_labels__gte=len(labels)) + ) + + return queryset + + class Cover(SubmissionMixin): def get_absolute_url(self): return reverse( -- 2.55.0 _______________________________________________ Patchwork mailing list [email protected] https://lists.ozlabs.org/listinfo/patchwork
