Moving neighbors logic into standalone module

remotes/origin/enhancement/email-actions
Anler Hp 2014-05-20 16:14:02 +02:00 committed by David Barragán Merino
parent ac49a76146
commit 8003abbbef
10 changed files with 504 additions and 272 deletions

View File

@ -63,13 +63,6 @@ class DestroyModelMixin(mixins.DestroyModelMixin):
# Other mixins (what they are doing here?)
class NeighborsApiMixin(object):
def filter_queryset(self, queryset, force=False):
for backend in self.get_filter_backends():
if force or self.action != "retrieve" or backend not in self.retrieve_exclude_filters:
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
class PreconditionMixin(object):
def pre_conditions_on_save(self, obj):

View File

@ -23,87 +23,3 @@ monkey.patch_api_view()
monkey.patch_serializer()
monkey.patch_import_module()
monkey.patch_south_hacks()
class NeighborsMixin:
def get_neighbors(self, queryset=None):
"""Get the objects around this object.
:param queryset: A queryset object to use as a starting point. Useful if you need to
pre-filter the neighbor candidates.
:return: The tuple `(previous, next)`.
"""
if queryset is None:
queryset = type(self).objects.get_queryset()
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
def _get_order_by(self, queryset):
return self._get_queryset_order_by(queryset) or self._meta.ordering
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
return value
def _transform_order_field_into_lookup(self, field, operator, operator_if_order_desc):
if field.startswith("-"):
field = field[1:]
operator = operator_if_order_desc
return field, operator
def _format(self, value):
if hasattr(value, "format"):
value = value.format(obj=self)
return value
def _or(self, conditions):
result = Q()
for condition in conditions:
result |= Q(**{key: self._format(condition[key]) for key in condition})
return result
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_prev_neighbor_filters(self, queryset):
return self._get_neighbor_filters(queryset, "lte", "gte")
def _get_next_neighbor_filters(self, queryset):
return self._get_neighbor_filters(queryset, "gte", "lte")
def _get_previous_neighbor(self, queryset):
queryset = queryset.filter(self._or(self._get_prev_neighbor_filters(queryset)))
try:
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

122
taiga/base/neighbors.py Normal file
View File

