Skip to content

Commit

Permalink
Merge pull request #141 from GeneriekPublicatiePlatformWoo/feature/68…
Browse files Browse the repository at this point in the history
…-publication-filters

Feature/68 publication filters
  • Loading branch information
sergei-maertens authored Nov 18, 2024
2 parents c6673aa + b28045f commit dc0acfb
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 7 deletions.
31 changes: 31 additions & 0 deletions src/woo_publications/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,19 @@ paths:
schema:
type: string
description: Filter publicaties op basis van de identificatie van de eigenaar.
- in: query
name: informatieCategorieen
schema:
type: array
items:
type: string
format: uuid
description: |-
Filter publications that belong to a particular information category. When you specify multiple categories, publications belonging to any category are returned.
Filter values should be the UUID of the categories.
explode: true
style: form
- name: page
required: false
in: query
Expand All @@ -815,6 +828,24 @@ paths:
* `gepubliceerd` - Published
* `concept` - Concept
* `ingetrokken` - Revoked
- in: query
name: registratiedatumTot
schema:
type: string
format: date-time
description: Filter publications that were registered before the given value.
- in: query
name: registratiedatumVanaf
schema:
type: string
format: date-time
description: Filter publications that were registered after or on the given
value.
- in: query
name: search
schema:
type: string
description: Searches publications based on the official and short title.
- in: query
name: sorteer
schema:
Expand Down
10 changes: 10 additions & 0 deletions src/woo_publications/conf/ci.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
import warnings

from django.core.paginator import UnorderedObjectListWarning

os.environ.setdefault("IS_HTTPS", "no")
os.environ.setdefault("SECRET_KEY", "for-testing-purposes-only")
os.environ.setdefault("LOG_REQUESTS", "no")
Expand Down Expand Up @@ -39,3 +41,11 @@
RuntimeWarning,
r"django\.db\.models\.fields",
)

# querysets in api viewsets *must* be ordered
warnings.filterwarnings(
"error",
r"Pagination may yield inconsistent results with an unordered object_list: .*",
UnorderedObjectListWarning,
r"rest_framework\.pagination",
)
10 changes: 10 additions & 0 deletions src/woo_publications/conf/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import sys
import warnings

from django.core.paginator import UnorderedObjectListWarning

os.environ.setdefault("DEBUG", "yes")
os.environ.setdefault("ALLOWED_HOSTS", "*")
os.environ.setdefault(
Expand Down Expand Up @@ -103,6 +105,14 @@
r"django\.db\.models\.fields",
)

# querysets in api viewsets *must* be ordered
warnings.filterwarnings(
"error",
r"Pagination may yield inconsistent results with an unordered object_list: .*",
UnorderedObjectListWarning,
r"rest_framework\.pagination",
)

# Override settings with local settings.
try:
from .local import * # pyright: ignore # noqa
Expand Down
55 changes: 48 additions & 7 deletions src/woo_publications/publications/api/filters.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.translation import gettext_lazy as _

from django_filters.rest_framework import FilterSet, filters
from django_filters.widgets import CSVWidget

from woo_publications.logging.constants import Events
from woo_publications.logging.models import TimelineLogProxy
from woo_publications.metadata.models import InformationCategory

from ..constants import PublicationStatusOptions
from ..models import Document, Publication
Expand Down Expand Up @@ -47,13 +50,9 @@ class Meta:


class PublicationFilterSet(FilterSet):
sorteer = filters.OrderingFilter(
help_text=_("Order on."),
fields=(
"registratiedatum",
"officiele_titel",
"verkorte_titel",
),
search = filters.CharFilter(
help_text=_("Searches publications based on the official and short title."),
method="search_official_and_short_title",
)
eigenaar = filters.CharFilter(
help_text=_("Filter publications based on the owner identifier of the object."),
Expand All @@ -63,12 +62,49 @@ class PublicationFilterSet(FilterSet):
help_text=_("Filter publications based on the publication status."),
choices=PublicationStatusOptions.choices,
)
registratiedatum_vanaf = filters.DateTimeFilter(
help_text=_(
"Filter publications that were registered after or on the given value."
),
field_name="registratiedatum",
lookup_expr="gte",
)
registratiedatum_tot = filters.DateTimeFilter(
help_text=_("Filter publications that were registered before the given value."),
field_name="registratiedatum",
lookup_expr="lt",
)
informatie_categorieen = filters.ModelMultipleChoiceFilter(
help_text=_(
"Filter publications that belong to a particular information category. "
"When you specify multiple categories, publications belonging to any "
"category are returned.\n\n"
"Filter values should be the UUID of the categories."
),
field_name="informatie_categorieen__uuid",
to_field_name="uuid",
queryset=InformationCategory.objects.all(),
widget=CSVWidget(),
)

sorteer = filters.OrderingFilter(
help_text=_("Order on."),
fields=(
"registratiedatum",
"officiele_titel",
"verkorte_titel",
),
)

class Meta:
model = Publication
fields = (
"search",
"eigenaar",
"publicatiestatus",
"informatie_categorieen",
"registratiedatum_vanaf",
"registratiedatum_tot",
"sorteer",
)

Expand All @@ -82,3 +118,8 @@ def filter_eigenaar(self, queryset, name: str, value: str):
).values_list("object_id", flat=True)

