Merge pull request #818 from mjrk/feature/date-filters

Adds created-, modified-, finished- and finish_date filters to issues, tasks and userstories (if applicable)
remotes/origin/issue/4795/notification_even_they_are_disabled
David Barragán Merino 2016-09-02 12:45:25 +02:00 committed by GitHub
commit 24a507b8e4
9 changed files with 445 additions and 3 deletions

View File

@ -29,6 +29,7 @@ answer newbie questions, and generally made taiga that much better:
- Joe Letts
- Julien Palard
- luyikei <luyikei.qmltu@gmail.com>
- Michael Jurke <m.jurke@gmx.de>
- Motius GmbH <mail@motius.de>
- Riccardo Coccioli <riccardo.coccioli@immobiliare.it>
- Ricky Posner <e@eposner.com>

View File

@ -29,6 +29,9 @@
- Improve messages generated on webhooks input.
- Add mentions support in commit messages.
- Cleanup hooks code.
- Add created-, modified-, finished- and finish_date queryset filters
- Support exact match, gt, gte, lt, lte
- added issues, tasks and userstories accordingly
### Misc
- [API] Improve performance of some calls over list.

View File

@ -18,6 +18,8 @@
import logging
from dateutil.parser import parse as parse_date
from django.apps import apps
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
@ -447,6 +449,68 @@ class WatchersFilter(FilterBackend):
return super().filter_queryset(request, queryset, view)
class BaseCompareFilter(FilterBackend):
operators = ["", "lt", "gt", "lte", "gte"]
def __init__(self, filter_name_base=None, operators=None):
if filter_name_base:
self.filter_name_base = filter_name_base
def _get_filter_names(self):
return [
self._get_filter_name(operator)
for operator in self.operators
]
def _get_filter_name(self, operator):
if operator and len(operator) > 0:
return "{base}__{operator}".format(
base=self.filter_name_base, operator=operator
)
else:
return self.filter_name_base
def _get_constraints(self, params):
constraints = {}
for filter_name in self._get_filter_names():
raw_value = params.get(filter_name, None)
if raw_value is not None:
constraints[filter_name] = self._get_value(raw_value)
return constraints
def _get_value(self, raw_value):
return raw_value
def filter_queryset(self, request, queryset, view):
constraints = self._get_constraints(request.QUERY_PARAMS)
if len(constraints) > 0:
queryset = queryset.filter(**constraints)
return super().filter_queryset(request, queryset, view)
class BaseDateFilter(BaseCompareFilter):
def _get_value(self, raw_value):
return parse_date(raw_value)
class CreatedDateFilter(BaseDateFilter):
filter_name_base = "created_date"
class ModifiedDateFilter(BaseDateFilter):
filter_name_base = "modified_date"
class FinishedDateFilter(BaseDateFilter):
filter_name_base = "finished_date"
class FinishDateFilter(BaseDateFilter):
filter_name_base = "finish_date"
#####################################################################
# Text search filters
#####################################################################

View File

