api: show_past_events for agendas/datetimes endpoint (#56615)

This commit is contained in:
Lauréline Guérin 2021-10-04 18:04:26 +02:00
parent 23dd874fdc
commit f07d2bb8b2
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
4 changed files with 104 additions and 24 deletions

View File

@ -654,6 +654,8 @@ class Agenda(models.Model):
if prefetched_queryset:
entries = self.prefetched_events
# we may have past events
entries = [e for e in entries if e.start_datetime >= localtime(now())]
else:
# recurring events are never opened
entries = self.event_set.filter(recurrence_days__isnull=True)
@ -702,23 +704,29 @@ class Agenda(models.Model):
def get_past_events(
self,
prefetched_queryset=False,
min_start=None,
max_start=None,
user_external_id=None,
):
assert self.kind == 'events'
# recurring events are never opened
entries = self.event_set.filter(recurrence_days__isnull=True)
# exclude canceled events except for event recurrences
entries = entries.filter(Q(cancelled=False) | Q(primary_event__isnull=False))
# we want only past events
entries = entries.filter(start_datetime__lt=localtime(now()))
if prefetched_queryset:
entries = self.prefetched_events
# we may have future events
entries = [e for e in entries if e.start_datetime < localtime(now())]
else:
# no recurring events
entries = self.event_set.filter(recurrence_days__isnull=True)
# exclude canceled events except for event recurrences
entries = entries.filter(Q(cancelled=False) | Q(primary_event__isnull=False))
# we want only past events
entries = entries.filter(start_datetime__lt=localtime(now()))
if min_start:
if min_start and not prefetched_queryset:
entries = entries.filter(start_datetime__gte=min_start)
if max_start:
if max_start and not prefetched_queryset:
entries = entries.filter(start_datetime__lt=max_start)
if user_external_id:
@ -729,6 +737,7 @@ class Agenda(models.Model):
entries,
min_start,
min(max_start or localtime(now()), localtime(now())),
prefetched_queryset=prefetched_queryset,
)
return entries
@ -870,16 +879,23 @@ class Agenda(models.Model):
]
@staticmethod
def prefetch_events_and_exceptions(qs, user_external_id=None):
def prefetch_events_and_exceptions(
qs, user_external_id=None, show_past_events=False, min_start=None, max_start=None
):
event_queryset = Event.objects.filter(
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),
recurrence_days__isnull=True,
cancelled=False,
start_datetime__gte=localtime(now()),
).order_by()
if user_external_id:
event_queryset = Event.annotate_queryset_for_user(event_queryset, user_external_id)
if not show_past_events:
event_queryset = event_queryset.filter(start_datetime__gte=localtime(now()))
if min_start:
event_queryset = event_queryset.filter(start_datetime__gte=min_start)
if max_start:
event_queryset = event_queryset.filter(start_datetime__lt=max_start)
recurring_event_queryset = Event.objects.filter(
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),

View File

@ -133,6 +133,7 @@ class MultipleAgendasDatetimesSerializer(DatetimesSerializer):
agendas = CommaSeparatedStringField(
required=True, child=serializers.SlugField(max_length=160, allow_blank=False)
)
show_past_events = serializers.BooleanField(default=False)
class EventSerializer(serializers.ModelSerializer):

View File

@ -842,27 +842,31 @@ class MultipleAgendasDatetimes(APIView):
)
payload = serializer.validated_data
if 'events' in payload:
raise APIError(
_('events parameter is not supported'),
err_class='events parameter is not supported',
http_status=status.HTTP_400_BAD_REQUEST,
)
agenda_slugs = payload['agendas']
agendas = get_objects_from_slugs(agenda_slugs, qs=Agenda.objects.filter(kind='events'))
user_external_id = payload.get('user_external_id') or payload.get('exclude_user_external_id')
disable_booked = bool(payload.get('exclude_user_external_id'))
agendas = Agenda.prefetch_events_and_exceptions(agendas, user_external_id=user_external_id)
show_past_events = bool(payload.get('show_past_events'))
agendas = Agenda.prefetch_events_and_exceptions(
agendas,
user_external_id=user_external_id,
show_past_events=show_past_events,
min_start=payload.get('date_start'),
max_start=payload.get('date_end'),
)
entries = []
for agenda in agendas:
if show_past_events:
entries.extend(
agenda.get_past_events(
prefetched_queryset=True,
)
)
entries.extend(
agenda.get_open_events(
prefetched_queryset=True,
min_start=payload.get('date_start'),
max_start=payload.get('date_end'),
)
)

View File

