Projet

Général

Profil

0002-api-prefetch-events-in-multiple-agendas-datetimes-55.patch

Valentin Deniaud, 11 août 2021 10:23

Télécharger (8,11 ko)

Voir les différences:

Subject: [PATCH 2/2] api: prefetch events in multiple agendas datetimes
 (#55370)

 chrono/agendas/models.py    | 58 ++++++++++++++++++++++++++++++++++-
 chrono/api/views.py         | 61 +++++++------------------------------
 tests/api/test_datetimes.py |  2 +-
 3 files changed, 69 insertions(+), 52 deletions(-)
chrono/agendas/models.py
32 32
from django.core.exceptions import FieldDoesNotExist, ValidationError
33 33
from django.core.validators import MaxValueValidator, MinValueValidator
34 34
from django.db import IntegrityError, connection, models, transaction
35
from django.db.models import Count, IntegerField, Max, OuterRef, Q, Subquery, Value
35
from django.db.models import Count, IntegerField, Max, OuterRef, Prefetch, Q, Subquery, Value
36 36
from django.db.models.functions import Coalesce
37 37
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist, engines
38 38
from django.urls import reverse
......
877 877
                if e.desk_id == desk.pk or e.unavailability_calendar_id in uc_ids
878 878
            ]
879 879

  
880
    @staticmethod
881
    def prefetch_events_and_exceptions(qs, annotate_events=False, user_external_id=None):
882
        event_queryset = Event.objects.filter(
883
            Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),
884
            cancelled=False,
885
            start_datetime__gte=localtime(now()),
886
        ).order_by()
887

  
888
        if user_external_id:
889
            event_queryset = Event.annotate_queryset_for_user(event_queryset, user_external_id)
890
        if annotate_events:
891
            event_queryset = Event.annotate_queryset(event_queryset)
892

  
893
        recurring_event_queryset = Event.objects.filter(
894
            Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),
895
            recurrence_days__isnull=False,
896
        )
897
        exceptions_desk = Desk.objects.filter(slug='_exceptions_holder').prefetch_related(
898
            'unavailability_calendars'
899
        )
900
        qs = qs.filter(kind='events').prefetch_related(
901
            Prefetch(
902
                'event_set',
903
                queryset=event_queryset,
904
                to_attr='prefetched_events',
905
            ),
906
            Prefetch(
907
                'event_set',
908
                queryset=recurring_event_queryset,
909
                to_attr='prefetched_recurring_events',
910
            ),
911
            Prefetch(
912
                'desk_set',
913
                queryset=exceptions_desk,
914
                to_attr='prefetched_desks',
915
            ),
916
        )
917
        agendas_exceptions = TimePeriodException.objects.filter(
918
            Q(desk__slug='_exceptions_holder', desk__agenda__in=qs)
919
            | Q(
920
                unavailability_calendar__desks__slug='_exceptions_holder',
921
                unavailability_calendar__desks__agenda__in=qs,
922
            ),
923
            start_datetime__gte=localtime(now()),
924
        )
925
        agendas = list(qs)
926
        for agenda in agendas:
927
            desk = agenda.prefetched_desks[0]
928
            uc_ids = [uc.pk for uc in desk.unavailability_calendars.all()]
929
            agenda.prefetched_exceptions = [
930
                e
931
                for e in agendas_exceptions
932
                if e.desk_id == desk.pk or e.unavailability_calendar_id in uc_ids
933
            ]
934
        return agendas
935

  
880 936
    def is_available_for_simple_management(self):
881 937
        if self.kind != 'meetings':
882 938
            return False
chrono/api/views.py
681 681
        with_open_events = request.GET.get('with_open_events') in ['1', 'true']
682 682
        if with_open_events:
683 683
            # return only events agenda
684
            event_queryset = Event.objects.filter(
685
                Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),
686
                cancelled=False,
687
                start_datetime__gte=localtime(now()),
688
            ).order_by()
689
            recurring_event_queryset = Event.objects.filter(
690
                Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),
691
                recurrence_days__isnull=False,
692
            )
693
            exceptions_desk = Desk.objects.filter(slug='_exceptions_holder').prefetch_related(
694
                'unavailability_calendars'
695
            )
696
            agendas_queryset = agendas_queryset.filter(kind='events').prefetch_related(
697
                Prefetch(
698
                    'event_set',
699
                    queryset=event_queryset,
700
                    to_attr='prefetched_events',
701
                ),
702
                Prefetch(
703
                    'event_set',
704
                    queryset=recurring_event_queryset,
705
                    to_attr='prefetched_recurring_events',
706
                ),
707
                Prefetch(
708
                    'desk_set',
709
                    queryset=exceptions_desk,
710
                    to_attr='prefetched_desks',
711
                ),
712
            )
713
            agendas_exceptions = TimePeriodException.objects.filter(
714
                Q(desk__slug='_exceptions_holder', desk__agenda__in=agendas_queryset)
715
                | Q(
716
                    unavailability_calendar__desks__slug='_exceptions_holder',
717
                    unavailability_calendar__desks__agenda__in=agendas_queryset,
718
                ),
719
                start_datetime__gte=localtime(now()),
720
            )
684
            agendas_queryset = Agenda.prefetch_events_and_exceptions(agendas_queryset)
721 685

  
722 686
        agendas = []
723 687
        for agenda in agendas_queryset:
724
            if with_open_events:
725
                desk = agenda.prefetched_desks[0]
726
                uc_ids = [uc.pk for uc in desk.unavailability_calendars.all()]
727
                agenda.prefetched_exceptions = [
728
                    e
729
                    for e in agendas_exceptions
730
                    if e.desk_id == desk.pk or e.unavailability_calendar_id in uc_ids
731
                ]
732
                if not any(not e.full for e in agenda.get_open_events(prefetched_queryset=True)):
733
                    # exclude agendas without open events
734
                    continue
688
            if with_open_events and not any(
689
                not e.full for e in agenda.get_open_events(prefetched_queryset=True)
690
            ):
691
                # exclude agendas without open events
692
                continue
735 693
            agendas.append(get_agenda_detail(request, agenda))
736 694

  
737 695
        return Response({'data': agendas})
......
861 819
            )
862 820

  
863 821
        user_external_id = payload.get('user_external_id') or payload.get('exclude_user_external_id')
822
        agendas = Agenda.prefetch_events_and_exceptions(
823
            agendas, annotate_events=True, user_external_id=user_external_id
824
        )
825

  
864 826
        entries = []
865 827
        for agenda in agendas:
866 828
            entries.extend(
867 829
                agenda.get_open_events(
868
                    annotate_queryset=True,
830
                    prefetched_queryset=True,
869 831
                    min_start=payload.get('date_start'),
870 832
                    max_start=payload.get('date_end'),
871
                    user_external_id=user_external_id,
872 833
                )
873 834
            )
874 835

  
tests/api/test_datetimes.py
1478 1478
    with CaptureQueriesContext(connection) as ctx:
1479 1479
        resp = app.get('/api/agenda/datetimes/', params={'agendas': ','.join(str(i) for i in range(10))})
1480 1480
        assert len(resp.json['data']) == 20
1481
        assert len(ctx.captured_queries) == 21
1481
        assert len(ctx.captured_queries) == 7
1482
-