manager: day and month view for virtual agenda (#40892)

This commit is contained in:
Lauréline Guérin 2020-09-22 10:28:49 +02:00
parent 43e11b6f87
commit 66b71a6d84
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
5 changed files with 345 additions and 148 deletions

View File

@ -41,7 +41,7 @@
<tr>
<td></td>
{% for desk_info in desk_infos %}
<th>{{ desk_info.desk.label }}</th>
<th>{{ desk_info.desk.label }}{% if agenda.kind == 'virtual' %} ({{ desk_info.desk.agenda.label }}){% endif %}</th>
{% endfor %}
</tr>
</thead>
@ -72,7 +72,7 @@
style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
><span class="start-time">{{booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
<a {% if booking.backoffice_url %}href="{{booking.backoffice_url}}"{% endif %}>{{ booking.meetings_display }}</a>
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=agenda.id booking_pk=booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=booking.event.agenda_id booking_pk=booking.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
</div>
{% endfor %}
</td>

View File

@ -37,7 +37,7 @@
<div class="booking" style="left:{{ slot.css_left|stringformat:".1f" }}%;height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%">
<span class="start-time">{{slot.booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
<a {% if slot.booking.backoffice_url %}href="{{slot.booking.backoffice_url}}"{% endif %}>{{ slot.booking.meetings_display }}</a>
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=agenda.id booking_pk=slot.booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=slot.booking.event.agenda_id booking_pk=slot.booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
{% if not single_desk %}<span class="desk">{{ slot.desk }}</span>{% endif %}
</div>
{% endfor %}

View File

@ -1,25 +0,0 @@
{% extends "chrono/manager_agenda_view.html" %}
{% load i18n %}
{% block content %}
<div class="section">
<h3>{% trans 'Included Agendas' %}</h3>
<div>
{% if real_agendas %}
<ul class="objects-list single-links">
{% for real_agenda, can_be_viewed in real_agendas %}
<li><a {% if can_be_viewed %}href="{% url 'chrono-manager-agenda-view' pk=real_agenda.pk %}"{% endif %}>
{{real_agenda.label}}
</a>
{% endfor %}
</ul>
{% else %}
<div class="big-msg-info">
{% blocktrans %}
This virtual agenda is empty.
{% endblocktrans %}
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -23,13 +23,12 @@ import uuid
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.db.models import Q, F
from django.db.models import Q
from django.db.models import Min, Max
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.template.response import TemplateResponse
from django.urls import reverse, reverse_lazy
from django.utils.dates import MONTHS
from django.utils.timezone import now, make_aware, make_naive, localtime
@ -717,8 +716,8 @@ agenda_delete = AgendaDeleteView.as_view()
class AgendaView(ViewableAgendaMixin, View):
def get(self, request, *args, **kwargs):
if self.agenda.kind == 'meetings':
# redirect to today view
if self.agenda.kind != 'events':
# meetings or virtual - redirect to today view
today = datetime.date.today()
return redirect(
'chrono-manager-agenda-day-view',
@ -728,26 +727,10 @@ class AgendaView(ViewableAgendaMixin, View):
day=today.day,
)
if self.agenda.kind == 'events':
# redirect to month or open events view
if self.agenda.default_view == 'month':
return redirect('chrono-manager-agenda-month-redirect-view', pk=self.agenda.pk)
return redirect('chrono-manager-agenda-open-events-view', pk=self.agenda.pk)
# virtual agenda
real_agendas = [
(agenda, agenda.can_be_viewed(request.user)) for agenda in self.agenda.real_agendas.all()
]
return TemplateResponse(
request=request,
template='chrono/manager_virtual_agenda_view.html',
context={
'real_agendas': real_agendas,
'agenda': self.agenda,
'object': self.agenda,
'user_can_manage': self.agenda.can_be_managed(self.request.user),
},
)
# events agenda - redirect to month or open events view
if self.agenda.default_view == 'month':
return redirect('chrono-manager-agenda-month-redirect-view', pk=self.agenda.pk)
return redirect('chrono-manager-agenda-open-events-view', pk=self.agenda.pk)
agenda_view = AgendaView.as_view()
@ -811,7 +794,7 @@ class AgendaDateView(DateMixin, ViewableAgendaMixin):
def get_context_data(self, **kwargs):
context = super(AgendaDateView, self).get_context_data(**kwargs)
context['agenda'] = self.agenda
if self.agenda.kind == 'meetings':
if self.agenda.kind != 'events':
try:
context['hour_span'] = max(60 // self.agenda.get_base_meeting_duration(), 1)
except ValueError: # no meeting types defined
@ -820,13 +803,25 @@ class AgendaDateView(DateMixin, ViewableAgendaMixin):
return context
def get_queryset(self):
if self.agenda.kind == 'meetings':
if self.agenda.kind == 'events':
queryset = self.agenda.event_set.all()
elif self.agenda.kind == 'meetings':
self.agenda.prefetched_desks = self.agenda.desk_set.all().prefetch_related(
'timeperiod_set', 'timeperiodexception_set'
)
queryset = self.agenda.event_set.all()
if self.agenda.kind == 'meetings':
queryset = queryset.select_related('meeting_type').prefetch_related('booking_set')
queryset = self.agenda.event_set.select_related('meeting_type').prefetch_related('booking_set')
else:
self.agenda.prefetched_desks = (
Desk.objects.filter(agenda__virtual_agendas=self.agenda)
.prefetch_related('timeperiod_set', 'timeperiodexception_set')
.select_related('agenda')
.order_by('agenda', 'label')
)
queryset = (
Event.objects.filter(agenda__virtual_agendas=self.agenda)
.select_related('meeting_type')
.prefetch_related('booking_set')
)
return queryset
@ -835,7 +830,7 @@ class AgendaDayView(AgendaDateView, DayArchiveView):
def set_agenda(self, **kwargs):
# day view should only exist for meetings kind.
self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'), kind='meetings')
self.agenda = get_object_or_404(Agenda.objects.exclude(kind='events'), pk=kwargs.get('pk'))
def get_previous_day_url(self):
previous_day = self.date.date() - datetime.timedelta(days=1)
@ -948,21 +943,23 @@ agenda_day_view = AgendaDayView.as_view()
class AgendaMonthView(AgendaDateView, MonthArchiveView):
def get_queryset(self):
qs = super(AgendaMonthView, self).get_queryset()
if self.agenda.kind == 'meetings':
if self.agenda.kind != 'events':
return qs
return Event.annotate_queryset(qs).order_by('start_datetime', 'label')
def get_template_names(self):
if self.agenda.kind == 'virtual':
return ['chrono/manager_meetings_agenda_month_view.html']
return ['chrono/manager_%s_agenda_month_view.html' % self.agenda.kind]
def get_context_data(self, **kwargs):
context = super(AgendaMonthView, self).get_context_data(**kwargs)
if self.agenda.kind == 'meetings':
context['single_desk'] = bool(self.agenda.prefetched_desks)
elif self.agenda.kind == 'events':
if self.agenda.kind == 'events':
context['cancellation_reports'] = EventCancellationReport.objects.filter(
event__agenda=self.agenda, seen=False,
).all()
else:
context['single_desk'] = bool(self.agenda.prefetched_desks)
return context
def get_previous_month_url(self):

View File

@ -225,11 +225,13 @@ def test_meetings_agenda_redirect(app, admin_user):
assert resp.location.endswith('/manage/agendas/%s/2020/7/12/' % agenda.pk)
@freezegun.freeze_time('2020-07-12')
def test_virtual_agenda_redirect(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='virtual')
app = login(app)
app.get('/manage/agendas/%s/' % agenda.pk) # no redirection
resp = app.get('/manage/agendas/%s/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/7/12/' % agenda.pk)
def test_view_agendas_as_admin(app, admin_user):
@ -267,9 +269,13 @@ def test_view_agendas_as_manager(app, manager_user):
agenda.kind = 'meetings'
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=403)
# or virtual agenda
agenda.kind = 'virtual'
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=403)
# check it gives a 404 on unknown agendas
resp = app.get('/manage/agendas/%s/settings' % '9999', status=404)
resp = app.get('/manage/agendas/0/settings', status=404)
def test_list_resources_as_manager(app, manager_user):
@ -2660,20 +2666,22 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user):
assert resp.pyquery.find('.exception-hours span')[0].text == 'Exception for the afternoon'
def test_agenda_day_view_late_meeting(app, admin_user, manager_user, api_user):
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')
desk.save()
meetingtype = MeetingType(agenda=agenda, label='Bar', duration=30)
meetingtype.save()
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_day_view_late_meeting(app, admin_user, kind):
today = datetime.date.today()
timeperiod = TimePeriod(
if kind == 'meetings':
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')
MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
MeetingType.objects.create(agenda=real_agenda, label='Bar', duration=30)
TimePeriod.objects.create(
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(23, 30)
)
timeperiod.save()
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/%s/' % (agenda.pk, today.year, today.month, today.day))
@ -2681,24 +2689,39 @@ def test_agenda_day_view_late_meeting(app, admin_user, manager_user, api_user):
assert '<th class="hour">11 p.m.</th>' in resp.text
def test_agenda_invalid_day_view(app, admin_user, manager_user, api_user):
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')
desk.save()
meetingtype = MeetingType(agenda=agenda, label='Bar', duration=30)
meetingtype.save()
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_invalid_day_view(app, admin_user, kind):
if kind == 'meetings':
agenda = Agenda.objects.create(label='New Example', kind='meetings')
Desk.objects.create(agenda=agenda, label='New Desk')
MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
Desk.objects.create(agenda=real_agenda, label='New Desk')
MeetingType.objects.create(agenda=real_agenda, label='Bar', duration=30)
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, 2018, 11, 31), status=302)
assert resp.location.endswith('2018/11/30/')
def test_agenda_day_view_event_outside_timeperiod(app, admin_user):
today = datetime.date.today()
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_day_view_event_outside_timeperiod(app, admin_user, kind):
if kind == 'meetings':
today = datetime.date.today()
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
else:
today = datetime.date.today()
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=real_agenda, label='Bar', duration=30)
login(app)
# no time period - no events
@ -2709,7 +2732,7 @@ def test_agenda_day_view_event_outside_timeperiod(app, admin_user):
# book some slots
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=agenda,
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
@ -2933,19 +2956,26 @@ def test_agenda_month_view(app, admin_user, manager_user, api_user):
}
def test_agenda_month_view_weekend(app, admin_user, manager_user, api_user):
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_month_view_weekend(app, admin_user, kind):
month, year = 1, 2019
monday = 0
timeperiod = TimePeriod.objects.create(
if kind == 'meetings':
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
TimePeriod.objects.create(
desk=desk, weekday=monday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, year, month))
assert not 'Sunday' in resp.text
assert not 'Saturday' in resp.text
assert 'Sunday' not in resp.text
assert 'Saturday' not in resp.text
# No Monday on first row since month starts a Tuesday
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 1
@ -2963,12 +2993,12 @@ def test_agenda_month_view_weekend(app, admin_user, manager_user, api_user):
desk=desk, weekday=saturday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, year, month))
assert not 'Sunday' in resp.text
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 5
sunday = 6
timeperiod = TimePeriod.objects.create(
TimePeriod.objects.create(
desk=desk, weekday=sunday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, year, month))
@ -2981,17 +3011,23 @@ def test_agenda_month_view_weekend(app, admin_user, manager_user, api_user):
assert 'Saturday' in resp.text
def test_agenda_month_view_dst_change(app, admin_user, manager_user, api_user):
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
meetingtype = MeetingType(agenda=agenda, label='passeport', duration=20)
meetingtype.save()
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_month_view_dst_change(app, admin_user, kind):
if kind == 'meetings':
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
meetingtype = MeetingType.objects.create(agenda=agenda, label='passeport', duration=20)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=real_agenda, label='passeport', duration=20)
for weekday in range(0, 7): # open all mornings
TimePeriod(
TimePeriod.objects.create(
desk=desk, weekday=weekday, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
).save()
)
login(app)
for date in ('2019-10-01', '2019-10-31'):
@ -3001,14 +3037,15 @@ def test_agenda_month_view_dst_change(app, admin_user, manager_user, api_user):
assert resp.text.count('height:300.0%;top:0.0%') == 31
# book some slots
app.reset()
app.authorization = ('Basic', ('john.doe', 'password'))
with freezegun.freeze_time('2019-10-01'):
resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
booking_url = resp.json['data'][3]['api']['fillslot_url'] # 2019-10-02 10:00:00
booking_url2 = resp.json['data'][246]['api']['fillslot_url'] # 2019-10-29 10:00:00
resp = app.post(booking_url)
resp = app.post_json(booking_url2)
for date_tuple in [(2019, 10, 2, 10, 0), (2019, 10, 29, 10, 0)]:
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(make_aware(datetime.datetime(*date_tuple))),
)
Booking.objects.create(event=event)
# check booked slots are similarly aligned
login(app)
@ -3016,11 +3053,18 @@ def test_agenda_month_view_dst_change(app, admin_user, manager_user, api_user):
assert resp.text.count('height:33.0%;top:100.0%;') == 2
def test_agenda_month_view_januaries(app, admin_user, manager_user, api_user):
agenda = Agenda.objects.create(label='Passports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
meetingtype = MeetingType(agenda=agenda, label='passport', duration=20)
meetingtype.save()
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_month_view_januaries(app, admin_user, kind):
if kind == 'meetings':
agenda = Agenda.objects.create(label='Passports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType.objects.create(agenda=agenda, label='passport', duration=20)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
MeetingType.objects.create(agenda=real_agenda, label='passport', duration=20)
TimePeriod(desk=desk, weekday=2, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)).save()
for year in range(2020, 2030):
@ -3031,11 +3075,19 @@ def test_agenda_month_view_januaries(app, admin_user, manager_user, api_user):
assert resp.text.count('<th></th>') in (4, 5)
def test_agenda_month_view_event_outside_timeperiod(app, admin_user):
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_month_view_event_outside_timeperiod(app, admin_user, kind):
today = datetime.date.today()
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
if kind == 'meetings':
agenda = Agenda.objects.create(label='New Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=real_agenda, label='passport', duration=20)
login(app)
# no time period - no events
@ -3047,7 +3099,7 @@ def test_agenda_month_view_event_outside_timeperiod(app, admin_user):
middle_day = now().replace(day=15)
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=agenda,
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
@ -3080,7 +3132,7 @@ def test_agenda_month_view_event_outside_timeperiod(app, admin_user):
# create an event on saturday
event = Event.objects.create(
agenda=agenda,
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
@ -3110,7 +3162,7 @@ def test_agenda_month_view_event_outside_timeperiod(app, admin_user):
# create an event on sunday
middle_day = now().replace(day=15)
event = Event.objects.create(
agenda=agenda,
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
@ -3351,33 +3403,206 @@ def test_virtual_agenda_add(app, admin_user):
assert agenda.maximal_booking_delay is None
def test_virtual_agenda_baseview_empty(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
app = login(app)
resp = app.get(agenda.get_absolute_url())
assert 'Settings' in resp.text
assert 'My Virtual agenda' in resp.text
assert 'Included Agendas' in resp.text
assert 'This virtual agenda is empty.' in resp.text
assert '/manage/agendas/%s/settings' % agenda.pk in resp.text
def test_virtual_agenda_day_view(app, admin_user, manager_user):
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda_1 = Agenda.objects.create(label='Real 1', kind='meetings')
real_agenda_2 = Agenda.objects.create(label='Real 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda_1)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda_2)
desk1 = Desk.objects.create(agenda=real_agenda_1, label='New Desk')
desk2 = Desk.objects.create(agenda=real_agenda_2, label='New Desk')
today = datetime.date.today()
meetingtype1 = MeetingType.objects.create(agenda=real_agenda_1, label='Bar', duration=30)
meetingtype2 = MeetingType.objects.create(agenda=real_agenda_2, label='Bar', duration=30)
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/%s/' % (agenda.pk, today.year, today.month, today.day))
assert 'No opening hours this day.' in resp.text # no time pediod
timeperiod = TimePeriod.objects.create(
desk=desk1, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/agendas/%s/%s/%s/%s/' % (agenda.pk, today.year, today.month, today.day))
assert 'No opening hours this day.' not in resp.text
assert 'div class="booking' not in resp.text
assert resp.text.count('<tr') == 9 # 10->18 (not included)
timeperiod.end_time = datetime.time(18, 30) # end during an hour
timeperiod.save()
resp = app.get('/manage/agendas/%s/%s/%s/%s/' % (agenda.pk, today.year, today.month, today.day))
assert resp.text.count('<tr') == 10 # 10->18 (included)
# check opening hours cells
assert '<div class="opening-hours"' in resp.text
assert 'style="height: 850%; top: 0%;"' in resp.text
# book some slots
for hour, minute in [(10, 30), (14, 0)]:
event = Event.objects.create(
agenda=real_agenda_1,
places=1,
desk=desk1,
meeting_type=meetingtype1,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
event = Event.objects.create(
agenda=real_agenda_2,
places=1,
desk=desk2,
meeting_type=meetingtype2,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.text.count('div class="booking') == 4
assert 'hourspan-2' in resp.text # table CSS class
assert 'height: 50%; top: 0%;' in resp.text # booking cells
# create a shorter meeting type, this will change the table CSS class
# (and visually this will give more room for events)
MeetingType.objects.create(agenda=real_agenda_1, label='Baz', duration=15)
MeetingType.objects.create(agenda=real_agenda_2, label='Baz', duration=15)
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.text.count('div class="booking') == 4
assert 'hourspan-4' in resp.text # table CSS class
# cancel a booking
booking = Booking.objects.first()
booking.cancel()
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.text.count('div class="booking') == 3
# not enough permissions
app.reset()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=403)
# just enough permissions
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200)
# display exception
TimePeriodException.objects.create(
label='Exception for the afternoon',
desk=desk1,
start_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 13, 0)),
end_datetime=make_aware(datetime.datetime(date.year, date.month, date.day, 23, 0)),
)
with CaptureQueriesContext(connection) as ctx:
resp = app.get(
'/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200
)
assert len(ctx.captured_queries) == 15
# day is displaying rows from 10am to 6pm,
# opening hours, 10am to 1pm gives top: 300%
# rest of the day, 1pm to 6(+1)pm gives 600%
assert resp.pyquery.find('.exception-hours')[0].attrib == {
'class': 'exception-hours',
'style': 'height: 600%; top: 300%;',
}
assert resp.pyquery.find('.exception-hours span')[0].text == 'Exception for the afternoon'
def test_virtual_agenda_baseview(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
def test_virtual_agenda_month_view(app, admin_user):
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda_1 = Agenda.objects.create(label='Real 1', kind='meetings')
real_agenda_2 = Agenda.objects.create(label='Real 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda_1)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda_2)
app = login(app)
resp = app.get(agenda.get_absolute_url())
assert 'Settings' in resp.text
assert 'My Virtual agenda' in resp.text
assert 'Included Agendas' in resp.text
assert 'This virtual agenda is empty.' not in resp.text
for real_agenda in [meeting_agenda_1, meeting_agenda_2]:
assert real_agenda.label in resp.text
assert real_agenda.get_absolute_url() in resp.text
desk1 = Desk.objects.create(agenda=real_agenda_1, label='New Desk')
desk2 = Desk.objects.create(agenda=real_agenda_2, label='New Desk')
today = datetime.date.today()
meetingtype1 = MeetingType.objects.create(agenda=real_agenda_1, label='Bar', duration=30)
meetingtype2 = MeetingType.objects.create(agenda=real_agenda_2, label='Bar', duration=30)
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert 'Month view' in resp.text
resp = resp.click('Month view')
assert resp.request.url.endswith('%s/%s/' % (today.year, today.month))
assert 'Day view' in resp.text # date view link should be present
assert 'No opening hours this month.' in resp.text
today = datetime.date(2018, 11, 10) # fixed day
timeperiod_weekday = today.weekday()
TimePeriod.objects.create(
desk=desk1, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
assert 'No opening hours this month.' not in resp.text
assert '<div class="booking' not in resp.text
first_month_day = today.replace(day=1)
last_month_day = today.replace(day=1, month=today.month + 1) - datetime.timedelta(days=1)
start_week_number = first_month_day.isocalendar()[1]
end_week_number = last_month_day.isocalendar()[1]
weeks_number = end_week_number - start_week_number + 1
assert resp.text.count('<tr') == 9 * weeks_number
# check opening hours cells
assert '<div class="opening-hours" style="height:800.0%;top:0.0%;width:48.0%;left:1.0%' in resp.text
# book some slots
for hour, minute in [(10, 30), (14, 0)]:
event = Event.objects.create(
agenda=real_agenda_1,
places=1,
desk=desk1,
meeting_type=meetingtype1,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
event = Event.objects.create(
agenda=real_agenda_2,
places=1,
desk=desk2,
meeting_type=meetingtype2,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
assert resp.text.count('<div class="booking" style="left:1.0%;height:50.0%;') == 2 # booking cells
assert (
resp.text.count('<div class="booking" style="left:50.0%;height:50.0%;min-height:50.0%;') == 2
) # booking cells
# cancel a booking
booking = Booking.objects.first()
booking.cancel()
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, date.year, date.month))
assert resp.text.count('<div class="booking"') == 3
# check December is correctly displayed
today = datetime.date(2018, 12, 10)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
assert 'No opening hours this month.' not in resp.text
# display exception
TimePeriodException.objects.create(
label='Exception for a December day',
desk=desk1,
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 5, 0)),
end_datetime=make_aware(datetime.datetime(2018, 12, 15, 23, 0)),
)
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
assert len(ctx.captured_queries) == 10
assert resp.pyquery.find('.exception-hours')[0].attrib == {
'class': 'exception-hours',
'style': 'height:800.0%;top:0.0%;width:48.0%;left:1.0%;',
'title': 'Exception for a December day',
}
def test_virtual_agenda_settings_empty(app, admin_user):