api: make recurring events list endpoint work with multiple agendas (#57957)

This commit is contained in:
Valentin Deniaud 2021-10-21 11:40:55 +02:00
parent 84f5dbc4c8
commit bb781f8c83
3 changed files with 105 additions and 34 deletions

View File

@ -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<agenda_identifier>[\w-]+)/recurring-events/$',
views.recurring_events_list,
name='api-agenda-recurring-events',
),
url(
r'^agenda/(?P<agenda_identifier>[\w-]+)/recurring-events/fillslots/$',
views.recurring_fillslots,

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import collections
import copy
import datetime
import itertools
import uuid
@ -1073,28 +1074,38 @@ 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(
for agenda in agendas:
for event in agenda.get_open_recurring_events():
for day in event.recurrence_days:
event = copy.copy(event)
event.day = day
events.append(event)
agenda_querystring_indexes = {agenda_slug: i for i, agenda_slug in enumerate(agenda_slugs)}
events.sort(key=lambda event: (event.day, agenda_querystring_indexes[event.agenda.slug]))
return Response(
{
'data': [
{
'id': slug,
'text': get_event_text(event, agenda, day),
'id': '%s@%s:%s' % (event.agenda.slug, event.slug, event.day),
'text': get_event_text(event, event.agenda, event.day),
'label': event.label or '',
'day': WEEKDAYS[day].capitalize(),
'day': WEEKDAYS[event.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})
for event in events
]
}
)
recurring_events_list = RecurringEventsList.as_view()

View File

@ -1337,6 +1337,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',
@ -1346,10 +1347,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)
@ -1364,35 +1365,98 @@ 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]['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]['text'] == 'Friday: Example Event'
assert resp.json['data'][1]['id'] == 'foo-bar@other:1'
assert resp.json['data'][1]['text'] == 'Tuesday: Other'
assert resp.json['data'][1]['label'] == 'Other'
assert resp.json['data'][1]['day'] == 'Tuesday'
assert resp.json['data'][2]['id'] == 'foo-bar@example-event:3'
assert resp.json['data'][2]['text'] == 'Thursday: 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]['text'] == 'Tuesday: Other'
assert resp.json['data'][3]['label'] == 'Other'
assert resp.json['data'][3]['day'] == 'Tuesday'
assert resp.json['data'][2]['day'] == 'Thursday'
assert resp.json['data'][3]['id'] == 'foo-bar@example-event:4'
assert resp.json['data'][3]['text'] == 'Friday: Example Event'
assert resp.json['data'][3]['label'] == 'Example Event'
assert resp.json['data'][3]['day'] == 'Friday'
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')
event_ids = [x['id'] for x in resp.json['data']]
assert event_ids == [
'first-agenda@a:0',
'first-agenda@b:1',
'first-agenda@a:2',
'second-agenda@c:2',
'second-agenda@c:3',
'first-agenda@a:5',
]
assert event_ids.index('first-agenda@a:2') < event_ids.index('second-agenda@c:2')
# sorting depends on querystring order
resp = app.get('/api/agendas/recurring-events/?agendas=second-agenda,first-agenda')
event_ids = [x['id'] for x in resp.json['data']]
assert event_ids.index('first-agenda@a:2') > event_ids.index('second-agenda@c:2')
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(