From c309c767507dbac1717f75d7343abcd121d830fd Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 4 Feb 2021 15:10:29 +0100 Subject: [PATCH 3/4] 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 4c447b2..fc95f1b 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -596,10 +596,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 9a35085..a8d2aa4 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -492,6 +492,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', @@ -503,15 +506,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 3913549..a31fd39 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -309,7 +309,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) == 6 + assert len(ctx.captured_queries) == 7 def test_agendas_meetingtypes_api(app, some_data, meetings_agenda): -- 2.20.1