0002-manager-unavailability-calendars-exceptions-in-timet.patch
chrono/agendas/models.py | ||
---|---|---|
530 | 530 |
return |
531 | 531 | |
532 | 532 |
def prefetch_desks_and_exceptions(self): |
533 |
assert self.kind != 'events' |
|
533 |
if self.kind == 'meetings': |
|
534 |
desks = self.desk_set.all() |
|
535 |
elif self.kind == 'virtual': |
|
536 |
desks = ( |
|
537 |
Desk.objects.filter(agenda__virtual_agendas=self) |
|
538 |
.select_related('agenda') |
|
539 |
.order_by('agenda', 'label') |
|
540 |
) |
|
541 |
else: |
|
542 |
raise TypeError('This method cannot be called on events agendas.') |
|
534 | 543 | |
535 |
self.prefetched_desks = self.desk_set.all().prefetch_related( |
|
536 |
'timeperiod_set', 'unavailability_calendars', |
|
537 |
) |
|
544 |
self.prefetched_desks = desks.prefetch_related('timeperiod_set', 'unavailability_calendars') |
|
538 | 545 |
all_desks_exceptions = TimePeriodException.objects.filter( |
539 | 546 |
Q(desk__in=self.prefetched_desks) | Q(unavailability_calendar__desks__in=self.prefetched_desks) |
540 | 547 |
).select_related('source') |
... | ... | |
1382 | 1389 |
aware_date = make_aware(datetime.datetime(date.year, date.month, date.day)) |
1383 | 1390 |
exceptions = IntervalSet() |
1384 | 1391 |
aware_next_date = aware_date + datetime.timedelta(days=1) |
1385 |
for exception in self.timeperiodexception_set.all(): |
|
1386 |
# timeperiodexception_set.all() are prefetched, do not filter in queryset |
|
1392 |
for exception in self.prefetched_exceptions: |
|
1387 | 1393 |
if exception.end_datetime < aware_date: |
1388 | 1394 |
continue |
1389 | 1395 |
if exception.start_datetime > aware_next_date: |
chrono/manager/views.py | ||
---|---|---|
877 | 877 |
def get_queryset(self): |
878 | 878 |
if self.agenda.kind == 'events': |
879 | 879 |
queryset = self.agenda.event_set.all() |
880 |
elif self.agenda.kind == 'meetings': |
|
881 |
self.agenda.prefetched_desks = self.agenda.desk_set.all().prefetch_related( |
|
882 |
'timeperiod_set', 'timeperiodexception_set' |
|
883 |
) |
|
884 |
queryset = self.agenda.event_set.select_related('meeting_type').prefetch_related('booking_set') |
|
885 | 880 |
else: |
886 |
self.agenda.prefetched_desks = (
|
|
887 |
Desk.objects.filter(agenda__virtual_agendas=self.agenda)
|
|
888 |
.prefetch_related('timeperiod_set', 'timeperiodexception_set')
|
|
889 |
.select_related('agenda')
|
|
890 |
.order_by('agenda', 'label')
|
|
891 |
)
|
|
892 |
queryset = ( |
|
893 |
Event.objects.filter(agenda__virtual_agendas=self.agenda) |
|
894 |
.select_related('meeting_type') |
|
895 |
.prefetch_related('booking_set') |
|
896 |
) |
|
881 |
self.agenda.prefetch_desks_and_exceptions()
|
|
882 |
if self.agenda.kind == 'meetings':
|
|
883 |
queryset = self.agenda.event_set.select_related('meeting_type').prefetch_related(
|
|
884 |
'booking_set'
|
|
885 |
) |
|
886 |
else:
|
|
887 |
queryset = (
|
|
888 |
Event.objects.filter(agenda__virtual_agendas=self.agenda)
|
|
889 |
.select_related('meeting_type')
|
|
890 |
.prefetch_related('booking_set')
|
|
891 |
)
|
|
897 | 892 |
return queryset |
898 | 893 | |
899 | 894 | |
... | ... | |
977 | 972 | |
978 | 973 |
# and exceptions for this desk |
979 | 974 |
info['exceptions'] = [] |
980 |
for exception in desk.timeperiodexception_set.all():
|
|
981 |
if exception.end_datetime < start_date:
|
|
975 |
for exception in desk.prefetched_exceptions:
|
|
976 |
if exception.end_datetime < current_date:
|
|
982 | 977 |
continue |
983 | 978 |
if exception.start_datetime > max_date: |
984 | 979 |
continue |
... | ... | |
1177 | 1172 |
'css_left': left, |
1178 | 1173 |
} |
1179 | 1174 |
) |
1180 |
for exception in desk.timeperiodexception_set.all():
|
|
1175 |
for exception in desk.prefetched_exceptions:
|
|
1181 | 1176 |
if exception.end_datetime < current_date: |
1182 | 1177 |
continue |
1183 | 1178 |
if exception.start_datetime > max_date: |
tests/test_manager.py | ||
---|---|---|
2984 | 2984 |
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200) |
2985 | 2985 | |
2986 | 2986 |
# display exception |
2987 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='calendar') |
|
2988 |
TimePeriodException.objects.create( |
|
2989 |
label='Calendar exception', |
|
2990 |
unavailability_calendar=unavailability_calendar, |
|
2991 |
start_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 13, 0)), |
|
2992 |
end_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 15, 0)), |
|
2993 |
) |
|
2994 |
unavailability_calendar.desks.add(desk) |
|
2987 | 2995 |
TimePeriodException.objects.create( |
2988 | 2996 |
label='Exception for the afternoon', |
2989 | 2997 |
desk=desk, |
2990 |
start_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 13, 0)),
|
|
2998 |
start_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 16, 0)),
|
|
2991 | 2999 |
end_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 23, 0)), |
2992 | 3000 |
) |
2993 | 3001 |
with CaptureQueriesContext(connection) as ctx: |
2994 | 3002 |
resp = app.get( |
2995 | 3003 |
'/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200 |
2996 | 3004 |
) |
2997 |
assert len(ctx.captured_queries) == 14
|
|
3005 |
assert len(ctx.captured_queries) == 15
|
|
2998 | 3006 |
# day is displaying rows from 10am to 6pm, |
2999 | 3007 |
# opening hours, 10am to 1pm gives top: 300% |
3000 |
# rest of the day, 1pm to 6(+1)pm gives 600%
|
|
3008 |
# calendar exception, 1pm to 3pm gives heigh: 200%
|
|
3001 | 3009 |
assert resp.pyquery.find('.exception-hours')[0].attrib == { |
3002 | 3010 |
'class': 'exception-hours', |
3003 |
'style': 'height: 600%; top: 300%;',
|
|
3011 |
'style': 'height: 200%; top: 300%;',
|
|
3004 | 3012 |
} |
3005 |
assert resp.pyquery.find('.exception-hours span')[0].text == 'Exception for the afternoon' |
|
3013 |
assert resp.pyquery.find('.exception-hours span')[0].text == 'Calendar exception' |
|
3014 |
# rest of the day, opened from 3pm to 4pm, since we left off at 500% it gives top: 600% |
|
3015 |
# then exception from 4pm to 6pm included, gives height: 300% |
|
3016 |
assert resp.pyquery.find('.exception-hours')[1].attrib == { |
|
3017 |
'class': 'exception-hours', |
|
3018 |
'style': 'height: 300%; top: 600%;', |
|
3019 |
} |
|
3020 |
assert resp.pyquery.find('.exception-hours span')[1].text == 'Exception for the afternoon' |
|
3006 | 3021 | |
3007 | 3022 | |
3008 | 3023 |
@pytest.mark.parametrize('kind', ['meetings', 'virtual']) |
... | ... | |
3295 | 3310 |
assert 'No opening hours this month.' not in resp.text |
3296 | 3311 | |
3297 | 3312 |
# display exception |
3313 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='calendar') |
|
3314 |
TimePeriodException.objects.create( |
|
3315 |
label='Calendar exception', |
|
3316 |
unavailability_calendar=unavailability_calendar, |
|
3317 |
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 5, 0)), |
|
3318 |
end_datetime=make_aware(datetime.datetime(2018, 12, 15, 14, 0)), |
|
3319 |
) |
|
3320 |
unavailability_calendar.desks.add(desk) |
|
3298 | 3321 |
TimePeriodException.objects.create( |
3299 | 3322 |
label='Exception for a December day', |
3300 | 3323 |
desk=desk, |
3301 |
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 5, 0)),
|
|
3324 |
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 14, 0)),
|
|
3302 | 3325 |
end_datetime=make_aware(datetime.datetime(2018, 12, 15, 23, 0)), |
3303 | 3326 |
) |
3304 | 3327 |
with CaptureQueriesContext(connection) as ctx: |
3305 | 3328 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month)) |
3306 |
assert len(ctx.captured_queries) == 9
|
|
3329 |
assert len(ctx.captured_queries) == 10
|
|
3307 | 3330 |
assert resp.pyquery.find('.exception-hours')[0].attrib == { |
3308 | 3331 |
'class': 'exception-hours', |
3309 |
'style': 'height:800.0%;top:0.0%;width:48.0%;left:50.0%;', |
|
3332 |
'style': 'height:400.0%;top:0.0%;width:48.0%;left:50.0%;', |
|
3333 |
'title': 'Calendar exception', |
|
3334 |
} |
|
3335 |
assert resp.pyquery.find('.exception-hours')[1].attrib == { |
|
3336 |
'class': 'exception-hours', |
|
3337 |
'style': 'height:400.0%;top:400.0%;width:48.0%;left:50.0%;', |
|
3310 | 3338 |
'title': 'Exception for a December day', |
3311 | 3339 |
} |
3312 | 3340 | |
... | ... | |
3934 | 3962 |
resp = app.get( |
3935 | 3963 |
'/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200 |
3936 | 3964 |
) |
3937 |
assert len(ctx.captured_queries) == 15
|
|
3965 |
assert len(ctx.captured_queries) == 16
|
|
3938 | 3966 |
# day is displaying rows from 10am to 6pm, |
3939 | 3967 |
# opening hours, 10am to 1pm gives top: 300% |
3940 | 3968 |
# rest of the day, 1pm to 6(+1)pm gives 600% |
... | ... | |
4032 | 4060 |
) |
4033 | 4061 |
with CaptureQueriesContext(connection) as ctx: |
4034 | 4062 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month)) |
4035 |
assert len(ctx.captured_queries) == 10
|
|
4063 |
assert len(ctx.captured_queries) == 11
|
|
4036 | 4064 |
assert resp.pyquery.find('.exception-hours')[0].attrib == { |
4037 | 4065 |
'class': 'exception-hours', |
4038 | 4066 |
'style': 'height:800.0%;top:0.0%;width:48.0%;left:1.0%;', |
4039 |
- |