0005-api-show_past_events-for-agendas-datetimes-endpoint-.patch
chrono/agendas/models.py | ||
---|---|---|
654 | 654 | |
655 | 655 |
if prefetched_queryset: |
656 | 656 |
entries = self.prefetched_events |
657 |
# we may have past events |
|
658 |
entries = [e for e in entries if e.start_datetime >= localtime(now())] |
|
657 | 659 |
else: |
658 | 660 |
# recurring events are never opened |
659 | 661 |
entries = self.event_set.filter(recurrence_days__isnull=True) |
... | ... | |
702 | 704 | |
703 | 705 |
def get_past_events( |
704 | 706 |
self, |
707 |
prefetched_queryset=False, |
|
705 | 708 |
min_start=None, |
706 | 709 |
max_start=None, |
707 | 710 |
user_external_id=None, |
708 | 711 |
): |
709 | 712 |
assert self.kind == 'events' |
710 | 713 | |
711 |
# recurring events are never opened |
|
712 |
entries = self.event_set.filter(recurrence_days__isnull=True) |
|
713 |
# exclude canceled events except for event recurrences |
|
714 |
entries = entries.filter(Q(cancelled=False) | Q(primary_event__isnull=False)) |
|
715 |
# we want only past events |
|
716 |
entries = entries.filter(start_datetime__lt=localtime(now())) |
|
714 |
if prefetched_queryset: |
|
715 |
entries = self.prefetched_events |
|
716 |
# we may have future events |
|
717 |
entries = [e for e in entries if e.start_datetime < localtime(now())] |
|
718 |
else: |
|
719 |
# no recurring events |
|
720 |
entries = self.event_set.filter(recurrence_days__isnull=True) |
|
721 |
# exclude canceled events except for event recurrences |
|
722 |
entries = entries.filter(Q(cancelled=False) | Q(primary_event__isnull=False)) |
|
723 |
# we want only past events |
|
724 |
entries = entries.filter(start_datetime__lt=localtime(now())) |
|
717 | 725 | |
718 |
if min_start: |
|
726 |
if min_start and not prefetched_queryset:
|
|
719 | 727 |
entries = entries.filter(start_datetime__gte=min_start) |
720 | 728 | |
721 |
if max_start: |
|
729 |
if max_start and not prefetched_queryset:
|
|
722 | 730 |
entries = entries.filter(start_datetime__lt=max_start) |
723 | 731 | |
724 | 732 |
if user_external_id: |
... | ... | |
729 | 737 |
entries, |
730 | 738 |
min_start, |
731 | 739 |
min(max_start or localtime(now()), localtime(now())), |
740 |
prefetched_queryset=prefetched_queryset, |
|
732 | 741 |
) |
733 | 742 | |
734 | 743 |
return entries |
... | ... | |
870 | 879 |
] |
871 | 880 | |
872 | 881 |
@staticmethod |
873 |
def prefetch_events_and_exceptions(qs, user_external_id=None): |
|
882 |
def prefetch_events_and_exceptions( |
|
883 |
qs, user_external_id=None, show_past_events=False, min_start=None, max_start=None |
|
884 |
): |
|
874 | 885 |
event_queryset = Event.objects.filter( |
875 | 886 |
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()), |
876 | 887 |
recurrence_days__isnull=True, |
877 | 888 |
cancelled=False, |
878 |
start_datetime__gte=localtime(now()), |
|
879 | 889 |
).order_by() |
880 | 890 | |
881 | 891 |
if user_external_id: |
882 | 892 |
event_queryset = Event.annotate_queryset_for_user(event_queryset, user_external_id) |
893 |
if not show_past_events: |
|
894 |
event_queryset = event_queryset.filter(start_datetime__gte=localtime(now())) |
|
895 |
if min_start: |
|
896 |
event_queryset = event_queryset.filter(start_datetime__gte=min_start) |
|
897 |
if max_start: |
|
898 |
event_queryset = event_queryset.filter(start_datetime__lt=max_start) |
|
883 | 899 | |
884 | 900 |
recurring_event_queryset = Event.objects.filter( |
885 | 901 |
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()), |
chrono/api/serializers.py | ||
---|---|---|
133 | 133 |
agendas = CommaSeparatedStringField( |
134 | 134 |
required=True, child=serializers.SlugField(max_length=160, allow_blank=False) |
135 | 135 |
) |
136 |
show_past_events = serializers.BooleanField(default=False) |
|
136 | 137 | |
137 | 138 | |
138 | 139 |
class EventSerializer(serializers.ModelSerializer): |
chrono/api/views.py | ||
---|---|---|
842 | 842 |
) |
843 | 843 |
payload = serializer.validated_data |
844 | 844 | |
845 |
if 'events' in payload: |
|
846 |
raise APIError( |
|
847 |
_('events parameter is not supported'), |
|
848 |
err_class='events parameter is not supported', |
|
849 |
http_status=status.HTTP_400_BAD_REQUEST, |
|
850 |
) |
|
851 | ||
852 | 845 |
agenda_slugs = payload['agendas'] |
853 | 846 |
agendas = get_objects_from_slugs(agenda_slugs, qs=Agenda.objects.filter(kind='events')) |
854 | 847 | |
855 | 848 |
user_external_id = payload.get('user_external_id') or payload.get('exclude_user_external_id') |
856 | 849 |
disable_booked = bool(payload.get('exclude_user_external_id')) |
857 |
agendas = Agenda.prefetch_events_and_exceptions(agendas, user_external_id=user_external_id) |
|
850 |
show_past_events = bool(payload.get('show_past_events')) |
|
851 |
agendas = Agenda.prefetch_events_and_exceptions( |
|
852 |
agendas, |
|
853 |
user_external_id=user_external_id, |
|
854 |
show_past_events=show_past_events, |
|
855 |
min_start=payload.get('date_start'), |
|
856 |
max_start=payload.get('date_end'), |
|
857 |
) |
|
858 | 858 | |
859 | 859 |
entries = [] |
860 | 860 |
for agenda in agendas: |
861 |
if show_past_events: |
|
862 |
entries.extend( |
|
863 |
agenda.get_past_events( |
|
864 |
prefetched_queryset=True, |
|
865 |
) |
|
866 |
) |
|
861 | 867 |
entries.extend( |
862 | 868 |
agenda.get_open_events( |
863 | 869 |
prefetched_queryset=True, |
864 |
min_start=payload.get('date_start'), |
|
865 |
max_start=payload.get('date_end'), |
|
866 | 870 |
) |
867 | 871 |
) |
868 | 872 |
tests/api/test_datetimes.py | ||
---|---|---|
1462 | 1462 |
resp = app.get('/api/agendas/datetimes/', params={'agendas': 'first-agenda,xxx,yyy'}, status=400) |
1463 | 1463 |
assert resp.json['err_desc'] == 'invalid slugs: xxx, yyy' |
1464 | 1464 | |
1465 |
# no support for past events |
|
1466 |
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'events': 'past'}, status=400) |
|
1465 |
# it's possible to show past events |
|
1466 |
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True}) |
|
1467 |
assert len(resp.json['data']) == 2 |
|
1468 |
assert resp.json['data'][0]['id'] == 'first-agenda@event' |
|
1469 |
assert resp.json['data'][1]['id'] == 'second-agenda@event' |
|
1470 | ||
1471 |
Event.objects.create( |
|
1472 |
slug='event-in-past', |
|
1473 |
start_datetime=now() - datetime.timedelta(days=5), |
|
1474 |
places=5, |
|
1475 |
agenda=first_agenda, |
|
1476 |
) |
|
1477 |
Event.objects.create( # not visible in datetimes api |
|
1478 |
slug='recurring-in-past', |
|
1479 |
start_datetime=now() - datetime.timedelta(days=5), |
|
1480 |
recurrence_days=[localtime().weekday()], |
|
1481 |
recurrence_end_date=now() - datetime.timedelta(days=5), |
|
1482 |
places=5, |
|
1483 |
agenda=first_agenda, |
|
1484 |
) |
|
1485 | ||
1486 |
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True}) |
|
1487 |
assert len(resp.json['data']) == 3 |
|
1488 |
assert resp.json['data'][0]['id'] == 'first-agenda@event-in-past' |
|
1489 |
assert resp.json['data'][0]['disabled'] is True |
|
1490 |
assert resp.json['data'][1]['id'] == 'first-agenda@event' |
|
1491 |
assert resp.json['data'][1]['disabled'] is False |
|
1492 |
assert resp.json['data'][2]['id'] == 'second-agenda@event' |
|
1493 |
assert resp.json['data'][2]['disabled'] is False |
|
1494 | ||
1495 |
date_start = localtime() - datetime.timedelta(days=4) |
|
1496 |
resp = app.get( |
|
1497 |
'/api/agendas/datetimes/', |
|
1498 |
params={'agendas': agenda_slugs, 'date_start': date_start, 'show_past_events': True}, |
|
1499 |
) |
|
1500 |
assert len(resp.json['data']) == 2 |
|
1501 |
assert resp.json['data'][0]['id'] == 'first-agenda@event' |
|
1502 |
assert resp.json['data'][1]['id'] == 'second-agenda@event' |
|
1503 | ||
1504 |
date_end = localtime() + datetime.timedelta(days=5, hours=1) |
|
1505 |
resp = app.get( |
|
1506 |
'/api/agendas/datetimes/', |
|
1507 |
params={'agendas': agenda_slugs, 'date_end': date_end, 'show_past_events': True}, |
|
1508 |
) |
|
1509 |
assert len(resp.json['data']) == 2 |
|
1510 |
assert resp.json['data'][0]['id'] == 'first-agenda@event-in-past' |
|
1511 |
assert resp.json['data'][1]['id'] == 'first-agenda@event' |
|
1467 | 1512 | |
1468 | 1513 | |
1469 | 1514 |
@pytest.mark.freeze_time('2021-05-06 14:00') |
... | ... | |
1474 | 1519 |
second_agenda = Agenda.objects.create(label='Second agenda', kind='events') |
1475 | 1520 |
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder') |
1476 | 1521 |
Event.objects.create(label='09-05', start_datetime=now().replace(day=9), places=5, agenda=second_agenda) |
1522 |
Event.objects.create(label='04-05', start_datetime=now().replace(day=4), places=5, agenda=second_agenda) |
|
1477 | 1523 |
third_agenda = Agenda.objects.create(label='Third agenda', kind='events') |
1478 | 1524 |
Desk.objects.create(agenda=third_agenda, slug='_exceptions_holder') |
1479 | 1525 |
Event.objects.create(label='09-05', start_datetime=now().replace(day=9), places=5, agenda=third_agenda) |
1526 |
Event.objects.create(label='04-05', start_datetime=now().replace(day=4), places=5, agenda=third_agenda) |
|
1480 | 1527 | |
1481 | 1528 |
# check events are ordered by start_datetime and then by agenda order in querystring |
1482 | 1529 |
agenda_slugs = ','.join((first_agenda.slug, third_agenda.slug, second_agenda.slug)) |
... | ... | |
1486 | 1533 |
assert resp.json['data'][1]['id'] == 'second-agenda@09-05' |
1487 | 1534 |
assert resp.json['data'][2]['id'] == 'first-agenda@10-05' |
1488 | 1535 | |
1536 |
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True}) |
|
1537 |
assert len(resp.json['data']) == 5 |
|
1538 |
assert resp.json['data'][0]['id'] == 'third-agenda@04-05' |
|
1539 |
assert resp.json['data'][1]['id'] == 'second-agenda@04-05' |
|
1540 |
assert resp.json['data'][2]['id'] == 'third-agenda@09-05' |
|
1541 |
assert resp.json['data'][3]['id'] == 'second-agenda@09-05' |
|
1542 |
assert resp.json['data'][4]['id'] == 'first-agenda@10-05' |
|
1543 | ||
1489 | 1544 | |
1490 | 1545 |
@pytest.mark.freeze_time('2021-05-06 14:00') |
1491 | 1546 |
def test_datetimes_multiple_agendas_queries(app): |
1492 | 1547 |
for i in range(10): |
1493 | 1548 |
agenda = Agenda.objects.create(label=str(i), kind='events') |
1494 | 1549 |
Desk.objects.create(agenda=agenda, slug='_exceptions_holder') |
1550 |
Event.objects.create(start_datetime=now() - datetime.timedelta(days=5), places=5, agenda=agenda) |
|
1495 | 1551 |
Event.objects.create(start_datetime=now() + datetime.timedelta(days=5), places=5, agenda=agenda) |
1496 | 1552 |
Event.objects.create(start_datetime=now() + datetime.timedelta(days=5), places=5, agenda=agenda) |
1497 | 1553 | |
1498 | 1554 |
with CaptureQueriesContext(connection) as ctx: |
1499 |
resp = app.get('/api/agendas/datetimes/', params={'agendas': ','.join(str(i) for i in range(10))}) |
|
1500 |
assert len(resp.json['data']) == 20 |
|
1555 |
resp = app.get( |
|
1556 |
'/api/agendas/datetimes/', |
|
1557 |
params={'agendas': ','.join(str(i) for i in range(10)), 'show_past_events': True}, |
|
1558 |
) |
|
1559 |
assert len(resp.json['data']) == 30 |
|
1501 | 1560 |
assert len(ctx.captured_queries) == 7 |
1502 |
- |