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 - Joe Letts
- Julien Palard - Julien Palard
- luyikei <luyikei.qmltu@gmail.com> - luyikei <luyikei.qmltu@gmail.com>
- Michael Jurke <m.jurke@gmx.de>
- Motius GmbH <mail@motius.de> - Motius GmbH <mail@motius.de>
- Riccardo Coccioli <riccardo.coccioli@immobiliare.it> - Riccardo Coccioli <riccardo.coccioli@immobiliare.it>
- Ricky Posner <e@eposner.com> - Ricky Posner <e@eposner.com>

View File

@ -29,6 +29,9 @@
- Improve messages generated on webhooks input. - Improve messages generated on webhooks input.
- Add mentions support in commit messages. - Add mentions support in commit messages.
- Cleanup hooks code. - 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 ### Misc
- [API] Improve performance of some calls over list. - [API] Improve performance of some calls over list.

View File

@ -18,6 +18,8 @@
import logging import logging
from dateutil.parser import parse as parse_date
from django.apps import apps from django.apps import apps
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q from django.db.models import Q
@ -447,6 +449,68 @@ class WatchersFilter(FilterBackend):
return super().filter_queryset(request, queryset, view) 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 # Text search filters
##################################################################### #####################################################################

View File

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

View File

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

View File

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

View File

@ -19,6 +19,10 @@
import uuid import uuid
import csv import csv
import pytz
from datetime import datetime, timedelta
from urllib.parse import quote
from unittest import mock from unittest import mock
@ -221,6 +225,149 @@ def test_api_filter_by_text_6(client):
assert number_of_issues == 1 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): def test_api_filters_data(client):
project = f.ProjectFactory.create() project = f.ProjectFactory.create()
user1 = f.UserFactory.create(is_superuser=True) user1 = f.UserFactory.create(is_superuser=True)

View File

@ -19,6 +19,10 @@
import uuid import uuid
import csv import csv
import pytz
from datetime import datetime, timedelta
from urllib.parse import quote
from unittest import mock from unittest import mock
@ -451,6 +455,110 @@ def test_get_tasks_including_attachments(client):
assert len(response.data[0].get("attachments")) == 1 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): def test_api_filters_data(client):
project = f.ProjectFactory.create() project = f.ProjectFactory.create()
user1 = f.UserFactory.create(is_superuser=True) user1 = f.UserFactory.create(is_superuser=True)

View File

@ -19,6 +19,10 @@
import uuid import uuid
import csv import csv
import pytz
from datetime import datetime, timedelta
from urllib.parse import quote
from unittest import mock from unittest import mock
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@ -501,6 +505,112 @@ def test_get_total_points(client):
assert us_mixed.get_total_points() == 1.0 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): def test_api_filters_data(client):
project = f.ProjectFactory.create() project = f.ProjectFactory.create()
user1 = f.UserFactory.create(is_superuser=True) user1 = f.UserFactory.create(is_superuser=True)