From 778fce6257bfe9734581495acb928d2c6a169d7e Mon Sep 17 00:00:00 2001 From: ikame Date: Fri, 28 Feb 2014 14:59:12 +0100 Subject: [PATCH] US #50: Neighbor taking into account issues ordering --- taiga/base/models.py | 72 ++++++++----- taiga/projects/issues/models.py | 151 +++++++++++++++++++++++++++ taiga/projects/userstories/models.py | 10 +- 3 files changed, 200 insertions(+), 33 deletions(-) diff --git a/taiga/base/models.py b/taiga/base/models.py index c50758c5..263ff7d5 100644 --- a/taiga/base/models.py +++ b/taiga/base/models.py @@ -22,27 +22,32 @@ class NeighborsMixin: """ if queryset is None: queryset = type(self).objects.get_queryset() - if not self._get_queryset_order_by(queryset): - queryset = queryset.order_by(*self._get_order_by()) + queryset = queryset.order_by(*self._get_order_by(queryset)) queryset = queryset.filter(~Q(id=self.id)) return self._get_previous_neighbor(queryset), self._get_next_neighbor(queryset) def _get_queryset_order_by(self, queryset): - return queryset.query.order_by or [] + return queryset.query.order_by - def _get_order_by(self): - return self._meta.ordering + def _get_order_by(self, queryset): + return self._get_queryset_order_by(queryset) or self._meta.ordering - def _field(self, field): - return getattr(self, field.lstrip("-")) + def _get_order_field_value(self, field): + field = field.lstrip("-") + obj = self + for attr in field.split("__"): + value = getattr(obj, attr, None) + if value is None: + break + obj = value - def _filter(self, field, inc, desc): + return value + + def _transform_order_field_into_lookup(self, field, operator, operator_if_order_desc): if field.startswith("-"): field = field[1:] - operator = desc - else: - operator = inc + operator = operator_if_order_desc return field, operator def _format(self, value): @@ -51,30 +56,39 @@ class NeighborsMixin: return value def _or(self, conditions): - condition = conditions[0] - result = Q(**{key: self._format(condition[key]) for key in condition}) - for condition in conditions[1:]: - result = result | Q(**{key: self._format(condition[key]) for key in condition}) + result = Q() + for condition in conditions: + result |= Q(**{key: self._format(condition[key]) for key in condition}) return result - def _get_prev_neighbor_filters(self, queryset): - conds = [{"{}__{}".format(*self._filter(field, "lt", "gt")): self._field(field)} - for field in self._get_queryset_order_by(queryset)] - return self._or(conds) + def _get_neighbor_filters(self, queryset, operator, operator_if_order_desc): + conds = [] + for field in self._get_queryset_order_by(queryset): + value = self._get_order_field_value(field) + if value is None: + continue + lookup_field, operator = self._transform_order_field_into_lookup( + field, operator, operator_if_order_desc) + lookup = "{}__{}".format(lookup_field, operator) + conds.append({lookup: value}) + return conds - def _get_previous_neighbor(self, queryset): - try: - return queryset.filter(self._get_prev_neighbor_filters(queryset)).reverse()[0] - except IndexError: - return None + def _get_prev_neighbor_filters(self, queryset): + return self._get_neighbor_filters(queryset, "lte", "gte") def _get_next_neighbor_filters(self, queryset): - conds = [{"{}__{}".format(*self._filter(field, "gt", "lt")): self._field(field)} - for field in self._get_queryset_order_by(queryset)] - return self._or(conds) + return self._get_neighbor_filters(queryset, "gte", "lte") - def _get_next_neighbor(self, queryset): + def _get_previous_neighbor(self, queryset): + queryset = queryset.filter(self._or(self._get_prev_neighbor_filters(queryset))) try: - return queryset.filter(self._get_next_neighbor_filters(queryset))[0] + return queryset.reverse()[0] + except IndexError: + return None + + def _get_next_neighbor(self, queryset): + queryset = queryset.filter(self._or(self._get_next_neighbor_filters(queryset))) + try: + return queryset[0] except IndexError: return None diff --git a/taiga/projects/issues/models.py b/taiga/projects/issues/models.py index 761f0060..9c9cc701 100644 --- a/taiga/projects/issues/models.py +++ b/taiga/projects/issues/models.py @@ -81,6 +81,157 @@ class Issue(NeighborsMixin, WatchedMixin, BlockedMixin): def __str__(self): return "({1}) {0}".format(self.ref, self.subject) + def _get_order_by(self, queryset): + ordering = self._get_queryset_order_by(queryset) + if ordering: + main_order = ordering[0] + need_extra_ordering = ("severity", "-severity", "owner__first_name", + "-owner__first_name", "status", "-status", "priority", + "-priority", "assigned_to__first_name", + "-assigned_to__first_name") + if main_order in need_extra_ordering: + ordering += self._meta.ordering + + return ordering + + def _get_prev_neighbor_filters(self, queryset): + conds = super()._get_prev_neighbor_filters(queryset) + main_order = queryset.query.order_by[0] + if main_order == "severity": + conds = [{"severity__order__lt": self.severity.order}, + {"severity__order": self.severity.order, + "created_date__lt": self.created_date}] + elif main_order == "-severity": + conds = [{"severity__order__gt": self.severity.order}, + {"severity__order": self.severity.order, + "created_date__lt": self.created_date}] + elif main_order == "status": + conds = [{"status__order__lt": self.status.order}, + {"status__order": self.status.order, + "created_date__lt": self.created_date}] + elif main_order == "-status": + conds = [{"status__order__gt": self.status.order}, + {"status__order": self.status.order, + "created_date__lt": self.created_date}] + elif main_order == "priority": + conds = [{"priority__order__lt": self.priority.order}, + {"priority__order": self.priority.order, + "created_date__lt": self.created_date}] + elif main_order == "-priority": + conds = [{"priority__order__gt": self.priority.order}, + {"priority__order": self.priority.order, + "created_date__lt": self.created_date}] + elif main_order == "owner__first_name": + conds = [{"owner__first_name": self.owner.first_name, + "owner__last_name": self.owner.last_name, + "created_date__lt": self.created_date}, + {"owner__first_name": self.owner.first_name, + "owner__last_name__lt": self.owner.last_name}, + {"owner__first_name__lt": self.owner.first_name}] + elif main_order == "-owner__first_name": + conds = [{"owner__first_name": self.owner.first_name, + "owner__last_name": self.owner.last_name, + "created_date__lt": self.created_date}, + {"owner__first_name": self.owner.first_name, + "owner__last_name__gt": self.owner.last_name}, + {"owner__first_name__gt": self.owner.first_name}] + elif main_order == "assigned_to__first_name": + if self.assigned_to: + conds = [{"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name": self.assigned_to.last_name, + "created_date__lt": self.created_date}, + {"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name__lt": self.assigned_to.last_name}, + {"assigned_to__first_name__lt": self.assigned_to.first_name}] + else: + conds = [{"assigned_to__isnull": True, + "created_date__lt": self.created_date}, + {"assigned_to__isnull": False}] + elif main_order == "-assigned_to__first_name": + if self.assigned_to: + conds = [{"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name": self.assigned_to.last_name, + "created_date__lt": self.created_date}, + {"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name__gt": self.assigned_to.last_name}, + {"assigned_to__first_name__gt": self.assigned_to.first_name}, + {"assigned_to__isnull": True}] + else: + conds = [{"assigned_to__isnull": True, + "created_date__lt": self.created_date}, + {"assigned_to__isnull": False}] + + return conds + + def _get_next_neighbor_filters(self, queryset): + conds = super()._get_next_neighbor_filters(queryset) + ordering = queryset.query.order_by + main_order = ordering[0] + if main_order == "severity": + conds = [{"severity__order__gt": self.severity.order}, + {"severity__order": self.severity.order, + "created_date__gt": self.created_date}] + elif main_order == "-severity": + conds = [{"severity__order__lt": self.severity.order}, + {"severity__order": self.severity.order, + "created_date__gt": self.created_date}] + elif main_order == "status": + conds = [{"status__order__gt": self.status.order}, + {"status__order": self.status.order, + "created_date__gt": self.created_date}] + elif main_order == "-status": + conds = [{"status__order__lt": self.status.order}, + {"status__order": self.status.order, + "created_date__gt": self.created_date}] + elif main_order == "priority": + conds = [{"priority__order__gt": self.priority.order}, + {"priority__order": self.priority.order, + "created_date__gt": self.created_date}] + elif main_order == "-priority": + conds = [{"priority__order__lt": self.priority.order}, + {"priority__order": self.priority.order, + "created_date__gt": self.created_date}] + elif main_order == "owner__first_name": + conds = [{"owner__first_name": self.owner.first_name, + "owner__last_name": self.owner.last_name, + "created_date__gt": self.created_date}, + {"owner__first_name": self.owner.first_name, + "owner__last_name__gt": self.owner.last_name}, + {"owner__first_name__gt": self.owner.first_name}] + elif main_order == "-owner__first_name": + conds = [{"owner__first_name": self.owner.first_name, + "owner__last_name": self.owner.last_name, + "created_date__gt": self.created_date}, + {"owner__first_name": self.owner.first_name, + "owner__last_name__lt": self.owner.last_name}, + {"owner__first_name__lt": self.owner.first_name}] + elif main_order == "assigned_to__first_name": + if self.assigned_to: + conds = [{"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name": self.assigned_to.last_name, + "created_date__gt": self.created_date}, + {"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name__gt": self.assigned_to.last_name}, + {"assigned_to__first_name__gt": self.assigned_to.first_name}, + {"assigned_to__isnull": True}] + else: + conds = [{"assigned_to__isnull": True, + "created_date__gt": self.created_date}] + elif main_order == "-assigned_to__first_name": + if self.assigned_to: + conds = [{"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name": self.assigned_to.last_name, + "created_date__gt": self.created_date}, + {"assigned_to__first_name": self.assigned_to.first_name, + "assigned_to__last_name__lt": self.assigned_to.last_name}, + {"assigned_to__first_name__lt": self.assigned_to.first_name}] + else: + conds = [{"assigned_to__isnull": True, + "created_date__gt": self.created_date}, + {"assigned_to__isnull": False}] + + return conds + @property def is_closed(self): return self.status.is_closed diff --git a/taiga/projects/userstories/models.py b/taiga/projects/userstories/models.py index cec2c97e..36d29cb6 100644 --- a/taiga/projects/userstories/models.py +++ b/taiga/projects/userstories/models.py @@ -113,12 +113,14 @@ class UserStory(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model): return "" % (self.id) def _get_prev_neighbor_filters(self, queryset): - return self._or([{"order__lt": "{obj.order}"}, - {"order__lte": "{obj.order}", "ref__lt": "{obj.ref}"}]) + conds = [{"order__lt": "{obj.order}"}, + {"order__lte": "{obj.order}", "ref__lt": "{obj.ref}"}] + return conds def _get_next_neighbor_filters(self, queryset): - return self._or([{"order__gt": "{obj.order}"}, - {"order__gte": "{obj.order}", "ref__gt": "{obj.ref}"}]) + conds = [{"order__gt": "{obj.order}"}, + {"order__gte": "{obj.order}", "ref__gt": "{obj.ref}"}] + return conds def get_role_points(self): return self.role_points