From dd7fcc42562e723e6ab2b71aa313aa951c93b6e8 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 21 Oct 2021 11:40:55 +0200 Subject: [PATCH 2/5] api: make recurring events list endpoint work with multiple agendas (#57957) --- chrono/api/urls.py | 6 +-- chrono/api/views.py | 38 +++++++++---------- tests/api/test_datetimes.py | 75 ++++++++++++++++++++++++++++++++----- 3 files changed, 86 insertions(+), 33 deletions(-) diff --git a/chrono/api/urls.py b/chrono/api/urls.py index 658d3ab2..a3cd495b 100644 --- a/chrono/api/urls.py +++ b/chrono/api/urls.py @@ -21,6 +21,7 @@ from . import views urlpatterns = [ url(r'^agenda/$', views.agendas), url(r'^agendas/datetimes/$', views.agendas_datetimes, name='api-agendas-datetimes'), + url(r'^agendas/recurring-events/$', views.recurring_events_list, name='api-agenda-recurring-events'), url( r'^agendas/events/fillslots/$', views.agendas_events_fillslots, @@ -39,11 +40,6 @@ urlpatterns = [ views.events_fillslots, name='api-agenda-events-fillslots', ), - url( - r'^agenda/(?P[\w-]+)/recurring-events/$', - views.recurring_events_list, - name='api-agenda-recurring-events', - ), url( r'^agenda/(?P[\w-]+)/recurring-events/fillslots/$', views.recurring_fillslots, diff --git a/chrono/api/views.py b/chrono/api/views.py index a585126c..27fa9d0d 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -1066,27 +1066,27 @@ class RecurringEventsList(APIView): if not settings.ENABLE_RECURRING_EVENT_BOOKING: raise Http404() - agenda = get_object_or_404(Agenda, slug=agenda_identifier, kind='events') - entries = agenda.get_open_recurring_events() + agenda_slugs = get_agendas_from_request(request) + agendas = get_objects_from_slugs(agenda_slugs, qs=Agenda.objects.filter(kind='events')) events = [] - for event in entries: - for day in event.recurrence_days: - slug = '%s:%s' % (event.slug, day) - events.append( - { - 'id': slug, - 'text': get_event_text(event, agenda, day), - 'label': event.label or '', - 'day': WEEKDAYS[day].capitalize(), - 'date': format_response_date(event.start_datetime), - 'datetime': format_response_datetime(event.start_datetime), - 'description': event.description, - 'pricing': event.pricing, - 'url': event.url, - } - ) - + for agenda in agendas: + for event in agenda.get_open_recurring_events(): + for day in event.recurrence_days: + slug = '%s@%s:%s' % (agenda.slug, event.slug, day) + events.append( + { + 'id': slug, + 'text': get_event_text(event, agenda, day), + 'label': event.label or '', + 'day': WEEKDAYS[day].capitalize(), + 'date': format_response_date(event.start_datetime), + 'datetime': format_response_datetime(event.start_datetime), + 'description': event.description, + 'pricing': event.pricing, + 'url': event.url, + } + ) return Response({'data': events}) diff --git a/tests/api/test_datetimes.py b/tests/api/test_datetimes.py index bc3f2dda..c06279d1 100644 --- a/tests/api/test_datetimes.py +++ b/tests/api/test_datetimes.py @@ -1318,6 +1318,7 @@ def test_past_datetimes_recurring_event(app, user): def test_recurring_events_api_list(app, freezer): freezer.move_to('2021-09-06 12:00') agenda = Agenda.objects.create(label='Foo bar', kind='events') + Desk.objects.create(agenda=agenda, slug='_exceptions_holder') Event.objects.create(label='Normal event', start_datetime=now(), places=5, agenda=agenda) event = Event.objects.create( label='Example Event', @@ -1327,10 +1328,10 @@ def test_recurring_events_api_list(app, freezer): agenda=agenda, ) - resp = app.get('/api/agenda/xxx/recurring-events/', status=404) + resp = app.get('/api/agendas/recurring-events/', status=400) # recurring events without recurrence_end_date are not bookable - resp = app.get('/api/agenda/%s/recurring-events/' % agenda.slug) + resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug) assert len(resp.json['data']) == 0 event.recurrence_end_date = now() + datetime.timedelta(days=30) @@ -1345,35 +1346,91 @@ def test_recurring_events_api_list(app, freezer): recurrence_end_date=now() + datetime.timedelta(days=45), ) - resp = app.get('/api/agenda/%s/recurring-events/' % agenda.slug) + resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug) assert len(resp.json['data']) == 4 - assert resp.json['data'][0]['id'] == 'example-event:0' + assert resp.json['data'][0]['id'] == 'foo-bar@example-event:0' assert resp.json['data'][0]['text'] == 'Monday: Example Event' assert resp.json['data'][0]['label'] == 'Example Event' assert resp.json['data'][0]['day'] == 'Monday' - assert resp.json['data'][1]['id'] == 'example-event:3' + assert resp.json['data'][1]['id'] == 'foo-bar@example-event:3' assert resp.json['data'][1]['text'] == 'Thursday: Example Event' assert resp.json['data'][1]['label'] == 'Example Event' assert resp.json['data'][1]['day'] == 'Thursday' - assert resp.json['data'][2]['id'] == 'example-event:4' + assert resp.json['data'][2]['id'] == 'foo-bar@example-event:4' assert resp.json['data'][2]['text'] == 'Friday: Example Event' assert resp.json['data'][2]['label'] == 'Example Event' assert resp.json['data'][2]['day'] == 'Friday' - assert resp.json['data'][3]['id'] == 'other:1' + assert resp.json['data'][3]['id'] == 'foo-bar@other:1' assert resp.json['data'][3]['text'] == 'Tuesday: Other' assert resp.json['data'][3]['label'] == 'Other' assert resp.json['data'][3]['day'] == 'Tuesday' event.publication_datetime = now() + datetime.timedelta(days=2) event.save() - resp = app.get('/api/agenda/%s/recurring-events/' % agenda.slug) + resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug) assert len(resp.json['data']) == 1 freezer.move_to(event.recurrence_end_date) - resp = app.get('/api/agenda/%s/recurring-events/' % agenda.slug) + resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug) assert len(resp.json['data']) == 1 +@pytest.mark.freeze_time('2021-09-06 12:00') +def test_recurring_events_api_list_multiple_agendas(app): + agenda = Agenda.objects.create(label='First Agenda', kind='events') + Desk.objects.create(agenda=agenda, slug='_exceptions_holder') + start, end = now(), now() + datetime.timedelta(days=30) + Event.objects.create( + label='A', + start_datetime=start, + places=2, + recurrence_end_date=end, + recurrence_days=[0, 2, 5], + agenda=agenda, + ) + Event.objects.create( + label='B', start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1], agenda=agenda + ) + agenda2 = Agenda.objects.create(label='Second Agenda', kind='events') + Desk.objects.create(agenda=agenda2, slug='_exceptions_holder') + Event.objects.create( + label='C', + start_datetime=start, + places=2, + recurrence_end_date=end, + recurrence_days=[2, 3], + agenda=agenda2, + ) + + resp = app.get('/api/agendas/recurring-events/?agendas=first-agenda,second-agenda') + assert [x['id'] for x in resp.json['data']] == [ + 'first-agenda@a:0', + 'first-agenda@a:2', + 'first-agenda@a:5', + 'first-agenda@b:1', + 'second-agenda@c:2', + 'second-agenda@c:3', + ] + + resp = app.get('/api/agendas/recurring-events/?agendas=second-agenda') + assert [x['id'] for x in resp.json['data']] == ['second-agenda@c:2', 'second-agenda@c:3'] + + +@pytest.mark.freeze_time('2021-09-06 12:00') +def test_recurring_events_api_list_multiple_agendas_queries(app): + for i in range(20): + agenda = Agenda.objects.create(slug=f'{i}', kind='events') + Desk.objects.create(agenda=agenda, slug='_exceptions_holder') + start, end = now(), now() + datetime.timedelta(days=30) + Event.objects.create( + start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1, 2], agenda=agenda + ) + with CaptureQueriesContext(connection) as ctx: + resp = app.get('/api/agendas/recurring-events/?agendas=%s' % ','.join(str(i) for i in range(20))) + assert len(resp.json['data']) == 40 + assert len(ctx.captured_queries) == 23 + + @pytest.mark.freeze_time('2021-05-06 14:00') def test_datetimes_multiple_agendas(app): first_agenda = Agenda.objects.create(label='First agenda', kind='events') -- 2.30.2