@ -58,7 +58,10 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W
filters.TagsFilter,
filters.WatchersFilter,
filters.QFilter,
filters.OrderByFilterMixin)
filters.OrderByFilterMixin,
filters.CreatedDateFilter,
filters.ModifiedDateFilter,
filters.FinishedDateFilter)
retrieve_exclude_filters = (filters.OwnersFilter,
filters.AssignedToFilter,
filters.StatusesFilter,

View File

@ -56,7 +56,10 @@ class TaskViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin,
filters.StatusesFilter,
filters.TagsFilter,
filters.WatchersFilter,
filters.QFilter)
filters.QFilter,
filters.CreatedDateFilter,
filters.ModifiedDateFilter,
filters.FinishedDateFilter)
retrieve_exclude_filters = (filters.OwnersFilter,
filters.AssignedToFilter,
filters.StatusesFilter,

View File

@ -64,7 +64,10 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi
filters.TagsFilter,
filters.WatchersFilter,
filters.QFilter,
filters.OrderByFilterMixin)
filters.OrderByFilterMixin,
filters.CreatedDateFilter,
filters.ModifiedDateFilter,
filters.FinishDateFilter)
retrieve_exclude_filters = (filters.OwnersFilter,
filters.AssignedToFilter,
filters.StatusesFilter,

View File

@ -19,6 +19,10 @@
import uuid
import csv
import pytz
from datetime import datetime, timedelta
from urllib.parse import quote
from unittest import mock
@ -221,6 +225,149 @@ def test_api_filter_by_text_6(client):
assert number_of_issues == 1
def test_api_filter_by_created_date(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
issue = f.create_issue(owner=user)
url = reverse("issues-list") + "?created_date=%s" % (
quote(issue.created_date.isoformat())
)
client.login(issue.owner)
response = client.get(url)
number_of_issues = len(response.data)
assert response.status_code == 200
assert number_of_issues == 1
assert response.data[0]["ref"] == issue.ref
def test_api_filter_by_created_date__gt(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
issue = f.create_issue(owner=user)
url = reverse("issues-list") + "?created_date__gt=%s" % (
quote(one_day_ago.isoformat())
)
client.login(issue.owner)
response = client.get(url)
number_of_issues = len(response.data)
assert response.status_code == 200
assert number_of_issues == 1
assert response.data[0]["ref"] == issue.ref
def test_api_filter_by_created_date__gte(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
issue = f.create_issue(owner=user)
url = reverse("issues-list") + "?created_date__gte=%s" % (
quote(one_day_ago.isoformat())
)
client.login(issue.owner)
response = client.get(url)
number_of_issues = len(response.data)
assert response.status_code == 200
assert number_of_issues == 2
def test_api_filter_by_created_date__lt(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
issue = f.create_issue(owner=user)
url = reverse("issues-list") + "?created_date__lt=%s" % (
quote(issue.created_date.isoformat())
)
client.login(issue.owner)
response = client.get(url)
number_of_issues = len(response.data)
assert response.status_code == 200
assert response.data[0]["ref"] == old_issue.ref
def test_api_filter_by_created_date__lte(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
issue = f.create_issue(owner=user)
url = reverse("issues-list") + "?created_date__lte=%s" % (
quote(issue.created_date.isoformat())
)
client.login(issue.owner)
response = client.get(url)
number_of_issues = len(response.data)
assert response.status_code == 200
assert number_of_issues == 2
def test_api_filter_by_modified_date__gte(client):
user = f.UserFactory(is_superuser=True)
_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
older_issue = f.create_issue(owner=user)
issue = f.create_issue(owner=user)
# we have to refresh as it slightly differs
issue.refresh_from_db()
assert older_issue.modified_date < issue.modified_date
url = reverse("issues-list") + "?modified_date__gte=%s" % (
quote(issue.modified_date.isoformat())
)
client.login(issue.owner)
response = client.get(url)
number_of_issues = len(response.data)
assert response.status_code == 200
assert number_of_issues == 1
assert response.data[0]["ref"] == issue.ref
def test_api_filter_by_finished_date(client):
user = f.UserFactory(is_superuser=True)
project = f.ProjectFactory.create()
status0 = f.IssueStatusFactory.create(project=project, is_closed=True)
issue = f.create_issue(owner=user)
finished_issue = f.create_issue(owner=user, status=status0)
assert finished_issue.finished_date
url = reverse("issues-list") + "?finished_date__gte=%s" % (
quote(finished_issue.finished_date.isoformat())
)
client.login(issue.owner)
response = client.get(url)
number_of_issues = len(response.data)
assert response.status_code == 200
assert number_of_issues == 1
assert response.data[0]["ref"] == finished_issue.ref
def test_api_filters_data(client):
project = f.ProjectFactory.create()
user1 = f.UserFactory.create(is_superuser=True)

View File

@ -19,6 +19,10 @@
import uuid
import csv
import pytz
from datetime import datetime, timedelta
from urllib.parse import quote
from unittest import mock
@ -451,6 +455,110 @@ def test_get_tasks_including_attachments(client):
assert len(response.data[0].get("attachments")) == 1
def test_api_filter_by_created_date(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_task = f.create_task(owner=user, created_date=one_day_ago)
task = f.create_task(owner=user, subject="test")
url = reverse("tasks-list") + "?created_date=%s" % (
quote(task.created_date.isoformat())
)
client.login(task.owner)
response = client.get(url)
number_of_tasks = len(response.data)
assert response.status_code == 200
assert number_of_tasks == 1
assert response.data[0]["subject"] == task.subject
def test_api_filter_by_created_date__lt(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_task = f.create_task(owner=user, created_date=one_day_ago)
task = f.create_task(owner=user, subject="test")
url = reverse("tasks-list") + "?created_date__lt=%s" % (
quote(task.created_date.isoformat())
)
client.login(task.owner)
response = client.get(url)
number_of_tasks = len(response.data)
assert response.status_code == 200
assert response.data[0]["subject"] == old_task.subject
def test_api_filter_by_created_date__lte(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_task = f.create_task(owner=user, created_date=one_day_ago)
task = f.create_task(owner=user)
url = reverse("tasks-list") + "?created_date__lte=%s" % (
quote(task.created_date.isoformat())
)
client.login(task.owner)
response = client.get(url)
number_of_tasks = len(response.data)
assert response.status_code == 200
assert number_of_tasks == 2
def test_api_filter_by_modified_date__gte(client):
user = f.UserFactory(is_superuser=True)
_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
older_task = f.create_task(owner=user)
task = f.create_task(owner=user, subject="test")
# we have to refresh as it slightly differs
task.refresh_from_db()
assert older_task.modified_date < task.modified_date
url = reverse("tasks-list") + "?modified_date__gte=%s" % (
quote(task.modified_date.isoformat())
)
client.login(task.owner)
response = client.get(url)
number_of_tasks = len(response.data)
assert response.status_code == 200
assert number_of_tasks == 1
assert response.data[0]["subject"] == task.subject
def test_api_filter_by_finished_date(client):
user = f.UserFactory(is_superuser=True)
project = f.ProjectFactory.create()
status0 = f.TaskStatusFactory.create(project=project, is_closed=True)
task = f.create_task(owner=user)
finished_task = f.create_task(owner=user, status=status0, subject="test")
assert finished_task.finished_date
url = reverse("tasks-list") + "?finished_date__gte=%s" % (
quote(finished_task.finished_date.isoformat())
)
client.login(task.owner)
response = client.get(url)
number_of_tasks = len(response.data)
assert response.status_code == 200
assert number_of_tasks == 1
assert response.data[0]["subject"] == finished_task.subject
def test_api_filters_data(client):
project = f.ProjectFactory.create()
user1 = f.UserFactory.create(is_superuser=True)

View File

@ -19,6 +19,10 @@
import uuid
import csv
import pytz
from datetime import datetime, timedelta
from urllib.parse import quote
from unittest import mock
from django.core.urlresolvers import reverse
@ -501,6 +505,112 @@ def test_get_total_points(client):
assert us_mixed.get_total_points() == 1.0
def test_api_filter_by_created_date(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_userstory = f.create_userstory(owner=user, created_date=one_day_ago)
userstory = f.create_userstory(owner=user, subject="test")
url = reverse("userstories-list") + "?created_date=%s" % (
quote(userstory.created_date.isoformat())
)
client.login(userstory.owner)
response = client.get(url)
number_of_userstories = len(response.data)
assert response.status_code == 200
assert number_of_userstories == 1
assert response.data[0]["subject"] == userstory.subject
def test_api_filter_by_created_date__lt(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_userstory = f.create_userstory(
owner=user, created_date=one_day_ago, subject="old test"
)
userstory = f.create_userstory(owner=user)
url = reverse("userstories-list") + "?created_date__lt=%s" % (
quote(userstory.created_date.isoformat())
)
client.login(userstory.owner)
response = client.get(url)
number_of_userstories = len(response.data)
assert response.status_code == 200
assert response.data[0]["subject"] == old_userstory.subject
def test_api_filter_by_created_date__lte(client):
user = f.UserFactory(is_superuser=True)
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
old_userstory = f.create_userstory(owner=user, created_date=one_day_ago)
userstory = f.create_userstory(owner=user)
url = reverse("userstories-list") + "?created_date__lte=%s" % (
quote(userstory.created_date.isoformat())
)
client.login(userstory.owner)
response = client.get(url)
number_of_userstories = len(response.data)
assert response.status_code == 200
assert number_of_userstories == 2
def test_api_filter_by_modified_date__gte(client):
user = f.UserFactory(is_superuser=True)
older_userstory = f.create_userstory(owner=user)
userstory = f.create_userstory(owner=user, subject="test")
# we have to refresh as it slightly differs
userstory.refresh_from_db()
assert older_userstory.modified_date < userstory.modified_date
url = reverse("userstories-list") + "?modified_date__gte=%s" % (
quote(userstory.modified_date.isoformat())
)
client.login(userstory.owner)
response = client.get(url)
number_of_userstories = len(response.data)
assert response.status_code == 200
assert number_of_userstories == 1
assert response.data[0]["subject"] == userstory.subject
def test_api_filter_by_finish_date(client):
user = f.UserFactory(is_superuser=True)
one_day_later = datetime.now(pytz.utc) + timedelta(days=1)
userstory = f.create_userstory(owner=user)
userstory_to_finish = f.create_userstory(
owner=user, finish_date=one_day_later, subject="test"
)
assert userstory_to_finish.finish_date
url = reverse("userstories-list") + "?finish_date__gte=%s" % (
quote(userstory_to_finish.finish_date.isoformat())
)
client.login(userstory.owner)
response = client.get(url)
number_of_userstories = len(response.data)
assert response.status_code == 200
assert number_of_userstories == 1
assert response.data[0]["subject"] == userstory_to_finish.subject
def test_api_filters_data(client):
project = f.ProjectFactory.create()
user1 = f.UserFactory.create(is_superuser=True)