From 635bb94cc42d99b7a237656d236e4204f57614a6 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 9 Jun 2023 15:20:53 +0200 Subject: [PATCH] API: Add date range filters for events and subevents --- doc/api/resources/events.rst | 9 +++++++++ doc/api/resources/subevents.rst | 13 +++++++++++++ src/pretix/api/views/event.py | 4 ++++ src/tests/api/test_events.py | 8 ++++++++ src/tests/api/test_subevents.py | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+) diff --git a/doc/api/resources/events.rst b/doc/api/resources/events.rst index 8f8cf4d94..c3edba0c9 100644 --- a/doc/api/resources/events.rst +++ b/doc/api/resources/events.rst @@ -70,6 +70,11 @@ Endpoints The ``public_url`` field has been added. +.. versionchanged:: 5.0 + + The ``date_from_before``, ``date_from_after``, ``date_to_before``, and ``date_to_after`` query parameters have been + added. + .. http:get:: /api/v1/organizers/(organizer)/events/ Returns a list of all events within a given organizer the authenticated user/token has access to. @@ -141,6 +146,10 @@ Endpoints :query has_subevents: If set to ``true``/``false``, only events with a matching value of ``has_subevents`` are returned. :query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned. Event series are never (always) returned. :query is_past: If set to ``true`` (``false``), only events that are over are (not) returned. Event series are never (always) returned. + :query date_from_after: If set to a date and time, only events that start at or after the given time are returned. + :query date_from_before: If set to a date and time, only events that start at or before the given time are returned. + :query date_to_after: If set to a date and time, only events that have an end date and end at or after the given time are returned. + :query date_to_before: If set to a date and time, only events that have an end date and end at or before the given time are returned. :query ends_after: If set to a date and time, only events that happen during of after the given time are returned. Event series are never returned. :query string ordering: Manually set the ordering of results. Valid fields to be used are ``date_from`` and ``slug``. Keep in mind that ``date_from`` of event series does not really tell you anything. diff --git a/doc/api/resources/subevents.rst b/doc/api/resources/subevents.rst index 03d2d51a1..89c24cee0 100644 --- a/doc/api/resources/subevents.rst +++ b/doc/api/resources/subevents.rst @@ -63,6 +63,11 @@ last_modified datetime Last modificati The ``search`` query parameter has been added to filter sub-events by their name or location in any language. +.. versionchanged:: 5.0 + + The ``date_from_before``, ``date_from_after``, ``date_to_before``, and ``date_to_after`` query parameters have been + added. + Endpoints --------- @@ -130,6 +135,10 @@ Endpoints :query active: If set to ``true``/``false``, only events with a matching value of ``active`` are returned. :query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned. :query is_past: If set to ``true`` (``false``), only events that are over are (not) returned. + :query date_from_after: If set to a date and time, only events that start at or after the given time are returned. + :query date_from_before: If set to a date and time, only events that start at or before the given time are returned. + :query date_to_after: If set to a date and time, only events that have an end date and end at or after the given time are returned. + :query date_to_before: If set to a date and time, only events that have an end date and end at or before the given time are returned. :query ends_after: If set to a date and time, only events that happen during of after the given time are returned. :query search: Only return events matching a given search query. :param organizer: The ``slug`` field of a valid organizer @@ -458,6 +467,10 @@ Endpoints :query event__live: If set to ``true``/``false``, only events with a matching value of ``live`` on the parent event are returned. :query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned. :query is_past: If set to ``true`` (``false``), only events that are over are (not) returned. + :query date_from_after: If set to a date and time, only events that start at or after the given time are returned. + :query date_from_before: If set to a date and time, only events that start at or before the given time are returned. + :query date_to_after: If set to a date and time, only events that have an end date and end at or after the given time are returned. + :query date_to_before: If set to a date and time, only events that have an end date and end at or before the given time are returned. :query ends_after: If set to a date and time, only events that happen during of after the given time are returned. :query sales_channel: If set to a sales channel identifier, the response will only contain subevents from events available on this sales channel. :param organizer: The ``slug`` field of a valid organizer diff --git a/src/pretix/api/views/event.py b/src/pretix/api/views/event.py index f60b2b1c9..59ac871dc 100644 --- a/src/pretix/api/views/event.py +++ b/src/pretix/api/views/event.py @@ -71,6 +71,8 @@ with scopes_disabled(): ends_after = django_filters.rest_framework.IsoDateTimeFilter(method='ends_after_qs') sales_channel = django_filters.rest_framework.CharFilter(method='sales_channel_qs') search = django_filters.rest_framework.CharFilter(method='search_qs') + date_from = django_filters.rest_framework.IsoDateTimeFromToRangeFilter() + date_to = django_filters.rest_framework.IsoDateTimeFromToRangeFilter() class Meta: model = Event @@ -336,6 +338,8 @@ with scopes_disabled(): modified_since = django_filters.IsoDateTimeFilter(field_name='last_modified', lookup_expr='gte') sales_channel = django_filters.rest_framework.CharFilter(method='sales_channel_qs') search = django_filters.rest_framework.CharFilter(method='search_qs') + date_from = django_filters.rest_framework.IsoDateTimeFromToRangeFilter() + date_to = django_filters.rest_framework.IsoDateTimeFromToRangeFilter() class Meta: model = SubEvent diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index 71da2caa7..d2bf42177 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -200,6 +200,14 @@ def test_event_list_filter(token_client, organizer, event): assert resp.status_code == 200 assert resp.data['count'] == 0 + resp = token_client.get('/api/v1/organizers/{}/events/?date_from_after=2017-12-27T10:00:00Z'.format(organizer.slug)) + assert resp.status_code == 200 + assert resp.data['count'] == 1 + + resp = token_client.get('/api/v1/organizers/{}/events/?date_from_after=2017-12-27T10:00:01Z'.format(organizer.slug)) + assert resp.status_code == 200 + assert resp.data['count'] == 0 + @pytest.mark.django_db def test_event_list_name_filter(token_client, organizer, event): diff --git a/src/tests/api/test_subevents.py b/src/tests/api/test_subevents.py index f6258c21d..e46e58ea6 100644 --- a/src/tests/api/test_subevents.py +++ b/src/tests/api/test_subevents.py @@ -174,6 +174,38 @@ def test_subevent_list_filter(token_client, organizer, event, subevent): assert resp.status_code == 200 assert resp.data['count'] == 0 + resp = token_client.get( + '/api/v1/organizers/{}/events/{}/subevents/?date_from_after=2017-12-27T10:00:00Z'.format( + organizer.slug, event.slug + ) + ) + assert resp.status_code == 200 + assert resp.data['count'] == 1 + + resp = token_client.get( + '/api/v1/organizers/{}/events/{}/subevents/?date_from_after=2017-12-27T10:00:01Z'.format( + organizer.slug, event.slug + ) + ) + assert resp.status_code == 200 + assert resp.data['count'] == 0 + + resp = token_client.get( + '/api/v1/organizers/{}/events/{}/subevents/?date_from_before=2017-12-27T10:00:00Z'.format( + organizer.slug, event.slug + ) + ) + assert resp.status_code == 200 + assert resp.data['count'] == 1 + + resp = token_client.get( + '/api/v1/organizers/{}/events/{}/subevents/?date_from_before=2017-12-27T09:59:00Z'.format( + organizer.slug, event.slug + ) + ) + assert resp.status_code == 200 + assert resp.data['count'] == 0 + @pytest.mark.django_db def test_subevent_create(team, token_client, organizer, event, subevent, meta_prop, item):