From 6aa49605cc845eaeff6718538f22898aaecccaec Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 4 Feb 2021 15:10:29 +0100 Subject: [PATCH] api: prefetch exceptions for recurring events (#50561) --- chrono/agendas/models.py | 3 ++- chrono/api/views.py | 32 +++++++++++++++++++++++++++----- tests/test_api.py | 2 +- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index ee16fa4a..147f0629 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -660,10 +660,11 @@ class Agenda(models.Model): if prefetched_queryset: recurring_events = self.prefetched_recurring_events + exceptions = self.prefetched_exceptions else: recurring_events = self.event_set.filter(recurrence_rule__isnull=False) + exceptions = self.get_recurrence_exceptions(min_start, max_start) - exceptions = self.get_recurrence_exceptions(min_start, max_start) for event in recurring_events: events.extend( event.get_recurrences( diff --git a/chrono/api/views.py b/chrono/api/views.py index 6622d8c7..a5e5c948 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -565,6 +565,9 @@ class Agendas(APIView): start_datetime__gte=localtime(now()), ).order_by() recurring_event_queryset = Event.objects.filter(recurrence_rule__isnull=False) + exceptions_desk = Desk.objects.filter(slug='_exceptions_holder').prefetch_related( + 'unavailability_calendars' + ) agendas_queryset = agendas_queryset.filter(kind='events').prefetch_related( Prefetch( 'event_set', @@ -576,15 +579,34 @@ class Agendas(APIView): queryset=recurring_event_queryset, to_attr='prefetched_recurring_events', ), + Prefetch( + 'desk_set', + queryset=exceptions_desk, + to_attr='prefetched_desks', + ), + ) + agendas_exceptions = TimePeriodException.objects.filter( + Q(desk__slug='_exceptions_holder', desk__agenda__in=agendas_queryset) + | Q( + unavailability_calendar__desks__slug='_exceptions_holder', + unavailability_calendar__desks__agenda__in=agendas_queryset, + ), + start_datetime__gte=localtime(now()), ) agendas = [] for agenda in agendas_queryset: - if with_open_events and not any( - not e.full for e in agenda.get_open_events(prefetched_queryset=True) - ): - # exclude agendas without open events - continue + if with_open_events: + desk = agenda.prefetched_desks[0] + uc_ids = [uc.pk for uc in desk.unavailability_calendars.all()] + agenda.prefetched_exceptions = [ + e + for e in agendas_exceptions + if e.desk_id == desk.pk or e.unavailability_calendar_id in uc_ids + ] + if not any(not e.full for e in agenda.get_open_events(prefetched_queryset=True)): + # exclude agendas without open events + continue agendas.append(get_agenda_detail(request, agenda)) return Response({'data': agendas}) diff --git a/tests/test_api.py b/tests/test_api.py index 17edca2d..361c6630 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -317,7 +317,7 @@ def test_agendas_api(app): with CaptureQueriesContext(connection) as ctx: resp = app.get('/api/agenda/', params={'with_open_events': '1'}) - assert len(ctx.captured_queries) == 15 + assert len(ctx.captured_queries) == 7 def test_agendas_meetingtypes_api(app, some_data, meetings_agenda):