0001-manager-display-exceptions-in-day-month-views-39906.patch
chrono/manager/static/css/style.scss | ||
---|---|---|
172 | 172 |
padding: 1ex; |
173 | 173 |
position: absolute; |
174 | 174 |
overflow: hidden; |
175 |
&.opening-hours { |
|
175 |
&.opening-hours, &.exception-hours {
|
|
176 | 176 |
z-index: 1; |
177 | 177 |
background: #b1ea4d linear-gradient(135deg, #b1ea4d 0%, #459522 100%); |
178 | 178 |
opacity: 0.6; |
179 | 179 |
left: 0.5ex; |
180 | 180 |
width: calc(100% - 1ex); |
181 | 181 |
} |
182 |
&.exception-hours { |
|
183 |
background: #fee linear-gradient(135deg, #fee 0%, #fdd 100%); |
|
184 |
text-align: center; |
|
185 |
} |
|
182 | 186 |
&.booking { |
183 | 187 |
background: #eef linear-gradient(135deg, #eef 0%, #ddf 100%); |
184 | 188 |
box-shadow: 0 0 1px 0 #2d2dad; |
chrono/manager/templates/chrono/manager_agenda_day_view.html | ||
---|---|---|
60 | 60 |
style="height: {{ slot.css_height }}%; top: {{ slot.css_top }}%;" |
61 | 61 |
>{{slot.begin}} {{slot.end}}</div> |
62 | 62 |
{% endfor %} |
63 |
{% for slot in desk_info.exceptions %} |
|
64 |
<div class="exception-hours" |
|
65 |
style="height: {{ slot.css_height }}%; top: {{ slot.css_top }}%;" |
|
66 |
>{% if slot.label %}<span class="exception-label">{{slot.label}}</span>{% endif %}</div> |
|
67 |
{% endfor %} |
|
63 | 68 |
{% endif %} |
64 | 69 | |
65 | 70 |
{% for booking in desk_info.bookings %} |
chrono/manager/templates/chrono/manager_meetings_agenda_month_view.html | ||
---|---|---|
26 | 26 |
{% for slot in day.infos.opening_hours %} |
27 | 27 |
<div class="opening-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;"></div> |
28 | 28 |
{% endfor %} |
29 | ||
30 |
{% for slot in day.infos.exceptions %} |
|
31 |
<div class="exception-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;" {% if slot.label %}title="{{ slot.label }}"{% endif %}></div> |
|
32 |
{% endfor %} |
|
33 | ||
29 | 34 |
{% for slot in day.infos.booked_slots %} |
30 | 35 |
<div class="booking" style="left:{{ slot.css_left|stringformat:".1f" }}%;height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%"> |
31 | 36 |
<span class="start-time">{{slot.booking.event.start_datetime|date:"TIME_FORMAT"}}</span> |
chrono/manager/views.py | ||
---|---|---|
376 | 376 |
# until the end of the last hour. |
377 | 377 |
max_date += datetime.timedelta(hours=1) |
378 | 378 | |
379 |
desks = self.agenda.desk_set.all() |
|
379 |
desks = self.agenda.desk_set.all().prefetch_related('timeperiodexception_set')
|
|
380 | 380 | |
381 | 381 |
first = True |
382 | 382 |
while current_date < max_date: |
... | ... | |
395 | 395 |
'css_height': 100 * (opening_hour.end - opening_hour.begin).seconds // 3600, |
396 | 396 |
} |
397 | 397 |
) |
398 | ||
399 |
# and exceptions for this desk |
|
400 |
info['exceptions'] = [] |
|
401 |
for exception in desk.timeperiodexception_set.all(): |
|
402 |
if exception.end_datetime < start_date: |
|
403 |
continue |
|
404 |
if exception.start_datetime > max_date: |
|
405 |
continue |
|
406 |
start_max = max(start_date, exception.start_datetime) |
|
407 |
end_min = min(max_date, exception.end_datetime) |
|
408 |
exception.css_top = int(100 * (start_max - start_date).seconds // 3600) |
|
409 |
exception.css_height = int(100 * (end_min - start_max).seconds // 3600) |
|
410 |
info['exceptions'].append(exception) |
|
411 | ||
398 | 412 |
infos.append(info) |
399 | 413 |
info['bookings'] = bookings = [] # bookings for this desk |
400 | 414 |
finish_datetime = current_date + interval |
... | ... | |
507 | 521 |
'date': current_date, |
508 | 522 |
'today': day.date() == datetime.date.today(), |
509 | 523 |
'other_month': day.month != self.date.month, |
510 |
'infos': {'opening_hours': [], 'booked_slots': []}, |
|
524 |
'infos': {'opening_hours': [], 'exceptions': [], 'booked_slots': []},
|
|
511 | 525 |
} |
512 | 526 | |
513 |
desks = self.agenda.desk_set.all() |
|
527 |
desks = self.agenda.desk_set.all().prefetch_related('timeperiodexception_set')
|
|
514 | 528 |
desks_len = len(desks) |
515 | 529 |
max_date = day.replace(hour=self.max_timeperiod.hour, minute=0) |
516 | 530 | |
... | ... | |
553 | 567 |
'css_left': left, |
554 | 568 |
} |
555 | 569 |
) |
570 |
for exception in desk.timeperiodexception_set.all(): |
|
571 |
if exception.end_datetime < current_date: |
|
572 |
continue |
|
573 |
if exception.start_datetime > max_date: |
|
574 |
continue |
|
575 |
start_max = max(current_date, exception.start_datetime) |
|
576 |
end_min = min(max_date, exception.end_datetime) |
|
577 |
exception.css_top = int(100 * (start_max - current_date).seconds // 3600) |
|
578 |
exception.css_height = int(100 * (end_min - current_date).seconds // 3600) |
|
579 |
exception.css_width = width |
|
580 |
exception.css_left = left |
|
581 |
timetable['infos']['exceptions'].append(exception) |
|
582 | ||
556 | 583 |
left += width + 1 |
557 | 584 |
period += interval |
558 | 585 |
tests/test_manager.py | ||
---|---|---|
1704 | 1704 |
agenda.save() |
1705 | 1705 |
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200) |
1706 | 1706 | |
1707 |
# display exception |
|
1708 |
TimePeriodException.objects.create( |
|
1709 |
label='Exception for the afternoon', |
|
1710 |
desk=desk, |
|
1711 |
start_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 13, 0)), |
|
1712 |
end_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 23, 0)), |
|
1713 |
) |
|
1714 |
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200) |
|
1715 |
# day is displaying rows from 10am to 6pm, |
|
1716 |
# opening hours, 10am to 1pm gives top: 300% |
|
1717 |
# rest of the day, 1pm to 6(+1)pm gives 600% |
|
1718 |
assert 'height: 600%; top: 300%' in resp.text |
|
1719 |
assert 'Exception for the afternoon' in resp.text |
|
1720 | ||
1707 | 1721 | |
1708 | 1722 |
def test_agenda_day_view_late_meeting(app, admin_user, manager_user, api_user): |
1709 | 1723 |
agenda = Agenda.objects.create(label='New Example', kind='meetings') |
... | ... | |
1868 | 1882 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month)) |
1869 | 1883 |
assert not 'No opening hours this month.' in resp.text |
1870 | 1884 | |
1885 |
# display exception |
|
1886 |
TimePeriodException.objects.create( |
|
1887 |
label='Exception for a December day', |
|
1888 |
desk=desk, |
|
1889 |
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 5, 0)), |
|
1890 |
end_datetime=make_aware(datetime.datetime(2018, 12, 15, 23, 0)), |
|
1891 |
) |
|
1892 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month)) |
|
1893 |
assert 'height:800.0%;top:0.0%;width:48.0%;left:50.0%;' in resp.text |
|
1894 |
assert 'Exception for a December day' in resp.text |
|
1895 | ||
1871 | 1896 | |
1872 | 1897 |
def test_agenda_month_view_weekend(app, admin_user, manager_user, api_user): |
1873 | 1898 |
agenda = Agenda.objects.create(label='Passeports', kind='meetings') |
1874 |
- |