return queryset.filter(pk__in=[id for id in publication_object_ids])

def search_official_and_short_title(self, queryset, name: str, value: str):
return queryset.filter(
Q(officiele_titel__icontains=value) | Q(verkorte_titel__icontains=value)
)
206 changes: 206 additions & 0 deletions src/woo_publications/publications/tests/test_publications_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,212 @@ def test_list_publications_filter_order(self):
self.assertEqual(data["results"][0], expected_second_item_data)
self.assertEqual(data["results"][1], expected_first_item_data)

def test_list_publications_filter_information_categories(self):
ic, ic2, ic3, ic4 = InformationCategoryFactory.create_batch(4)
publication = PublicationFactory.create(informatie_categorieen=[ic])
publication2 = PublicationFactory.create(informatie_categorieen=[ic2])
publication3 = PublicationFactory.create(informatie_categorieen=[ic3, ic4])

with self.subTest("filter on a single information category"):
response = self.client.get(
reverse("api:publication-list"),
{"informatieCategorieen": str(ic.uuid)},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication.uuid))

with self.subTest("filter on multiple information categories "):
response = self.client.get(
reverse("api:publication-list"),
{"informatieCategorieen": f"{ic2.uuid},{ic4.uuid}"},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 2)
self.assertEqual(data["results"][0]["uuid"], str(publication3.uuid))
self.assertEqual(data["results"][1]["uuid"], str(publication2.uuid))

def test_list_publications_filter_registratie_datum(self):
ic = InformationCategoryFactory.create()
with freeze_time("2024-09-24T12:00:00-00:00"):
publication = PublicationFactory.create(informatie_categorieen=[ic])
with freeze_time("2024-09-25T12:00:00-00:00"):
publication2 = PublicationFactory.create(informatie_categorieen=[ic])
with freeze_time("2024-09-26T12:00:00-00:00"):
publication3 = PublicationFactory.create(informatie_categorieen=[ic])

with self.subTest("lte specific tests"):
with self.subTest("filter on gte date is exact match"):
response = self.client.get(
reverse("api:publication-list"),
{"registratiedatumVanaf": "2024-09-26T12:00:00-00:00"},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication3.uuid))

with self.subTest("filter on gte date is greater then publication"):
response = self.client.get(
reverse("api:publication-list"),
{"registratiedatumVanaf": "2024-09-26T00:00:00-00:00"},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication3.uuid))

with self.subTest("lte specific tests"):
with self.subTest("filter on lte date is exact match"):
response = self.client.get(
reverse("api:publication-list"),
{"registratiedatumTot": "2024-09-24T12:00:01-00:00"},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication.uuid))

with self.subTest("filter on lte date is lesser then publication"):
response = self.client.get(
reverse("api:publication-list"),
{"registratiedatumTot": "2024-09-25T00:00:00-00:00"},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication.uuid))

with self.subTest(
"filter both lte and gte to find publication between two dates"
):
with self.subTest("filter on lte date is lesser then publication"):
response = self.client.get(
reverse("api:publication-list"),
{
"registratiedatumVanaf": "2024-09-25T00:00:00-00:00",
"registratiedatumTot": "2024-09-26T00:00:00-00:00",
},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication2.uuid))

def test_list_publications_filter_search(self):
ic = InformationCategoryFactory.create()
publication = PublicationFactory.create(
informatie_categorieen=[ic],
officiele_titel="Een prachtige titel met een heleboel woorden.",
verkorte_titel="prachtige titel.",
)
publication2 = PublicationFactory.create(
informatie_categorieen=[ic],
officiele_titel="Een titel die anders is als de verkorte titel.",
verkorte_titel="waarom is deze titel anders.",
)

with self.subTest("officele titel exacte match"):
response = self.client.get(
reverse("api:publication-list"),
{"search": "Een prachtige titel met een heleboel woorden."},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication.uuid))

with self.subTest("verkorte titel exacte match"):
response = self.client.get(
reverse("api:publication-list"),
{"search": "waarom is deze titel anders."},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication2.uuid))

with self.subTest("officele titel partial match"):
response = self.client.get(
reverse("api:publication-list"),
{"search": "prachtige titel met"},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication.uuid))

with self.subTest("verkorte titel partial match"):
response = self.client.get(
reverse("api:publication-list"),
{"search": "deze titel anders"},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 1)
self.assertEqual(data["results"][0]["uuid"], str(publication2.uuid))

with self.subTest("partial match both objects different fields"):
response = self.client.get(
reverse("api:publication-list"),
{"search": "titel."},
headers=AUDIT_HEADERS,
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(data["count"], 2)
self.assertEqual(data["results"][0]["uuid"], str(publication2.uuid))
self.assertEqual(data["results"][1]["uuid"], str(publication.uuid))

def test_list_publications_filter_owner(self):
ic, ic2 = InformationCategoryFactory.create_batch(2)
with freeze_time("2024-09-24T12:00:00-00:00"):
Expand Down

0 comments on commit dc0acfb

Please sign in to comment.