0002-api-prefetch-events-in-multiple-agendas-datetimes-55.patch
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 | ||
---|---|---|
1451 | 1451 |
with CaptureQueriesContext(connection) as ctx: |
1452 | 1452 |
resp = app.get('/api/agenda/datetimes/', params={'agendas': ','.join(str(i) for i in range(10))}) |
1453 | 1453 |
assert len(resp.json['data']) == 20 |
1454 |
assert len(ctx.captured_queries) == 21 |
|
1454 |
assert len(ctx.captured_queries) == 7 |
|
1455 |
- |