@ -1462,8 +1462,53 @@ def test_datetimes_multiple_agendas(app):
resp = app.get('/api/agendas/datetimes/', params={'agendas': 'first-agenda,xxx,yyy'}, status=400)
assert resp.json['err_desc'] == 'invalid slugs: xxx, yyy'
# no support for past events
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'events': 'past'}, status=400)
# it's possible to show past events
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True})
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['id'] == 'first-agenda@event'
assert resp.json['data'][1]['id'] == 'second-agenda@event'
Event.objects.create(
slug='event-in-past',
start_datetime=now() - datetime.timedelta(days=5),
places=5,
agenda=first_agenda,
)
Event.objects.create( # not visible in datetimes api
slug='recurring-in-past',
start_datetime=now() - datetime.timedelta(days=5),
recurrence_days=[localtime().weekday()],
recurrence_end_date=now() - datetime.timedelta(days=5),
places=5,
agenda=first_agenda,
)
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True})
assert len(resp.json['data']) == 3
assert resp.json['data'][0]['id'] == 'first-agenda@event-in-past'
assert resp.json['data'][0]['disabled'] is True
assert resp.json['data'][1]['id'] == 'first-agenda@event'
assert resp.json['data'][1]['disabled'] is False
assert resp.json['data'][2]['id'] == 'second-agenda@event'
assert resp.json['data'][2]['disabled'] is False
date_start = localtime() - datetime.timedelta(days=4)
resp = app.get(
'/api/agendas/datetimes/',
params={'agendas': agenda_slugs, 'date_start': date_start, 'show_past_events': True},
)
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['id'] == 'first-agenda@event'
assert resp.json['data'][1]['id'] == 'second-agenda@event'
date_end = localtime() + datetime.timedelta(days=5, hours=1)
resp = app.get(
'/api/agendas/datetimes/',
params={'agendas': agenda_slugs, 'date_end': date_end, 'show_past_events': True},
)
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['id'] == 'first-agenda@event-in-past'
assert resp.json['data'][1]['id'] == 'first-agenda@event'
@pytest.mark.freeze_time('2021-05-06 14:00')
@ -1474,9 +1519,11 @@ def test_datetimes_multiple_agendas_sort(app):
second_agenda = Agenda.objects.create(label='Second agenda', kind='events')
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
Event.objects.create(label='09-05', start_datetime=now().replace(day=9), places=5, agenda=second_agenda)
Event.objects.create(label='04-05', start_datetime=now().replace(day=4), places=5, agenda=second_agenda)
third_agenda = Agenda.objects.create(label='Third agenda', kind='events')
Desk.objects.create(agenda=third_agenda, slug='_exceptions_holder')
Event.objects.create(label='09-05', start_datetime=now().replace(day=9), places=5, agenda=third_agenda)
Event.objects.create(label='04-05', start_datetime=now().replace(day=4), places=5, agenda=third_agenda)
# check events are ordered by start_datetime and then by agenda order in querystring
agenda_slugs = ','.join((first_agenda.slug, third_agenda.slug, second_agenda.slug))
@ -1486,16 +1533,28 @@ def test_datetimes_multiple_agendas_sort(app):
assert resp.json['data'][1]['id'] == 'second-agenda@09-05'
assert resp.json['data'][2]['id'] == 'first-agenda@10-05'
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True})
assert len(resp.json['data']) == 5
assert resp.json['data'][0]['id'] == 'third-agenda@04-05'
assert resp.json['data'][1]['id'] == 'second-agenda@04-05'
assert resp.json['data'][2]['id'] == 'third-agenda@09-05'
assert resp.json['data'][3]['id'] == 'second-agenda@09-05'
assert resp.json['data'][4]['id'] == 'first-agenda@10-05'
@pytest.mark.freeze_time('2021-05-06 14:00')
def test_datetimes_multiple_agendas_queries(app):
for i in range(10):
agenda = Agenda.objects.create(label=str(i), kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
Event.objects.create(start_datetime=now() - datetime.timedelta(days=5), places=5, agenda=agenda)
Event.objects.create(start_datetime=now() + datetime.timedelta(days=5), places=5, agenda=agenda)
Event.objects.create(start_datetime=now() + datetime.timedelta(days=5), places=5, agenda=agenda)
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/api/agendas/datetimes/', params={'agendas': ','.join(str(i) for i in range(10))})
assert len(resp.json['data']) == 20
resp = app.get(
'/api/agendas/datetimes/',
params={'agendas': ','.join(str(i) for i in range(10)), 'show_past_events': True},
)
assert len(resp.json['data']) == 30
assert len(ctx.captured_queries) == 7