misc: clean and simplify prefetch methods (#63268)

This commit is contained in:
Lauréline Guérin 2022-03-29 14:40:16 +02:00
parent a7c5e93227
commit db5b7f3be7
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
7 changed files with 38 additions and 88 deletions

View File

@ -931,75 +931,27 @@ class Agenda(models.Model):
return qs
@staticmethod
def prefetch_events_and_exceptions(
qs,
user_external_id=None,
guardian_external_id=None,
show_past_events=False,
show_only_subscribed=False,
min_start=None,
max_start=None,
):
def prefetch_events(qs, user_external_id=None, guardian_external_id=None, annotate_for_user=True):
event_queryset = Event.objects.filter(
Q(publication_datetime__isnull=True) | Q(publication_datetime__lte=now()),
recurrence_days__isnull=True,
cancelled=False,
start_datetime__gte=localtime(now()),
).order_by()
if user_external_id:
if user_external_id and annotate_for_user:
event_queryset = Event.annotate_queryset_for_user(event_queryset, user_external_id)
if not show_past_events:
event_queryset = event_queryset.filter(start_datetime__gte=localtime(now()))
if min_start:
event_queryset = event_queryset.filter(start_datetime__gte=min_start)
if max_start:
event_queryset = event_queryset.filter(start_datetime__lt=max_start)
if show_only_subscribed:
event_queryset = event_queryset.filter(
agenda__subscriptions__user_external_id=user_external_id,
agenda__subscriptions__date_start__lte=F('start_datetime'),
agenda__subscriptions__date_end__gt=F('start_datetime'),
)
if guardian_external_id:
if guardian_external_id and user_external_id:
event_queryset = Agenda.filter_for_guardian(
event_queryset, guardian_external_id, user_external_id
)
exceptions_desk = Desk.objects.filter(slug='_exceptions_holder').prefetch_related(
'unavailability_calendars'
)
qs = qs.filter(kind='events').prefetch_related(
return qs.filter(kind='events').prefetch_related(
Prefetch(
'event_set',
queryset=event_queryset,
to_attr='prefetched_events',
),
Prefetch(
'desk_set',
queryset=exceptions_desk,
to_attr='prefetched_desks',
),
)
qs = Agenda.prefetch_recurring_events(qs)
qs = qs.select_related('category')
agendas_exceptions = TimePeriodException.objects.filter(
Q(desk__slug='_exceptions_holder', desk__agenda__in=qs)
| Q(
unavailability_calendar__desks__slug='_exceptions_holder',
unavailability_calendar__desks__agenda__in=qs,
),
start_datetime__gte=localtime(now()),
)
agendas = list(qs)
for agenda in agendas:
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
]
return agendas
def is_available_for_simple_management(self):
if self.kind != 'meetings':

View File

@ -244,7 +244,7 @@ class AgendaOrSubscribedSlugsMixin(DateRangeMixin):
if 'date_end' in attrs:
# subscription must start before requested date end
lookups['subscriptions__date_start__lte'] = attrs['date_end']
agendas = Agenda.objects.filter(**lookups).distinct()
agendas = Agenda.objects.filter(**lookups).distinct().select_related('category')
if attrs['subscribed'] != ['all']:
agendas = agendas.filter(category__slug__in=attrs['subscribed'])

View File

@ -735,7 +735,7 @@ class Agendas(APIView):
with_open_events = request.GET.get('with_open_events') in ['1', 'true']
if with_open_events:
# return only events agenda
agendas_queryset = Agenda.prefetch_events_and_exceptions(agendas_queryset)
agendas_queryset = Agenda.prefetch_events(agendas_queryset)
agendas = []
for agenda in agendas_queryset:
@ -882,36 +882,33 @@ class MultipleAgendasDatetimes(APIView):
agendas = payload['agendas']
user_external_id = payload.get('user_external_id') or payload.get('exclude_user_external_id')
disable_booked = bool(payload.get('exclude_user_external_id'))
guardian_external_id = payload.get('guardian_external_id')
show_past_events = bool(payload.get('show_past_events'))
agendas = Agenda.prefetch_events_and_exceptions(
agendas,
user_external_id=user_external_id,
guardian_external_id=payload.get('guardian_external_id'),
show_past_events=show_past_events,
show_only_subscribed=bool('subscribed' in payload),
min_start=payload.get('date_start'),
max_start=payload.get('date_end'),
)
show_only_subscribed = bool('subscribed' in payload)
entries = []
entries = Event.objects.none()
for agenda in agendas:
if show_past_events:
entries.extend(
agenda.get_past_events(
prefetched_queryset=True,
min_start=payload.get('date_start'),
max_start=payload.get('date_end'),
)
)
entries.extend(
agenda.get_open_events(
prefetched_queryset=True,
entries |= agenda.get_past_events(
min_start=payload.get('date_start'),
max_start=payload.get('date_end'),
bypass_delays=payload.get('bypass_delays'),
show_out_of_minimal_delay=show_past_events,
)
entries |= agenda.get_open_events(
min_start=payload.get('date_start'),
max_start=payload.get('date_end'),
bypass_delays=payload.get('bypass_delays'),
show_out_of_minimal_delay=show_past_events,
)
entries = Event.annotate_queryset_for_user(entries, user_external_id)
if show_only_subscribed:
entries = entries.filter(
agenda__subscriptions__user_external_id=user_external_id,
agenda__subscriptions__date_start__lte=F('start_datetime'),
agenda__subscriptions__date_end__gt=F('start_datetime'),
)
if guardian_external_id:
entries = Agenda.filter_for_guardian(entries, guardian_external_id, user_external_id)
entries = list(entries)
if 'agendas' in request.query_params:
agenda_querystring_indexes = {
@ -1093,10 +1090,11 @@ class RecurringEventsList(APIView):
guardian_external_id = data.get('guardian_external_id')
if guardian_external_id:
agendas = Agenda.prefetch_events_and_exceptions(
agendas = Agenda.prefetch_events(
data['agendas'],
user_external_id=data.get('user_external_id'),
guardian_external_id=guardian_external_id,
annotate_for_user=False,
)
days_by_event = collections.defaultdict(set)
for agenda in agendas:
@ -1113,7 +1111,7 @@ class RecurringEventsList(APIView):
event.day = day
events.append(event)
else:
agendas = Agenda.prefetch_recurring_events(data['agendas']).select_related('category')
agendas = Agenda.prefetch_recurring_events(data['agendas'])
events = []
for agenda in agendas:
for event in agenda.get_open_recurring_events():
@ -1640,7 +1638,7 @@ class RecurringFillslots(APIView):
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
payload = serializer.validated_data
user_external_id = payload['user_external_id']
agendas = Agenda.prefetch_events_and_exceptions(data['agendas'], user_external_id=user_external_id)
agendas = Agenda.prefetch_events(data['agendas'], user_external_id=user_external_id)
if data['action'] == 'update':
events_to_book = self.get_event_recurrences(

View File

@ -310,7 +310,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) == 8
assert len(ctx.captured_queries) == 4
def test_agendas_meetingtypes_api(app, some_data, meetings_agenda):

View File

@ -3572,7 +3572,7 @@ def test_recurring_events_api_fillslots_multiple_agendas_queries(app, user):
params={'slots': events_to_book, 'user_external_id': 'user'},
)
assert resp.json['booking_count'] == 180
assert len(ctx.captured_queries) == 17
assert len(ctx.captured_queries) == 13
father = Person.objects.create(user_external_id='father_id', name='John Doe')
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
@ -3590,7 +3590,7 @@ def test_recurring_events_api_fillslots_multiple_agendas_queries(app, user):
params={'slots': events_to_book, 'user_external_id': 'xxx'},
)
assert resp.json['booking_count'] == 100
assert len(ctx.captured_queries) == 17
assert len(ctx.captured_queries) == 13
@pytest.mark.freeze_time('2021-09-06 12:00')

View File

@ -379,7 +379,7 @@ def test_datetimes_multiple_agendas_queries(app):
params={'agendas': ','.join(str(i) for i in range(10)), 'show_past_events': True},
)
assert len(resp.json['data']) == 30
assert len(ctx.captured_queries) == 7
assert len(ctx.captured_queries) == 2
with CaptureQueriesContext(connection) as ctx:
resp = app.get(
@ -387,7 +387,7 @@ def test_datetimes_multiple_agendas_queries(app):
params={'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True},
)
assert len(resp.json['data']) == 30
assert len(ctx.captured_queries) == 7
assert len(ctx.captured_queries) == 2
with CaptureQueriesContext(connection) as ctx:
resp = app.get(
@ -395,7 +395,7 @@ def test_datetimes_multiple_agendas_queries(app):
params={'subscribed': 'category-a', 'user_external_id': 'xxx', 'show_past_events': True},
)
assert len(resp.json['data']) == 30
assert len(ctx.captured_queries) == 7
assert len(ctx.captured_queries) == 2
father = Person.objects.create(user_external_id='father_id', name='John Doe')
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
@ -417,7 +417,7 @@ def test_datetimes_multiple_agendas_queries(app):
},
)
assert len(resp.json['data']) == 30
assert len(ctx.captured_queries) == 7
assert len(ctx.captured_queries) == 2
@pytest.mark.freeze_time('2021-05-06 14:00')

View File

@ -339,7 +339,7 @@ def test_recurring_events_api_list_multiple_agendas_queries(app):
'/api/agendas/recurring-events/?subscribed=category-a&user_external_id=xxx&guardian_external_id=father_id'
)
assert len(resp.json['data']) == 40
assert len(ctx.captured_queries) == 8
assert len(ctx.captured_queries) == 4
@pytest.mark.freeze_time('2021-09-06 12:00')