@ -0,0 +1,122 @@
from functools import partial
from collections import namedtuple
from django.db.models import Q
Neighbor = namedtuple("Neighbor", "left right")
def disjunction_filters(filters):
"""From a list of queryset filters, it returns a disjunction (OR) Q object.
:param filters: List of filters where each item is a dict where the keys are the lookups and
the values the values of those lookups.
:return: :class:`django.db.models.Q` instance representing the disjunction of the filters.
"""
result = Q()
for filter in filters:
result |= Q(**{lookup: value for lookup, value in filter.items()})
return result
def get_attribute(obj, attr):
"""Finds `attr` in obj.
:param obj: Object where to look for the attribute.
:param attr: Attribute name as a string. It can be a Django lookup field such as
`project__owner__name`, in which case it will look for `obj.project.owner.name`.
:return: The attribute value.
:raises: `AttributeError` if some attribute doesn't exist.
"""
chunks = attr.lstrip("-").split("__")
attr, chunks = chunks[0], chunks[1:]
obj = value = getattr(obj, attr)
for path in chunks:
value = getattr(obj, path)
obj = value
return value
def transform_field_into_lookup(name, value, operator="", operator_if_desc=""):
"""From a field name and value, return a dict that may be used as a queryset filter.
:param name: Field name as a string.
:param value: Field value.
:param operator: Operator to use in the lookup.
:param operator_if_desc: If the field is reversed (a "-" in front) use this operator
instead.
:return: A dict that may be used as a queryset filter.
"""
if name.startswith("-"):
name = name[1:]
operator = operator_if_desc
lookup = "{}{}".format(name, operator)
return {lookup: value}
def get_neighbors(obj, results_set=None):
"""Get the neighbors of a model instance.
The neighbors are the objects that are at the left/right of `obj` in the results set.
:param obj: The object you want to know its neighbors.
:param results_set: Find the neighbors applying the constraints of this set (a Django queryset
object).
:return: Tuple `<left neighbor>, <right neighbor>`. Left and right neighbors can be `None`.
"""
if results_set is None:
results_set = type(obj).objects.get_queryset()
try:
left = _left_candidates(obj, results_set).reverse()[0]
except IndexError:
left = None
try:
right = _right_candidates(obj, results_set)[0]
except IndexError:
right = None
return Neighbor(left, right)
def _get_candidates(obj, results_set, reverse=False):
ordering = (results_set.query.order_by or []) + list(obj._meta.ordering)
main_ordering, rest_ordering = ordering[0], ordering[1:]
try:
filters = obj.get_neighbors_additional_filters(results_set, ordering, reverse)
if filters is None:
raise AttributeError
except AttributeError:
filters = [order_field_as_filter(obj, main_ordering, reverse)]
filters += [ordering_fields_as_filter(obj, main_ordering, rest_ordering, reverse)]
return (results_set
.filter(~Q(id=obj.id), disjunction_filters(filters))
.distinct()
.order_by(*ordering))
_left_candidates = partial(_get_candidates, reverse=True)
_right_candidates = partial(_get_candidates, reverse=False)
def order_field_as_filter(obj, order_field, reverse=None, operator=None):
value = get_attribute(obj, order_field)
if reverse is not None:
if operator is None:
operator = ("__gt", "__lt")
operator = (operator[1], operator[0]) if reverse else operator
else:
operator = ()
return transform_field_into_lookup(order_field, value, *operator)
def ordering_fields_as_filter(obj, main_order_field, ordering_fields, reverse=False):
"""Transform a list of ordering fields into a queryset filter."""
filter = order_field_as_filter(obj, main_order_field)
for field in ordering_fields:
filter.update(order_field_as_filter(obj, field, reverse, ("__gte", "__lte")))
return filter

View File

@ -20,6 +20,7 @@ from rest_framework import serializers
from taiga.domains.base import get_active_domain
from taiga.domains.models import Domain
from .neighbors import get_neighbors
class PickleField(serializers.WritableField):
@ -72,9 +73,12 @@ class NeighborsSerializerMixin:
def get_neighbors(self, obj):
view, request = self.context.get("view", None), self.context.get("request", None)
if view and request:
queryset = view.filter_queryset(view.get_queryset(), True)
previous, next = obj.get_neighbors(queryset)
queryset = view.filter_queryset(view.get_queryset())
left, right = get_neighbors(obj, results_set=queryset)
else:
left = right = None
return {"previous": self.serialize_neighbor(previous),
"next": self.serialize_neighbor(next)}
return {"previous": None, "next": None}
return {
"previous": self.serialize_neighbor(left),
"next": self.serialize_neighbor(right)
}

View File

@ -26,7 +26,7 @@ from taiga.base import filters
from taiga.base import exceptions as exc
from taiga.base.decorators import list_route
from taiga.base.api import ModelCrudViewSet
from taiga.base.api import NeighborsApiMixin
from taiga.projects.mixins.notifications import NotificationSenderMixin
from . import models
@ -87,6 +87,7 @@ class IssuesFilter(filters.FilterBackend):
class IssuesOrdering(filters.FilterBackend):
def filter_queryset(self, request, queryset, view):
order_by = request.QUERY_PARAMS.get('order_by', None)
if order_by in ['owner', '-owner', 'assigned_to', '-assigned_to']:
return queryset.order_by(
'{}__first_name'.format(order_by),
@ -95,7 +96,7 @@ class IssuesOrdering(filters.FilterBackend):
return queryset
class IssueViewSet(NeighborsApiMixin, NotificationSenderMixin, ModelCrudViewSet):
class IssueViewSet(NotificationSenderMixin, ModelCrudViewSet):
model = models.Issue
queryset = models.Issue.objects.all().prefetch_related("attachments")
serializer_class = serializers.IssueNeighborsSerializer

View File

@ -23,13 +23,12 @@ from django.utils.translation import ugettext_lazy as _
from picklefield.fields import PickledObjectField
from taiga.base.models import NeighborsMixin
from taiga.base.utils.slug import ref_uniquely
from taiga.projects.notifications.models import WatchedMixin
from taiga.projects.mixins.blocked import BlockedMixin
class Issue(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model):
class Issue(WatchedMixin, BlockedMixin, models.Model):
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
verbose_name=_("ref"))
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
@ -77,164 +76,6 @@ class Issue(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model):
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)
try:
main_order = queryset.query.order_by[0]
except IndexError:
main_order = None
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
try:
main_order = ordering[0]
except IndexError:
main_order = None
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" and 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

