0006-manager-display-date-time-period-in-calendar-views-7.patch
chrono/agendas/models.py | ||
---|---|---|
795 | 795 |
end_datetime__gt=min_start, |
796 | 796 |
) |
797 | 797 | |
798 |
def prefetch_desks_and_exceptions(self, with_sources=False, min_date=None):
|
|
798 |
def prefetch_desks_and_exceptions(self, min_date, max_date=None, with_sources=False):
|
|
799 | 799 |
if self.kind == 'meetings': |
800 | 800 |
desks = self.desk_set.all() |
801 | 801 |
elif self.kind == 'virtual': |
... | ... | |
807 | 807 |
else: |
808 | 808 |
raise ValueError('does not work with kind %r' % self.kind) |
809 | 809 | |
810 |
if min_date: |
|
811 |
past_date_time_periods = TimePeriod.objects.filter(desk=OuterRef('pk'), date__lt=min_date) |
|
812 |
desks = desks.annotate(has_past_date_time_periods=Exists(past_date_time_periods)) |
|
810 |
past_date_time_periods = TimePeriod.objects.filter(desk=OuterRef('pk'), date__lt=min_date) |
|
811 |
desks = desks.annotate(has_past_date_time_periods=Exists(past_date_time_periods)) |
|
813 | 812 | |
814 |
time_period_queryset = TimePeriod.objects.filter(Q(date__isnull=True) | Q(date__gte=min_date)) |
|
813 |
time_period_queryset = TimePeriod.objects.filter(Q(date__isnull=True) | Q(date__gte=min_date)) |
|
814 |
if max_date: |
|
815 |
time_period_queryset = time_period_queryset.filter(Q(date__isnull=True) | Q(date__lte=max_date)) |
|
815 | 816 | |
816 | 817 |
self.prefetched_desks = desks.prefetch_related( |
817 | 818 |
'unavailability_calendars', Prefetch('timeperiod_set', queryset=time_period_queryset) |
... | ... | |
2276 | 2277 |
def get_opening_hours(self, date): |
2277 | 2278 |
openslots = IntervalSet() |
2278 | 2279 |
weekday_index = get_weekday_index(date) |
2280 |
real_date = date.date() if isinstance(date, datetime.datetime) else date |
|
2279 | 2281 |
for timeperiod in self.timeperiod_set.all(): |
2280 | 2282 |
if timeperiod.weekday_indexes and weekday_index not in timeperiod.weekday_indexes: |
2281 | 2283 |
continue |
2282 | 2284 |
# timeperiod_set.all() are prefetched, do not filter in queryset |
2283 |
if timeperiod.weekday != date.weekday(): |
|
2285 |
if timeperiod.date != real_date and timeperiod.weekday != date.weekday():
|
|
2284 | 2286 |
continue |
2285 | 2287 |
start_datetime = make_aware(datetime.datetime.combine(date, timeperiod.start_time)) |
2286 | 2288 |
end_datetime = make_aware(datetime.datetime.combine(date, timeperiod.end_time)) |
chrono/manager/views.py | ||
---|---|---|
1264 | 1264 |
if self.agenda.kind == 'events': |
1265 | 1265 |
queryset = self.agenda.event_set.filter(recurrence_days__isnull=True) |
1266 | 1266 |
else: |
1267 |
self.agenda.prefetch_desks_and_exceptions() |
|
1267 |
self.agenda.prefetch_desks_and_exceptions(min_date=self.date, max_date=self.get_max_date())
|
|
1268 | 1268 |
if self.agenda.kind == 'meetings': |
1269 | 1269 |
queryset = self.agenda.event_set.select_related('meeting_type').prefetch_related( |
1270 | 1270 |
'booking_set' |
... | ... | |
1324 | 1324 |
}, |
1325 | 1325 |
) |
1326 | 1326 | |
1327 |
def get_max_date(self): |
|
1328 |
return self.date.date() + datetime.timedelta(days=1) |
|
1329 | ||
1327 | 1330 |
def get_timetable_infos(self): |
1328 | 1331 |
timeperiods = itertools.chain(*(d.timeperiod_set.all() for d in self.agenda.prefetched_desks)) |
1329 | 1332 |
timeperiods = [ |
1330 | 1333 |
t |
1331 | 1334 |
for t in timeperiods |
1332 |
if t.weekday == self.date.weekday() |
|
1335 |
if t.date == self.date.date() |
|
1336 |
or t.weekday == self.date.weekday() |
|
1333 | 1337 |
and (not t.weekday_indexes or get_weekday_index(self.date) in t.weekday_indexes) |
1334 | 1338 |
] |
1335 | 1339 | |
... | ... | |
1457 | 1461 |
if timeperiods: |
1458 | 1462 |
min_timeperiod = min(x.start_time for x in timeperiods) |
1459 | 1463 |
max_timeperiod = max(x.end_time for x in timeperiods) |
1460 |
hide_sunday_timeperiod = not any([e.weekday == 6 for e in timeperiods]) |
|
1464 |
hide_sunday_timeperiod = not any( |
|
1465 |
[e.weekday == 6 or (e.date and e.date.weekday() == 6) for e in timeperiods] |
|
1466 |
) |
|
1461 | 1467 |
hide_weekend_timeperiod = hide_sunday_timeperiod and not any( |
1462 |
[e.weekday == 5 for e in timeperiods] |
|
1468 |
[e.weekday == 5 or (e.date and e.date.weekday() == 5) for e in timeperiods]
|
|
1463 | 1469 |
) |
1464 | 1470 |
active_events = [ |
1465 | 1471 |
x for x in self.object_list if any([y.cancellation_datetime is None for y in x.booking_set.all()]) |
... | ... | |
1633 | 1639 |
date = datetime.datetime.strptime('%s-W%s-1' % (self.get_year(), self.get_week()), "%Y-W%W-%w") |
1634 | 1640 |
return date.day |
1635 | 1641 | |
1642 |
def get_max_date(self): |
|
1643 |
return self.get_next_week(self.date.date()) |
|
1644 | ||
1636 | 1645 | |
1637 | 1646 |
agenda_weekly_view = AgendaWeekView.as_view() |
1638 | 1647 | |
... | ... | |
1665 | 1674 |
def get_day(self): |
1666 | 1675 |
return '1' |
1667 | 1676 | |
1677 |
def get_max_date(self): |
|
1678 |
return self.get_next_month(self.date.date()) |
|
1679 | ||
1668 | 1680 | |
1669 | 1681 |
agenda_monthly_view = AgendaMonthView.as_view() |
1670 | 1682 |
tests/manager/test_all.py | ||
---|---|---|
3716 | 3716 |
assert resp.text.count('height:400.0%') == 1 |
3717 | 3717 |
assert resp.text.count('height:700.0%') == 1 |
3718 | 3718 |
assert resp.text.count('height:300.0%') == 1 |
3719 | ||
3720 | ||
3721 |
@freezegun.freeze_time('2022-11-15 14:00') |
|
3722 |
def test_agenda_calendar_views_date_time_period(app, admin_user): |
|
3723 |
agenda = Agenda.objects.create(label='New Example', kind='meetings') |
|
3724 |
desk = Desk.objects.create(agenda=agenda, label='New Desk') |
|
3725 |
MeetingType.objects.create(agenda=agenda, label='Bar', duration=30) |
|
3726 |
today = datetime.date.today() |
|
3727 |
TimePeriod.objects.create( |
|
3728 |
desk=desk, |
|
3729 |
date=today, |
|
3730 |
start_time=datetime.time(10, 0), |
|
3731 |
end_time=datetime.time(14, 0), |
|
3732 |
) |
|
3733 |
login(app) |
|
3734 | ||
3735 |
# check day view |
|
3736 |
resp = app.get('/manage/agendas/%s/%s/%s/%s/' % (agenda.pk, today.year, today.month, today.day)) |
|
3737 |
assert resp.text.count('<tr') == 5 # 10->14 |
|
3738 |
assert 'style="height: 400%; top: 0%;"' in resp.text |
|
3739 | ||
3740 |
resp = app.get('/manage/agendas/%s/%s/%s/%s/' % (agenda.pk, today.year, today.month, today.day + 7)) |
|
3741 |
assert 'No opening hours this day.' in resp.text |
|
3742 | ||
3743 |
# check week view |
|
3744 |
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.pk, today.year, today.isocalendar().week)) |
|
3745 |
assert resp.text.count('height:400.0%') == 1 |
|
3746 | ||
3747 |
# check month view |
|
3748 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month)) |
|
3749 |
assert resp.text.count('height:400.0%') == 1 |
|
3750 | ||
3751 |
# check month boundaries |
|
3752 |
TimePeriod.objects.create( |
|
3753 |
desk=desk, |
|
3754 |
date=today.replace(day=1), |
|
3755 |
start_time=datetime.time(10, 0), |
|
3756 |
end_time=datetime.time(14, 0), |
|
3757 |
) |
|
3758 |
TimePeriod.objects.create( |
|
3759 |
desk=desk, |
|
3760 |
date=today.replace(day=30), |
|
3761 |
start_time=datetime.time(10, 0), |
|
3762 |
end_time=datetime.time(14, 0), |
|
3763 |
) |
|
3764 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month)) |
|
3765 |
assert resp.text.count('height:400.0%') == 3 |
|
3766 | ||
3767 |
TimePeriod.objects.create( |
|
3768 |
desk=desk, |
|
3769 |
date=today.replace(day=31, month=10), |
|
3770 |
start_time=datetime.time(10, 0), |
|
3771 |
end_time=datetime.time(14, 0), |
|
3772 |
) |
|
3773 |
TimePeriod.objects.create( |
|
3774 |
desk=desk, |
|
3775 |
date=today.replace(day=1, month=12), |
|
3776 |
start_time=datetime.time(10, 0), |
|
3777 |
end_time=datetime.time(14, 0), |
|
3778 |
) |
|
3779 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month)) |
|
3780 |
assert resp.text.count('height:400.0%') == 3 |
|
3781 | ||
3782 |
# check week boundaries |
|
3783 |
TimePeriod.objects.create( |
|
3784 |
desk=desk, |
|
3785 |
date=today.replace(day=14), |
|
3786 |
start_time=datetime.time(10, 0), |
|
3787 |
end_time=datetime.time(14, 0), |
|
3788 |
) |
|
3789 |
TimePeriod.objects.create( |
|
3790 |
desk=desk, |
|
3791 |
date=today.replace(day=20), |
|
3792 |
start_time=datetime.time(10, 0), |
|
3793 |
end_time=datetime.time(14, 0), |
|
3794 |
) |
|
3795 |
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.pk, today.year, today.isocalendar().week)) |
|
3796 |
assert resp.text.count('height:400.0%') == 3 |
|
3797 | ||
3798 |
TimePeriod.objects.create( |
|
3799 |
desk=desk, |
|
3800 |
date=today.replace(day=13), |
|
3801 |
start_time=datetime.time(10, 0), |
|
3802 |
end_time=datetime.time(14, 0), |
|
3803 |
) |
|
3804 |
TimePeriod.objects.create( |
|
3805 |
desk=desk, |
|
3806 |
date=today.replace(day=21), |
|
3807 |
start_time=datetime.time(10, 0), |
|
3808 |
end_time=datetime.time(14, 0), |
|
3809 |
) |
|
3810 |
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.pk, today.year, today.isocalendar().week)) |
|
3811 |
assert resp.text.count('height:400.0%') == 3 |
|
3812 | ||
3813 | ||
3814 |
@freezegun.freeze_time('2022-11-15 14:00') |
|
3815 |
@pytest.mark.parametrize('kind', ['meetings', 'virtual']) |
|
3816 |
def test_agenda_date_time_period_hide_weekend(app, admin_user, kind): |
|
3817 |
today = datetime.date.today() # Tuesday |
|
3818 |
if kind == 'meetings': |
|
3819 |
agenda = Agenda.objects.create(label='Passeports', kind='meetings') |
|
3820 |
desk = Desk.objects.create(agenda=agenda, label='Desk A') |
|
3821 |
else: |
|
3822 |
agenda = Agenda.objects.create(label='Virtual', kind='virtual') |
|
3823 |
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings') |
|
3824 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda) |
|
3825 |
desk = Desk.objects.create(agenda=real_agenda, label='New Desk') |
|
3826 |
TimePeriod.objects.create( |
|
3827 |
desk=desk, date=today, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
3828 |
) |
|
3829 | ||
3830 |
login(app) |
|
3831 |
# check month view |
|
3832 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month)) |
|
3833 |
assert 'Sunday' not in resp.text |
|
3834 |
assert 'Saturday' not in resp.text |
|
3835 | ||
3836 |
# check week view |
|
3837 |
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.pk, today.year, today.isocalendar().week)) |
|
3838 |
assert 'Sunday' not in resp.text |
|
3839 |
assert 'Saturday' not in resp.text |
|
3840 | ||
3841 |
TimePeriod.objects.create( |
|
3842 |
desk=desk, date=today.replace(day=19), start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
3843 |
) # Saturday |
|
3844 | ||
3845 |
# check month view |
|
3846 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month)) |
|
3847 |
assert 'Sunday' not in resp.text |
|
3848 |
assert 'Saturday' in resp.text |
|
3849 | ||
3850 |
# check week view |
|
3851 |
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.pk, today.year, today.isocalendar().week)) |
|
3852 |
assert 'Sunday' not in resp.text |
|
3853 |
assert 'Saturday' in resp.text |
|
3854 | ||
3855 |
TimePeriod.objects.create( |
|
3856 |
desk=desk, date=today.replace(day=20), start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
3857 |
) # Sunday |
|
3858 | ||
3859 |
# check month view |
|
3860 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month)) |
|
3861 |
assert 'Sunday' in resp.text |
|
3862 |
assert 'Saturday' in resp.text |
|
3863 | ||
3864 |
# check week view |
|
3865 |
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.pk, today.year, today.isocalendar().week)) |
|
3866 |
assert 'Sunday' in resp.text |
|
3867 |
assert 'Saturday' in resp.text |
tests/test_agendas.py | ||
---|---|---|
276 | 276 |
def check_is_available(result, use_prefetch=True): |
277 | 277 |
agenda = Agenda.objects.get() |
278 | 278 |
if with_prefetch and use_prefetch: |
279 |
agenda.prefetch_desks_and_exceptions(with_sources=True) |
|
279 |
agenda.prefetch_desks_and_exceptions(with_sources=True, min_date=now())
|
|
280 | 280 |
assert agenda.is_available_for_simple_management() == result |
281 | 281 | |
282 | 282 |
agenda = Agenda.objects.create(label='Agenda', kind='meetings') |
tests/test_time_periods.py | ||
---|---|---|
299 | 299 |
assert len(hours) == 0 |
300 | 300 | |
301 | 301 | |
302 |
def test_desk_opening_hours_date_time_period(): |
|
303 |
def set_prefetched_exceptions(desk): |
|
304 |
desk.prefetched_exceptions = TimePeriodException.objects.filter( |
|
305 |
Q(desk=desk) | Q(unavailability_calendar__desks=desk) |
|
306 |
) |
|
307 | ||
308 |
agenda = Agenda.objects.create(label='Foo bar', slug='bar') |
|
309 |
desk = Desk.objects.create(label='Desk 1', agenda=agenda) |
|
310 | ||
311 |
# morning |
|
312 |
TimePeriod.objects.create( |
|
313 |
desk=desk, |
|
314 |
date=datetime.date(2022, 10, 24), |
|
315 |
start_time=datetime.time(9, 0), |
|
316 |
end_time=datetime.time(12, 0), |
|
317 |
) |
|
318 |
set_prefetched_exceptions(desk) |
|
319 |
hours = desk.get_opening_hours(datetime.date(2022, 10, 24)) |
|
320 |
assert len(hours) == 1 |
|
321 |
assert hours[0].begin.time() == datetime.time(9, 0) |
|
322 |
assert hours[0].end.time() == datetime.time(12, 0) |
|
323 | ||
324 |
# and afternoon |
|
325 |
TimePeriod.objects.create( |
|
326 |
desk=desk, |
|
327 |
date=datetime.date(2022, 10, 24), |
|
328 |
start_time=datetime.time(14, 0), |
|
329 |
end_time=datetime.time(17, 0), |
|
330 |
) |
|
331 |
set_prefetched_exceptions(desk) |
|
332 |
previous_hours = hours |
|
333 |
hours = desk.get_opening_hours(datetime.date(2022, 10, 24)) |
|
334 |
assert len(hours) == 2 |
|
335 |
assert hours[0] == previous_hours[0] |
|
336 | ||
337 |
assert hours[1].begin.time() == datetime.time(14, 0) |
|
338 |
assert hours[1].end.time() == datetime.time(17, 0) |
|
339 | ||
340 |
# mix with repeating period |
|
341 |
TimePeriod.objects.create( |
|
342 |
desk=desk, |
|
343 |
weekday=0, |
|
344 |
start_time=datetime.time(19, 0), |
|
345 |
end_time=datetime.time(20, 0), |
|
346 |
) |
|
347 |
previous_hours = hours |
|
348 |
hours = desk.get_opening_hours(datetime.date(2022, 10, 24)) |
|
349 |
assert len(hours) == 3 |
|
350 |
assert hours[:2] == previous_hours[:2] |
|
351 | ||
352 |
assert hours[2].begin.time() == datetime.time(19, 0) |
|
353 |
assert hours[2].end.time() == datetime.time(20, 0) |
|
354 | ||
355 |
# full day exception |
|
356 |
TimePeriodException.objects.create( |
|
357 |
desk=desk, |
|
358 |
start_datetime=make_aware(datetime.datetime(2022, 10, 24)), |
|
359 |
end_datetime=make_aware(datetime.datetime(2022, 10, 25)), |
|
360 |
) |
|
361 | ||
362 |
set_prefetched_exceptions(desk) |
|
363 |
hours = desk.get_opening_hours(datetime.date(2022, 10, 24)) |
|
364 |
assert len(hours) == 0 |
|
365 | ||
366 |
# next week |
|
367 |
hours = desk.get_opening_hours(datetime.date(2022, 10, 31)) |
|
368 |
assert len(hours) == 1 |
|
369 |
assert hours[0].begin.time() == datetime.time(19, 0) |
|
370 |
assert hours[0].end.time() == datetime.time(20, 0) |
|
371 | ||
372 | ||
302 | 373 |
def test_timeperiod_midnight_overlap_time_slots(): |
303 | 374 |
# https://dev.entrouvert.org/issues/29142 |
304 | 375 |
agenda = Agenda(label='Foo bar', slug='bar') |
305 |
- |