Projet

Général

Profil

0005-api-show_past_events-for-agendas-datetimes-endpoint-.patch

Lauréline Guérin, 04 octobre 2021 18:05

Télécharger (11,6 ko)

Voir les différences:

Subject: [PATCH 5/5] api: show_past_events for agendas/datetimes endpoint
 (#56615)

 chrono/agendas/models.py    | 36 ++++++++++++++------
 chrono/api/serializers.py   |  1 +
 chrono/api/views.py         | 24 +++++++------
 tests/api/test_datetimes.py | 67 ++++++++++++++++++++++++++++++++++---
 4 files changed, 104 insertions(+), 24 deletions(-)
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
-