View File

@ -28,7 +28,7 @@ from taiga.base.decorators import list_route
from taiga.base.decorators import action
from taiga.base.permissions import has_project_perm
from taiga.base.api import ModelCrudViewSet
from taiga.base.api import NeighborsApiMixin
from taiga.projects.mixins.notifications import NotificationSenderMixin
from taiga.projects.models import Project
from taiga.projects.history.services import take_snapshot
@ -39,7 +39,7 @@ from . import serializers
from . import services
class UserStoryViewSet(NeighborsApiMixin, NotificationSenderMixin, ModelCrudViewSet):
class UserStoryViewSet(NotificationSenderMixin, ModelCrudViewSet):
model = models.UserStory
serializer_class = serializers.UserStoryNeighborsSerializer
list_serializer_class = serializers.UserStorySerializer

View File

@ -22,7 +22,6 @@ from django.utils.translation import ugettext_lazy as _
from picklefield.fields import PickledObjectField
from taiga.base.models import NeighborsMixin
from taiga.base.utils.slug import ref_uniquely
from taiga.projects.notifications.models import WatchedMixin
from taiga.projects.mixins.blocked import BlockedMixin
@ -52,7 +51,8 @@ class RolePoints(models.Model):
return "{}: {}".format(self.role.name, self.points.name)
class UserStory(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model):
class UserStory(WatchedMixin, BlockedMixin, models.Model):
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
verbose_name=_("ref"))
milestone = models.ForeignKey("milestones.Milestone", null=True, blank=True,
@ -112,16 +112,6 @@ class UserStory(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model):
def __repr__(self):
return "<UserStory %s>" % (self.id)
def _get_prev_neighbor_filters(self, queryset):
conds = [{"order__lt": "{obj.order}"},
{"order__lte": "{obj.order}", "ref__lt": "{obj.ref}"}]
return conds
def _get_next_neighbor_filters(self, queryset):
conds = [{"order__gt": "{obj.order}"},
{"order__gte": "{obj.order}", "ref__gt": "{obj.ref}"}]
return conds
def get_role_points(self):
return self.role_points

View File

@ -5,6 +5,9 @@ from django.conf import settings
import taiga.domains.models
import taiga.projects.models
import taiga.projects.userstories.models
import taiga.projects.issues.models
import taiga.projects.milestones.models
import taiga.users.models
import taiga.userstorage.models
@ -76,3 +79,61 @@ class StorageEntryFactory(factory.DjangoModelFactory):
owner = factory.SubFactory("tests.factories.UserFactory")
key = factory.Sequence(lambda n: "key-{}".format(n))
value = factory.Sequence(lambda n: "value {}".format(n))
class UserStoryFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.userstories.models.UserStory
ref = factory.Sequence(lambda n: n)
project = factory.SubFactory("tests.factories.ProjectFactory")
owner = factory.SubFactory("tests.factories.UserFactory")
subject = factory.Sequence(lambda n: "User Story {}".format(n))
class MilestoneFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.milestones.models.Milestone
name = factory.Sequence(lambda n: "Milestone {}".format(n))
owner = factory.SubFactory("tests.factories.UserFactory")
project = factory.SubFactory("tests.factories.ProjectFactory")
class IssueFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.issues.models.Issue
subject = factory.Sequence(lambda n: "Issue {}".format(n))
owner = factory.SubFactory("tests.factories.UserFactory")
project = factory.SubFactory("tests.factories.ProjectFactory")
status = factory.SubFactory("tests.factories.IssueStatusFactory")
severity = factory.SubFactory("tests.factories.SeverityFactory")
priority = factory.SubFactory("tests.factories.PriorityFactory")
type = factory.SubFactory("tests.factories.IssueTypeFactory")
milestone = factory.SubFactory("tests.factories.MilestoneFactory")
class IssueStatusFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.models.IssueStatus
name = factory.Sequence(lambda n: "Issue Status {}".format(n))
project = factory.SubFactory("tests.factories.ProjectFactory")
class SeverityFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.models.Severity
name = factory.Sequence(lambda n: "Severity {}".format(n))
project = factory.SubFactory("tests.factories.ProjectFactory")
class PriorityFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.models.Priority
name = factory.Sequence(lambda n: "Priority {}".format(n))
project = factory.SubFactory("tests.factories.ProjectFactory")
class IssueTypeFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.models.IssueType
name = factory.Sequence(lambda n: "Issue Type {}".format(n))
project = factory.SubFactory("tests.factories.ProjectFactory")

View File

@ -0,0 +1,304 @@
from functools import partial
from unittest import mock
import pytest
from taiga.projects.userstories.models import UserStory
from taiga.projects.issues.models import Issue
from taiga.base.utils.db import filter_by_tags
from taiga.base import neighbors as n
from .. import factories as f
class TestGetAttribute:
def test_no_attribute(self, object):
object.first_name = "name"
with pytest.raises(AttributeError):
n.get_attribute(object, "name")
with pytest.raises(AttributeError):
n.get_attribute(object, "first_name__last_name")
def test_one_level(self, object):
object.name = "name"
assert n.get_attribute(object, "name") == object.name
def test_two_levels(self, object):
object.name = object
object.name.first_name = "first name"
assert n.get_attribute(object, "name__first_name") == object.name.first_name
def test_three_levels(self, object):
object.info = object
object.info.name = object
object.info.name.first_name = "first name"
assert n.get_attribute(object, "info__name__first_name") == object.info.name.first_name
def test_transform_field_into_lookup():
transform = partial(n.transform_field_into_lookup, value="chuck", operator="__lt",
operator_if_desc="__gt")
assert transform(name="name") == {"name__lt": "chuck"}
assert transform(name="-name") == {"name__gt": "chuck"}
def test_disjunction_filters():
filters = [{"age__lt": 21, "name__eq": "chuck"}]
result_str = str(n.disjunction_filters(filters))
assert result_str.startswith("(OR: ")
assert "('age__lt', 21)" in result_str
assert "('name__eq', 'chuck')" in result_str
@pytest.mark.django_db
@pytest.mark.slow
class TestUserStories:
def test_no_filters(self):
project = f.ProjectFactory.create()
us1 = f.UserStoryFactory.create(project=project)
us2 = f.UserStoryFactory.create(project=project)
us3 = f.UserStoryFactory.create(project=project)
neighbors = n.get_neighbors(us2)
assert neighbors.left == us1
assert neighbors.right == us3
def test_filtered_by_tags(self):
tags = ["test"]
project = f.ProjectFactory.create()
f.UserStoryFactory.create(project=project)
us1 = f.UserStoryFactory.create(project=project, tags=tags)
us2 = f.UserStoryFactory.create(project=project, tags=tags)
test_user_stories = filter_by_tags(tags, queryset=UserStory.objects.get_queryset())
neighbors = n.get_neighbors(us1, results_set=test_user_stories)
assert neighbors.left is None
assert neighbors.right == us2
def test_filtered_by_milestone(self):
project = f.ProjectFactory.create()
milestone = f.MilestoneFactory.create(project=project)
f.UserStoryFactory.create(project=project)
us1 = f.UserStoryFactory.create(project=project, milestone=milestone)
us2 = f.UserStoryFactory.create(project=project, milestone=milestone)
milestone_user_stories = UserStory.objects.filter(milestone=milestone)
neighbors = n.get_neighbors(us1, results_set=milestone_user_stories)
assert neighbors.left is None
assert neighbors.right == us2
@pytest.mark.django_db
@pytest.mark.slow
class TestIssues:
def test_no_filters(self):
project = f.ProjectFactory.create()
issue1 = f.IssueFactory.create(project=project)
issue2 = f.IssueFactory.create(project=project)
issue3 = f.IssueFactory.create(project=project)
neighbors = n.get_neighbors(issue2)
assert neighbors.left == issue1
assert neighbors.right == issue3
def test_ordering_by_severity(self):
project = f.ProjectFactory.create()
severity1 = f.SeverityFactory.create(project=project, order=1)
severity2 = f.SeverityFactory.create(project=project, order=2)
issue1 = f.IssueFactory.create(project=project, severity=severity2)
issue2 = f.IssueFactory.create(project=project, severity=severity1)
issue3 = f.IssueFactory.create(project=project, severity=severity1)
issues = Issue.objects.filter(project=project).order_by("severity")
issue2_neighbors = n.get_neighbors(issue2, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue2_neighbors.left is None
assert issue2_neighbors.right == issue3
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right == issue1
def test_ordering_by_severity_desc(self):
project = f.ProjectFactory.create()
severity1 = f.SeverityFactory.create(project=project, order=1)
severity2 = f.SeverityFactory.create(project=project, order=2)
issue1 = f.IssueFactory.create(project=project, severity=severity2)
issue2 = f.IssueFactory.create(project=project, severity=severity1)
issue3 = f.IssueFactory.create(project=project, severity=severity1)
issues = Issue.objects.filter(project=project).order_by("-severity")
issue1_neighbors = n.get_neighbors(issue1, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue1_neighbors.left is None
assert issue1_neighbors.right == issue2
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right is None
def test_ordering_by_status(self):
project = f.ProjectFactory.create()
status1 = f.IssueStatusFactory.create(project=project, order=1)
status2 = f.IssueStatusFactory.create(project=project, order=2)
issue1 = f.IssueFactory.create(project=project, status=status2)
issue2 = f.IssueFactory.create(project=project, status=status1)
issue3 = f.IssueFactory.create(project=project, status=status1)
issues = Issue.objects.filter(project=project).order_by("status")
issue2_neighbors = n.get_neighbors(issue2, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue2_neighbors.left is None
assert issue2_neighbors.right == issue3
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right == issue1
def test_ordering_by_status_desc(self):
project = f.ProjectFactory.create()
status1 = f.IssueStatusFactory.create(project=project, order=1)
status2 = f.IssueStatusFactory.create(project=project, order=2)
issue1 = f.IssueFactory.create(project=project, status=status2)
issue2 = f.IssueFactory.create(project=project, status=status1)
issue3 = f.IssueFactory.create(project=project, status=status1)
issues = Issue.objects.filter(project=project).order_by("-status")
issue1_neighbors = n.get_neighbors(issue1, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue1_neighbors.left is None
assert issue1_neighbors.right == issue2
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right is None
def test_ordering_by_priority(self):
project = f.ProjectFactory.create()
priority1 = f.PriorityFactory.create(project=project, order=1)
priority2 = f.PriorityFactory.create(project=project, order=2)
issue1 = f.IssueFactory.create(project=project, priority=priority2)
issue2 = f.IssueFactory.create(project=project, priority=priority1)
issue3 = f.IssueFactory.create(project=project, priority=priority1)
issues = Issue.objects.filter(project=project).order_by("priority")
issue2_neighbors = n.get_neighbors(issue2, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue2_neighbors.left is None
assert issue2_neighbors.right == issue3
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right == issue1
def test_ordering_by_priority_desc(self):
project = f.ProjectFactory.create()
priority1 = f.PriorityFactory.create(project=project, order=1)
priority2 = f.PriorityFactory.create(project=project, order=2)
issue1 = f.IssueFactory.create(project=project, priority=priority2)
issue2 = f.IssueFactory.create(project=project, priority=priority1)
issue3 = f.IssueFactory.create(project=project, priority=priority1)
issues = Issue.objects.filter(project=project).order_by("-priority")
issue1_neighbors = n.get_neighbors(issue1, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue1_neighbors.left is None
assert issue1_neighbors.right == issue2
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right is None
def test_ordering_by_owner(self):
project = f.ProjectFactory.create()
owner1 = f.UserFactory.create(first_name="Chuck", last_name="Norris")
owner2 = f.UserFactory.create(first_name="George", last_name="Of The Jungle")
issue1 = f.IssueFactory.create(project=project, owner=owner2)
issue2 = f.IssueFactory.create(project=project, owner=owner1)
issue3 = f.IssueFactory.create(project=project, owner=owner1)
issues = Issue.objects.filter(project=project).order_by("owner__first_name")
issue2_neighbors = n.get_neighbors(issue2, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue2_neighbors.left is None
assert issue2_neighbors.right == issue3
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right == issue1
def test_ordering_by_owner_desc(self):
project = f.ProjectFactory.create()
owner1 = f.UserFactory.create(first_name="Chuck", last_name="Norris")
owner2 = f.UserFactory.create(first_name="George", last_name="Of The Jungle")
issue1 = f.IssueFactory.create(project=project, owner=owner2)
issue2 = f.IssueFactory.create(project=project, owner=owner1)
issue3 = f.IssueFactory.create(project=project, owner=owner1)
issues = Issue.objects.filter(project=project).order_by("-owner__first_name")
issue1_neighbors = n.get_neighbors(issue1, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue1_neighbors.left is None
assert issue1_neighbors.right == issue2
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right is None
def test_ordering_by_assigned_to(self):
project = f.ProjectFactory.create()
assigned_to1 = f.UserFactory.create(first_name="Chuck", last_name="Norris")
assigned_to2 = f.UserFactory.create(first_name="George", last_name="Of The Jungle")
issue1 = f.IssueFactory.create(project=project, assigned_to=assigned_to2)
issue2 = f.IssueFactory.create(project=project, assigned_to=assigned_to1)
issue3 = f.IssueFactory.create(project=project, assigned_to=assigned_to1)
issues = Issue.objects.filter(project=project).order_by("assigned_to__first_name")
issue2_neighbors = n.get_neighbors(issue2, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue2_neighbors.left is None
assert issue2_neighbors.right == issue3
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right == issue1
def test_ordering_by_assigned_to_desc(self):
project = f.ProjectFactory.create()
assigned_to1 = f.UserFactory.create(first_name="Chuck", last_name="Norris")
assigned_to2 = f.UserFactory.create(first_name="George", last_name="Of The Jungle")
issue1 = f.IssueFactory.create(project=project, assigned_to=assigned_to2)
issue2 = f.IssueFactory.create(project=project, assigned_to=assigned_to1)
issue3 = f.IssueFactory.create(project=project, assigned_to=assigned_to1)
issues = Issue.objects.filter(project=project).order_by("-assigned_to__first_name")
issue1_neighbors = n.get_neighbors(issue1, results_set=issues)
issue3_neighbors = n.get_neighbors(issue3, results_set=issues)
assert issue1_neighbors.left is None
assert issue1_neighbors.right == issue2
assert issue3_neighbors.left == issue2
assert issue3_neighbors.right is None