chrono/tests/test_manager.py

4876 lines
199 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import codecs
import copy
import datetime
import json
import mock
import os
from django.contrib.auth.models import User, Group
from django.core.management import call_command
from django.db import connection
from django.test import override_settings
from django.test.utils import CaptureQueriesContext
from django.urls import reverse
from django.utils.encoding import force_text
from django.utils.timezone import make_aware, now, localtime
import freezegun
import pytest
import requests
from webtest import Upload
from chrono.agendas.models import (
Agenda,
Booking,
Category,
Desk,
Event,
MeetingType,
Resource,
TimePeriod,
TimePeriodException,
TimePeriodExceptionSource,
VirtualMember,
AgendaReminderSettings,
UnavailabilityCalendar,
)
from chrono.manager.forms import TimePeriodExceptionForm
from chrono.utils.signature import check_query
pytestmark = pytest.mark.django_db
@pytest.fixture
def simple_user():
try:
user = User.objects.get(username='user')
except User.DoesNotExist:
user = User.objects.create_user('user', password='user')
return user
@pytest.fixture
def managers_group():
group, _ = Group.objects.get_or_create(name='Managers')
return group
@pytest.fixture
def manager_user(managers_group):
try:
user = User.objects.get(username='manager')
except User.DoesNotExist:
user = User.objects.create_user('manager', password='manager')
user.groups.set([managers_group])
return user
@pytest.fixture
def admin_user():
try:
user = User.objects.get(username='admin')
except User.DoesNotExist:
user = User.objects.create_superuser('admin', email=None, password='admin')
return user
@pytest.fixture
def api_user():
try:
user = User.objects.get(username='api-user')
except User.DoesNotExist:
user = User.objects.create(
username='john.doe', first_name=u'John', last_name=u'Doe', email='john.doe@example.net'
)
user.set_password('password')
user.save()
return user
def login(app, username='admin', password='admin'):
login_page = app.get('/login/')
login_form = login_page.forms[0]
login_form['username'] = username
login_form['password'] = password
resp = login_form.submit()
assert resp.status_int == 302
return app
def test_unlogged_access(app):
# connect while not being logged in
assert app.get('/manage/', status=302).location.endswith('/login/?next=/manage/')
def test_simple_user_access(app, simple_user):
# connect while being logged as a simple user, access should be forbidden
app = login(app, username='user', password='user')
assert app.get('/manage/', status=403)
def test_manager_user_access(app, manager_user):
# connect while being logged as a manager user, access should be granted if
# there's at least an agenda that is viewable or editable.
app = login(app, username='manager', password='manager')
assert app.get('/manage/', status=403)
agenda = Agenda(label=u'Foo bar')
agenda.save()
assert app.get('/manage/', status=403)
agenda.view_role = manager_user.groups.all()[0]
agenda.edit_role = None
agenda.save()
assert app.get('/manage/', status=200)
agenda.edit_role = manager_user.groups.all()[0]
agenda.view_role = None
agenda.save()
assert app.get('/manage/', status=200)
def test_home_redirect(app):
assert app.get('/', status=302).location.endswith('/manage/')
def test_access(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
assert '<h2>Agendas</h2>' in resp.text
assert "This site doesn't have any agenda yet." in resp.text
def test_logout(app, admin_user):
app = login(app)
app.get('/logout/')
assert app.get('/manage/', status=302).location.endswith('/login/?next=/manage/')
def test_menu_json(app, admin_user):
app = login(app)
resp = app.get('/manage/menu.json', status=200)
assert resp.content_type == 'application/json'
assert resp.json[0]['url'] == 'http://testserver/manage/'
assert resp.json[0]['label'] == 'Agendas'
resp2 = app.get('/manage/menu.json?callback=Q', status=200)
assert resp2.text == 'Q(%s);' % resp.text
assert resp2.content_type == 'application/javascript'
def test_events_agenda_redirect(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='events')
app = login(app)
resp = app.get('/manage/agendas/%s/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/month/' % agenda.pk)
agenda.default_view = 'open_events'
agenda.save()
resp = app.get('/manage/agendas/%s/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/events/open/' % agenda.pk)
@freezegun.freeze_time('2020-07-12')
def test_events_agenda_month_redirect(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='events')
app = login(app)
# no event, redirect to current month
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/7/' % agenda.pk)
# only past events, redirect to last event month
Event.objects.create(
agenda=agenda, places=1, start_datetime=now() - datetime.timedelta(days=60),
)
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/5/' % agenda.pk)
Event.objects.create(
agenda=agenda, places=1, start_datetime=now() - datetime.timedelta(days=30),
)
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/6/' % agenda.pk)
# future events
Event.objects.create(
agenda=agenda, places=1, start_datetime=now() + datetime.timedelta(days=60),
)
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/9/' % agenda.pk)
Event.objects.create(
agenda=agenda, places=1, start_datetime=now() + datetime.timedelta(days=30),
)
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/8/' % agenda.pk)
# wrong kind
agenda.kind = 'virtual'
agenda.save()
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/' % agenda.pk)
agenda.kind = 'meetings'
agenda.save()
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/' % agenda.pk)
@freezegun.freeze_time('2020-07-12')
def test_meetings_agenda_redirect(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='meetings')
app = login(app)
resp = app.get('/manage/agendas/%s/' % agenda.pk, status=302)
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)
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):
Agenda.objects.create(label=u'Bar Foo')
app = login(app)
resp = app.get('/manage/', status=200)
assert 'Bar Foo <span class="identifier">[identifier: bar-foo]</span>' in resp.text
def test_view_agendas_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
agenda2 = Agenda(label=u'Bar Foo')
agenda2.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/', status=200)
assert 'Foo Bar' in resp.text
assert 'Bar Foo <span class="identifier">[identifier: bar-foo]</span>' not in resp.text
assert 'Bar Foo' not in resp.text
assert 'New' not in resp.text
# check user doesn't have access
app.get('/manage/agendas/%s/' % agenda2.id, status=403)
# check there's no access to the settings page for "events" agenda
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=403)
app.get('/manage/agendas/%s/add-event' % agenda.id, status=403)
app.get('/manage/agendas/%s/edit' % agenda.id, status=403)
# check it doesn't give access for "meetings" agenda
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/0/settings', status=404)
def test_list_resources_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
app.get('/manage/resources/', status=403)
resp = app.get('/manage/', status=200)
assert 'Resources' not in resp.text
def test_add_resource(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('Resources')
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp = resp.form.submit()
resource = Resource.objects.latest('pk')
assert resp.location.endswith('/manage/resource/%s/' % resource.pk)
assert resource.label == 'Foo bar'
assert resource.slug == 'foo-bar'
def test_add_resource_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
app.get('/manage/resource/add/', status=403)
def test_view_resource(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='meetings')
resource = Resource.objects.create(label='Resource 1')
app = login(app)
resp = app.get('/manage/resource/%s/' % resource.pk, status=200)
assert '/manage/agendas/%s/settings' % agenda.pk not in resp.text
agenda.resources.add(resource)
resp = app.get('/manage/resource/%s/' % resource.pk, status=200)
assert '/manage/agendas/%s/settings' % agenda.pk in resp.text
def test_view_resource_as_manager(app, manager_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='meetings')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
resource = Resource.objects.create(label='Resource 1')
app = login(app, username='manager', password='manager')
app.get('/manage/resource/%s/' % resource.pk, status=403)
def test_resource_day_view(app, admin_user):
today = datetime.date.today()
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
timeperiod = TimePeriod.objects.create(
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
login(app)
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert 'div class="booking' not in resp.text
assert 'No bookings this day.' in resp.text
agenda.resources.add(resource)
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert 'div class="booking' not in resp.text
assert resp.text.count('<tr') == 8 # 10->18 (not included)
timeperiod.end_time = datetime.time(18, 30) # end during an hour
timeperiod.save()
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert resp.text.count('<tr') == 9 # 10->18 (included)
# book some slots
for hour, minute in [(10, 30), (14, 0)]:
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=now().replace(hour=hour, minute=minute),
)
event.resources.add(resource)
Booking.objects.create(event=event)
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert len(ctx.captured_queries) == 7
assert resp.text.count('div class="booking') == 2
assert 'hourspan-2' in resp.text # table CSS class
assert 'height: 50%; top: 0%;' in resp.text # booking cells
assert 'height: 50%; top: 50%;' 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 = MeetingType.objects.create(agenda=agenda, label='Baz', duration=15)
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert resp.text.count('div class="booking') == 2
assert 'hourspan-4' in resp.text # table CSS class
# cancel a booking
booking = Booking.objects.first()
booking.cancel()
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert resp.text.count('div class="booking') == 1
def test_resource_day_view_late_meeting(app, admin_user):
today = datetime.date.today()
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
MeetingType.objects.create(agenda=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)
)
login(app)
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert resp.text.count('<tr') == 14
assert '<th class="hour">midnight</th>' not in resp.text
assert '<th class="hour">11 p.m.</th>' in resp.text
def test_resource_invalid_day_view(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
login(app)
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, 2018, 11, 31), status=302)
assert resp.location.endswith('2018/11/30/')
def test_resource_day_view_event_outside_timeperiod(app, admin_user):
today = datetime.date.today()
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
login(app)
# no time period - no events
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert 'No bookings this day.' in resp.text
assert 'div class="booking' not in resp.text
# book some slots
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(hour=hour, minute=minute),
)
event.resources.add(resource)
Booking.objects.create(event=event)
# no time period - events are displayed
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert resp.text.count('div class="booking') == 2
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert 'No bookings this day.' in resp.text
assert resp.text.count('div class="booking') == 0
# events outside time period
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(16, 0)
)
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert resp.text.count('div class="booking') == 2
def test_day_view_resource_as_manager(app, manager_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='meetings')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
resource = Resource.objects.create(label='Resource 1')
app = login(app, username='manager', password='manager')
today = datetime.date.today()
app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day), status=403)
@freezegun.freeze_time('2020-06-15')
def test_resource_month_view(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=20)
TimePeriod.objects.create(
desk=desk, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
login(app)
today = datetime.date(2018, 11, 10) # fixed day
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, today.month))
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
# book some slots
for hour, minute in [(10, 30), (14, 0)]:
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=now().replace(hour=hour, minute=minute),
)
event.resources.add(resource)
Booking.objects.create(event=event)
today = datetime.date.today()
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, today.month))
assert len(ctx.captured_queries) == 8
assert resp.text.count('<div class="booking" style="height:33.0%;') == 2 # booking cells
# cancel booking
booking = Booking.objects.first()
booking.cancel()
# make sure the are not
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, today.month))
assert resp.text.count('<div class="booking"') == 1
def test_resource_month_view_weekend(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
monday = 0
TimePeriod.objects.create(
desk=desk, weekday=monday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
month, year = 1, 2019
login(app)
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, year, month))
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
# When weekend is hidden, do not display an empty first week
month, year = 12, 2019 # month starts a Sunday
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, year, month))
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
month, year = 6, 2019 # month starts a Saturday
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, year, month))
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
saturday = 5
timeperiod_sat = TimePeriod.objects.create(
desk=desk, weekday=saturday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, year, month))
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.objects.create(
desk=desk, weekday=sunday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, year, month))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
timeperiod_sat.delete()
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, year, month))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
def test_resource_month_view_dst_change(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
TimePeriod.objects.create(
desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
)
login(app)
# book some slots
with freezegun.freeze_time('2019-10-01'):
for start_datetime in [
make_aware(datetime.datetime(2019, 10, 2, 10, 0)),
make_aware(datetime.datetime(2019, 10, 29, 10, 0)),
]:
event = Event.objects.create(
agenda=agenda, places=1, desk=desk, meeting_type=meetingtype, start_datetime=start_datetime,
)
event.resources.add(resource)
Booking.objects.create(event=event)
# check booked slots are similarly aligned
resp = app.get('/manage/resource/%s/2019/10/' % resource.pk)
assert resp.text.count('height:50.0%;top:100.0%') == 2
def test_resource_month_view_januaries(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
TimePeriod(desk=desk, weekday=2, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)).save()
for year in range(2020, 2030):
date = datetime.date(year, 1, 1)
with freezegun.freeze_time(date):
login(app)
resp = app.get('/manage/resource/%s/%s/1/' % (resource.pk, date.year))
assert resp.text.count('<th></th>') in (4, 5)
def test_resource_month_view_event_outside_timeperiod(app, admin_user):
today = datetime.date.today()
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
login(app)
# no time period - no events
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert 'No bookings this month.' in resp.text
assert 'div class="booking' not in resp.text
# book some slots
middle_day = now().replace(day=15)
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(
day=middle_day.day - middle_day.weekday() + 2, hour=hour, minute=minute
),
)
event.resources.add(resource)
Booking.objects.create(event=event)
# no time period - events are displayed
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 2
# events outside time period
TimePeriod.objects.create(
desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(16, 0)
)
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 2
assert 'Sunday' not in resp.text
assert 'Saturday' not in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 0
# create an event on saturday
Booking.objects.update(cancellation_datetime=None) # reset
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(
day=middle_day.day - middle_day.weekday() + 5, hour=10, minute=0
),
)
event.resources.add(resource)
Booking.objects.create(event=event)
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=5, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# create an event on sunday
middle_day = now().replace(day=15)
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(
day=middle_day.day - middle_day.weekday() + 6, hour=10, minute=0
),
)
event.resources.add(resource)
Booking.objects.create(event=event)
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=6, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
def test_month_view_resource_as_manager(app, manager_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='meetings')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
resource = Resource.objects.create(label='Resource 1')
app = login(app, username='manager', password='manager')
today = datetime.date.today()
app.get('/manage/resource/%s/%d/%d/' % (resource.pk, today.year, today.month), status=403)
def test_edit_resource(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/resource/%s/' % resource.pk, status=200)
resp = resp.click('Edit')
resp.form['label'] = 'Foo bar baz'
resp.form['slug'] = 'baz'
resp = resp.form.submit()
assert resp.location.endswith('/manage/resource/%s/' % resource.pk)
resource.refresh_from_db()
assert resource.label == 'Foo bar baz'
assert resource.slug == 'baz'
def test_edit_resource_as_manager(app, manager_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='meetings')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
resource = Resource.objects.create(label='Resource 1')
app = login(app, username='manager', password='manager')
app.get('/manage/resource/%s/edit/' % resource.pk, status=403)
def test_delete_resource(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/resource/%s/' % resource.pk, status=200)
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/manage/resources/')
assert Resource.objects.exists() is False
def test_delete_resource_as_manager(app, manager_user):
agenda = Agenda.objects.create(label=u'Foo Bar', kind='meetings')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
resource = Resource.objects.create(label='Resource 1')
app = login(app, username='manager', password='manager')
app.get('/manage/resource/%s/delete/' % resource.pk, status=403)
def test_list_categories_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
app.get('/manage/categories/', status=403)
resp = app.get('/manage/', status=200)
assert 'Categories' not in resp.text
def test_add_category(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('Categories')
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp = resp.form.submit()
category = Category.objects.latest('pk')
assert resp.location.endswith('/manage/categories/')
assert category.label == 'Foo bar'
assert category.slug == 'foo-bar'
def test_add_category_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
app.get('/manage/category/add/', status=403)
def test_edit_category(app, admin_user):
category = Category.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/categories/', status=200)
resp = resp.click(href='/manage/category/%s/edit/' % category.pk)
resp.form['label'] = 'Foo bar baz'
resp.form['slug'] = 'baz'
resp = resp.form.submit()
assert resp.location.endswith('/manage/categories/')
category.refresh_from_db()
assert category.label == 'Foo bar baz'
assert category.slug == 'baz'
def test_edit_category_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
category = Category.objects.create(label='Foo bar')
app = login(app, username='manager', password='manager')
app.get('/manage/category/%s/edit/' % category.pk, status=403)
def test_delete_category(app, admin_user):
category = Category.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/categories/', status=200)
resp = resp.click(href='/manage/category/%s/delete/' % category.pk)
resp = resp.form.submit()
assert resp.location.endswith('/manage/categories/')
assert Category.objects.exists() is False
def test_delete_category_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
category = Category.objects.create(label='Foo bar')
app = login(app, username='manager', password='manager')
app.get('/manage/category/%s/delete/' % category.pk, status=403)
def test_add_agenda(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp = resp.form.submit()
agenda = Agenda.objects.get(label='Foo bar')
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
resp = resp.follow()
assert 'Foo bar' in resp.text
assert '<h2>Settings' in resp.text
assert agenda.minimal_booking_delay == 1
assert agenda.maximal_booking_delay == 56
def test_add_agenda_as_manager(app, manager_user):
# open /manage/ access to manager_user, and check agenda creation is not
# allowed.
agenda = Agenda(label='Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
app.get('/manage/agendas/add/', status=403)
def test_options_agenda(app, admin_user):
agenda_events = Agenda.objects.create(label=u'Foo bar', kind='events')
agenda_meetings = Agenda.objects.create(label=u'Foo bar', kind='meetings')
agenda_virtual = Agenda.objects.create(label=u'Foo bar', kind='virtual')
app = login(app)
resp = app.get('/manage/agendas/%s/edit' % agenda_events.pk)
assert resp.form['label'].value == 'Foo bar'
resp.form['label'] = 'Foo baz'
resp.form['anonymize_delay'] = 365
assert 'default_view' in resp.context['form'].fields
assert 'booking_form_url' in resp.context['form'].fields
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda_events.pk)
resp = resp.follow()
assert 'has_resources' not in resp.context
assert 'Foo baz' in resp.text
assert '<h2>Settings' in resp.text
agenda_events.refresh_from_db()
assert agenda_events.anonymize_delay == 365
resp = app.get('/manage/agendas/%s/edit' % agenda_meetings.pk)
assert 'default_view' not in resp.context['form'].fields
assert 'booking_form_url' not in resp.context['form'].fields
resp = app.get('/manage/agendas/%s/edit' % agenda_virtual.pk)
assert 'default_view' not in resp.context['form'].fields
assert 'booking_form_url' not in resp.context['form'].fields
def test_options_agenda_cant_unset_delays(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar')
assert agenda.minimal_booking_delay == 1
app = login(app)
url = '/manage/agendas/%s/edit' % agenda.pk
resp = app.get(url)
resp.form['minimal_booking_delay'] = None
resp = resp.form.submit()
agenda = Agenda.objects.get(label=u'Foo bar')
assert agenda.minimal_booking_delay == 1
def test_options_virtuale_agenda_can_unset_delays(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='virtual', maximal_booking_delay=2)
assert agenda.maximal_booking_delay == 2
app = login(app)
url = '/manage/agendas/%s/edit' % agenda.pk
resp = app.get(url)
resp.form['maximal_booking_delay'] = None
resp = resp.form.submit()
agenda = Agenda.objects.get(label=u'Foo bar')
assert agenda.maximal_booking_delay is None
def test_options_agenda_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/', status=200)
resp = resp.click('Foo bar').follow().follow()
assert 'Settings' not in resp.text
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=403)
resp = app.get('/manage/agendas/%s/edit' % agenda.id, status=403)
agenda.kind = 'meetings'
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=403)
resp = app.get('/manage/agendas/%s/edit' % agenda.id, status=403)
agenda.kind = 'events'
agenda.save()
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Options')
assert resp.form['label'].value == 'Foo bar'
resp.form['label'] = 'Foo baz'
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
resp = resp.follow()
assert 'Foo baz' in resp.text
assert '<h2>Settings' in resp.text
def test_options_meetings_agenda_num_queries(app, admin_user):
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
for i in range(0, 10):
MeetingType.objects.create(agenda=agenda, label='MT %s' % i)
desk = Desk.objects.create(agenda=agenda, label='Desk %s' % i)
for weekday in (0, 6):
TimePeriod.objects.create(
weekday=weekday, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
# exception starts and ends in the past
TimePeriodException.objects.create(
desk=desk,
start_datetime=now() - datetime.timedelta(days=2),
end_datetime=now() - datetime.timedelta(days=1),
)
if i % 2:
# exception starts in the past but ends in the futur
TimePeriodException.objects.create(
desk=desk,
start_datetime=now() - datetime.timedelta(days=1),
end_datetime=now() + datetime.timedelta(days=1),
)
else:
# exception in more than 2 weeks
TimePeriodException.objects.create(
desk=desk,
start_datetime=now() + datetime.timedelta(days=20),
end_datetime=now() + datetime.timedelta(days=21),
)
app = login(app)
with CaptureQueriesContext(connection) as ctx:
app.get('/manage/agendas/%s/settings' % agenda.pk)
assert len(ctx.captured_queries) == 10
def test_agenda_resources(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='events')
resource = Resource.objects.create(label='Resource 1')
app = login(app)
# not for events agenda
app.get('/manage/agendas/%s/add-resource/' % agenda.pk, status=404)
app.get('/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk), status=404)
def test_meetings_agenda_resources(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'has_resources' in resp.context
assert resp.context['has_resources'] is False
assert 'Add resource' not in resp.text
resource = Resource.objects.create(label='Resource 1')
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'has_resources' in resp.context
assert resp.context['has_resources'] is True
assert '/manage/resource/%s/' % resource.pk not in resp.text
assert '/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk) not in resp.text
resp = resp.click('Add resource')
assert list(resp.context['form'].fields['resource'].queryset) == [resource]
resp.form['resource'] = resource.pk
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk)
assert list(agenda.resources.all()) == [resource]
resp = resp.follow()
assert '/manage/resource/%s/' % resource.pk in resp.text
assert '/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk) in resp.text
resp = resp.click('Add resource')
assert list(resp.context['form'].fields['resource'].queryset) == []
resp = app.get('/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk))
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk)
assert list(agenda.resources.all()) == []
resp = resp.follow()
assert '/manage/resource/%s/' % resource.pk not in resp.text
assert '/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk) not in resp.text
def test_delete_agenda(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
app = login(app)
resp = app.get('/manage/', status=200)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/manage/')
resp = resp.follow()
assert 'Foo bar' not in resp.text
def test_delete_busy_agenda(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
event = Event(start_datetime=now() + datetime.timedelta(days=10), places=10, agenda=agenda)
event.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Delete')
assert 'Are you sure you want to delete this?' in resp.text
booking = Booking(event=event)
booking.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Delete')
assert 'This cannot be removed' in resp.text
booking.cancellation_datetime = now()
booking.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Delete')
assert 'Are you sure you want to delete this?' in resp.text
# suddenly the booking is no longer cancelled, but the admin clicks on the
# delete button.
booking.cancellation_datetime = None
booking.save()
resp = resp.form.submit(status=403)
def test_delete_agenda_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'Options' in resp.text
assert 'Delete' not in resp.text
resp = app.get('/manage/agendas/%s/delete' % agenda.id, status=403)
def test_delete_busy_desk(app, admin_user):
agenda = Agenda(label=u'Foo bar', kind='meetings')
agenda.save()
desk_a = Desk.objects.create(agenda=agenda, label='Desk A')
desk_b = Desk.objects.create(agenda=agenda, label='Desk B')
event = Event(start_datetime=now() + datetime.timedelta(days=10), places=10, agenda=agenda, desk=desk_a)
event.save()
app = login(app)
resp = app.get('/manage/', status=200)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
desk_page = resp.click('Desk A')
desk_delete_page = desk_page.click('Delete')
assert 'Are you sure you want to delete this?' in desk_delete_page.text
# make sure the deleting is not disabled
assert 'disabled' not in desk_delete_page.text
booking = Booking(event=event)
booking.save()
resp = desk_page.click('Delete')
assert 'This cannot be removed' in resp.text
# the button is disabled
assert 'disabled' in resp.text
app.post('/manage/desks/%s/delete' % desk_a.pk, status=403)
def test_add_event(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.maximal_booking_delay = 0
agenda.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert "This agenda doesn't have any event yet." in resp.text
year = now().year + 1
resp = resp.click('New Event')
resp.form['start_datetime_0'] = '%s-02-15' % year
resp.form['start_datetime_1'] = '17:00'
resp.form['places'] = 10
resp = resp.form.submit()
resp = resp.follow()
event = Event.objects.get(places=10)
assert event.publication_date is None
assert "This agenda doesn't have any event yet." not in resp.text
assert '/manage/agendas/%s/events/%s/' % (agenda.id, event.id) in resp.text
assert ('Feb. 15, %s, 5 p.m.' % year) in resp.text
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda.id)
assert resp_datetimes.json['data'][0]['text'] == 'Feb. 15, %s, 5 p.m.' % year
assert resp_datetimes.json['data'][0]['datetime'] == '%s-02-15 17:00:00' % year
# add with a description
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click('New Event')
resp.form['start_datetime_0'] = '%s-02-15' % year
resp.form['start_datetime_1'] = '18:00'
resp.form['publication_date'] = '2020-05-11'
resp.form['places'] = 11
resp.form['description'] = 'A description'
resp = resp.form.submit()
resp = resp.follow()
event = Event.objects.get(places=11)
assert event.description == 'A description'
assert event.publication_date == datetime.date(2020, 5, 11)
# add with errors in datetime parts
for parts in (
('', ''),
('invalid', ''),
('', 'invalid'),
('2019-02-24', 'invalid'),
('invalid', '17:00'),
):
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click('New Event')
resp.form['start_datetime_0'] = parts[0]
resp.form['start_datetime_1'] = parts[1]
resp.form['places'] = 10
resp = resp.form.submit()
assert (
resp.text.count('Enter a valid date')
or resp.text.count('Enter a valid time') == 1
or resp.text.count('This field is required.') == 1
)
def test_add_event_on_missing_agenda(app, admin_user):
app = login(app)
app.get('/manage/agendas/%s/add-event' % '999', status=404)
def test_add_event_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/' % agenda.id, status=302)
app.get('/manage/agendas/%s/add-event' % agenda.id, status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert '<h2>Settings' in resp.text
resp = resp.click('New Event')
resp.form['start_datetime_0'] = '2016-02-15'
resp.form['start_datetime_1'] = '17:00'
resp.form['places'] = 10
resp = resp.form.submit()
resp = resp.follow()
event = Event.objects.get(places=10)
assert "This agenda doesn't have any event yet." not in resp.text
assert '/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id) in resp.text
assert 'Feb. 15, 2016, 5 p.m.' in resp.text
assert event.duration is None
assert event.end_datetime is None
resp = resp.click('New Event')
resp.form['start_datetime_0'] = '2016-02-15'
resp.form['start_datetime_1'] = '17:00'
resp.form['duration'] = 45
resp.form['places'] = 12
resp = resp.form.submit()
resp = resp.follow()
event = Event.objects.get(places=12)
assert event.duration == 45
assert event.end_datetime == event.start_datetime + datetime.timedelta(minutes=45)
def test_edit_event(settings, app, admin_user):
settings.LANGUAGE_CODE = 'fr-fr' # check date initial value format
agenda = Agenda(label=u'Foo bar')
agenda.save()
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=20, agenda=agenda)
event.save()
assert event.duration is None
assert event.end_datetime is None
app = login(app)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.pk, event.pk))
assert resp.form['start_datetime_0'].value == '2016-02-15'
assert resp.form['start_datetime_1'].value == '17:00'
assert resp.form['publication_date'].value == ''
assert resp.form['duration'].value == ''
resp.form['start_datetime_0'] = '2016-02-16'
resp.form['start_datetime_1'] = '17:00'
resp.form['publication_date'] = '2020-05-11'
resp.form['duration'].value = 45
resp.form['places'] = 20
resp = resp.form.submit()
settings.LANGUAGE_CODE = 'en'
resp = resp.follow()
assert '/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id) in resp.text
assert 'Feb. 16, 2016, 5 p.m.' in resp.text
event.refresh_from_db()
assert event.places == 20
assert event.publication_date == datetime.date(2020, 5, 11)
assert event.duration == 45
assert event.end_datetime == event.start_datetime + datetime.timedelta(minutes=45)
def test_edit_missing_event(app, admin_user):
app = login(app)
app.get('/manage/agendas/999/', status=404)
def test_edit_event_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
event = Event.objects.create(
start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
places=20,
agenda=agenda,
publication_date=datetime.date(2020, 5, 11),
)
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id), status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click('Feb. 15, 2016, 5 p.m.')
assert resp.form['start_datetime_0'].value == '2016-02-15'
assert resp.form['start_datetime_1'].value == '17:00'
assert resp.form['publication_date'].value == '2020-05-11'
resp.form['start_datetime_0'] = '2016-02-16'
resp.form['start_datetime_1'] = '17:00'
resp.form['publication_date'] = ''
resp.form['places'] = 20
resp = resp.form.submit()
resp = resp.follow()
assert '/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id) in resp.text
assert 'Feb. 16, 2016, 5 p.m.' in resp.text
event.refresh_from_db()
assert event.publication_date is None
def test_booked_places(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
event.save()
Booking(event=event).save()
Booking(event=event).save()
app = login(app)
day = event.start_datetime
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month), status=200)
assert '8 remaining places' in resp.text
assert '(2/10 bookings)' in resp.text
def test_event_classes(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
event.save()
for i in range(2):
Booking(event=event).save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert not 'full' in resp.text
assert not 'overbooking' in resp.text
for i in range(8):
Booking(event=event).save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert 'full' in resp.text
assert not 'overbooking' in resp.text
Booking(event=event).save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert 'full' in resp.text
assert 'overbooking' in resp.text
def test_delete_event(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
event.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click(href='/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert Event.objects.count() == 0
def test_delete_busy_event(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
event = Event(start_datetime=now() + datetime.timedelta(days=10), places=10, agenda=agenda)
event.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click(href='/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp = resp.click('Delete')
assert 'Are you sure you want to delete this event?' in resp.text
booking = Booking(event=event)
booking.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click(href='/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp = resp.click('Delete')
assert 'This cannot be removed' in resp.text
booking.cancellation_datetime = now()
booking.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click(href='/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp = resp.click('Delete')
assert 'Are you sure you want to delete this event?' in resp.text
# suddenly the booking is no longer cancelled, but the admin clicks on the
# delete button.
booking.cancellation_datetime = None
booking.save()
resp = resp.form.submit(status=403)
def test_delete_event_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
event.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click(href='/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert Event.objects.count() == 0
def test_export_events(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar')
app = login(app)
resp = app.get('/manage/agendas/%s/export-events' % agenda.id)
csv_export = resp.text
assert (
csv_export
== 'date,time,number of places,number of places in waiting list,label,identifier,description,pricing,URL,publication date,duration\r\n'
)
resp = app.get('/manage/agendas/%s/import-events' % agenda.id)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,00:30,10', 'text/csv')
resp.form.submit(status=302)
resp = app.get('/manage/agendas/%s/export-events' % agenda.id)
csv_export = resp.text
assert (
csv_export
== 'date,time,number of places,number of places in waiting list,label,identifier,description,pricing,URL,publication date,duration\r\n'
'2016-09-16,00:30,10,0,,foo-bar-event,,,,,\r\n'
)
resp = app.get('/manage/agendas/%s/import-events' % agenda.id)
resp.form['events_csv_file'] = Upload(
't.csv',
b'2016-09-16,23:30,10,5,label,slug,"description\nfoobar",pricing,url,2016-10-16,90',
'text/csv',
)
resp.form.submit(status=302)
resp = app.get('/manage/agendas/%s/export-events' % agenda.id)
csv_export = resp.text
assert (
csv_export
== 'date,time,number of places,number of places in waiting list,label,identifier,description,pricing,URL,publication date,duration\r\n'
'2016-09-16,00:30,10,0,,foo-bar-event,,,,,\r\n'
'2016-09-16,23:30,10,5,label,slug,"description\nfoobar",pricing,url,2016-10-16,90\r\n'
)
def test_export_events_wrong_kind(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
app = login(app)
app.get('/manage/agendas/%s/export-events' % agenda.id, status=404)
agenda.kind = 'virtual'
agenda.save()
app.get('/manage/agendas/%s/export-events' % agenda.id, status=404)
def test_import_events(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click('Import Events')
sample_csv_resp = resp.click('Download sample file')
assert sample_csv_resp.content_type == 'text/csv'
assert sample_csv_resp.text.startswith('date,time')
resp.form['events_csv_file'] = Upload('t.csv', sample_csv_resp.content, 'text/csv')
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'xx', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Invalid file format.' in resp.text
resp.form['events_csv_file'] = Upload('t.csv', b'xxxx\0\0xxxx', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Invalid file format.' in resp.text
resp.form['events_csv_file'] = Upload('t.csv', b'2016-14-16,18:00', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Invalid file format.' in resp.text
resp.form['events_csv_file'] = Upload('t.csv', b'2016-14-16,18:00,10', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Invalid file format. (date/time format' in resp.text
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,blah', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Invalid file format. (number of places,' in resp.text
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,blah', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Invalid file format. (number of places in waiting list,' in resp.text
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,' + b'x' * 151, 'text/csv')
resp = resp.form.submit(status=200)
assert 'Ensure this value has at most 150' in resp.text
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10', 'text/csv')
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
assert Event.objects.all()[0].places == 10
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5', 'text/csv')
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
assert Event.objects.all()[0].places == 10
assert Event.objects.all()[0].waiting_list_places == 5
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', u'2016-09-16,18:00,10,5,éléphant'.encode('utf-8'), 'text/csv'
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
assert Event.objects.all()[0].places == 10
assert Event.objects.all()[0].waiting_list_places == 5
assert Event.objects.all()[0].label == u'éléphant'
Event.objects.all().delete()
# BOM
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', codecs.BOM_UTF8 + u'2016-09-16,18:00,10,5,éléphant'.encode('utf-8'), 'text/csv'
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
assert Event.objects.all()[0].places == 10
assert Event.objects.all()[0].waiting_list_places == 5
assert Event.objects.all()[0].label == u'éléphant'
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', u'2016-09-16,18:00,10,5,éléphant'.encode('iso-8859-15'), 'text/csv'
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
assert Event.objects.all()[0].places == 10
assert Event.objects.all()[0].waiting_list_places == 5
assert Event.objects.all()[0].label == u'éléphant'
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', u'2016-09-16,18:00,10,5,éléphant'.encode('eucjp'), 'text/csv'
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
assert Event.objects.all()[0].places == 10
assert Event.objects.all()[0].waiting_list_places == 5
assert Event.objects.all()[0].label == u'\x8f«±l\x8f«±phant' # eucjp interpreted as iso-8859-15
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv',
b'date,time,etc.\n' b'2016-09-16,18:00,10,5,bla bla bla\n' b'\n' b'2016-09-19,18:00,10',
'text/csv',
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 2
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv',
'"date"\t"time"\t"etc."\n'
'"2016-09-16"\t"18:00"\t"10"\t"5"\t"éléphant"\n'
'"2016-09-19"\t"18:00"\t"10"'.encode('iso-8859-15'),
'text/csv',
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 2
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug', 'text/csv')
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
event = Event.objects.latest('pk')
assert event.start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
assert event.places == 10
assert event.waiting_list_places == 5
assert event.label == 'label'
assert event.slug == 'slug'
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug', 'text/csv')
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
event = Event.objects.latest('pk')
assert event.slug == 'slug'
# additional optional attributes
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug,,,,,', 'text/csv')
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
event = Event.objects.get()
assert event.description == ''
assert event.pricing == ''
assert event.url == ''
assert event.publication_date is None
assert event.duration is None
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv',
b'2016-09-16,18:00,10,5,label,slug,"description\nfoobar",pricing,url,2016-10-16,90',
'text/csv',
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
event = Event.objects.get()
assert event.description == 'description\nfoobar'
assert event.pricing == 'pricing'
assert event.url == 'url'
assert event.publication_date == datetime.date(2016, 10, 16)
assert event.duration == 90
# publication date bad format
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', b'2016-09-16,18:00,10,5,label,slug,description,pricing,url,foobar', 'text/csv'
)
resp = resp.form.submit(status=200)
assert 'Invalid file format. (date format' in resp.text
# duration bad format
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', b'2016-09-16,18:00,10,5,label,slug,description,pricing,url,2016-09-16,foobar', 'text/csv'
)
resp = resp.form.submit(status=200)
assert 'Invalid file format. (duration' in resp.text
# import events with empty slugs
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv',
b'2016-09-16,18:00,10,5,labela,labelb,,pricing,\n'
b'2016-09-17,18:00,10,5,labela,labelb-1,,pricing,\n'
b'2016-09-18,18:00,10,5,labela,labelb-2,,pricing,\n'
b'2016-09-18,18:00,10,5,labelb,,,pricing,\n'
b'2016-09-18,18:00,10,5,labelb,,,pricing,\n',
'text/csv',
)
with CaptureQueriesContext(connection) as ctx:
resp = resp.form.submit(status=302)
assert len(ctx.captured_queries) == 22
assert Event.objects.count() == 5
assert set(Event.objects.values_list('slug', flat=True)) == set(
['labelb', 'labelb-1', 'labelb-2', 'labelb-3', 'labelb-4']
)
# forbidden numerical slug
Event.objects.all().delete()
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,1234', 'text/csv')
resp = resp.form.submit(status=200)
assert 'value cannot be a number' in resp.text
assert 'Identifier:' in resp.text # verbose_name is shown, not field name ('slug:')
def test_import_events_existing_event(app, admin_user, freezer):
agenda = Agenda.objects.create(label=u'Foo bar')
app = login(app)
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,18:00,10,5,label,slug\n', 'text/csv',
)
resp.form.submit(status=302)
assert agenda.event_set.count() == 1
event = Event.objects.latest('pk')
def check_import(date, time, with_alert):
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', b'%s,%s,10,5,label,slug\n' % (date.encode(), time.encode()), 'text/csv',
)
resp = resp.form.submit(status=302).follow()
assert agenda.event_set.count() == 1
event.refresh_from_db()
if with_alert:
assert (
'<li class="warning">Event &quot;label&quot; start date has changed. Do not forget to notify the registrants.</li>'
in resp.text
)
else:
assert (
'<li class="warning">Event &quot;label&quot; start date has changed. Do not forget to notify the registrants.</li>'
not in resp.text
)
assert event.start_datetime == make_aware(
datetime.datetime(*[int(v) for v in date.split('-')], *[int(v) for v in time.split(':')])
)
# change date or time
# event in the past, no alert, with or without booking
Booking.objects.create(
event=event, cancellation_datetime=make_aware(datetime.datetime(2017, 5, 20, 10, 30))
)
check_import('2016-09-15', '18:00', False) # change date
check_import('2016-09-15', '17:00', False) # change time
# available booking
Booking.objects.create(event=event)
check_import('2016-09-14', '17:00', False) # change date
check_import('2016-09-14', '16:00', False) # change time
# date in the future
freezer.move_to('2016-09-01')
# warn if available booking only
check_import('2016-09-13', '16:00', True) # change date
check_import('2016-09-13', '15:00', True) # change time
# no available booking
Booking.objects.all().delete()
Booking.objects.create(
event=event, cancellation_datetime=make_aware(datetime.datetime(2017, 5, 20, 10, 30))
)
check_import('2016-09-12', '15:00', False) # change date
check_import('2016-09-12', '14:00', False) # change time
# check there is a message per changed event
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv', b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,19:00,10,5,label,other_slug\n', 'text/csv',
)
resp.form.submit(status=302)
assert agenda.event_set.count() == 2
event2 = Event.objects.latest('pk')
Booking.objects.create(event=event)
Booking.objects.create(event=event2)
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv',
b'2016-09-17,18:00,10,5,label,slug\n2016-09-17,19:00,10,5,,other_slug\n2016-09-17,20:00,10,5,,other_slug\n',
'text/csv',
)
resp = resp.form.submit(status=302).follow()
assert agenda.event_set.count() == 2
assert (
resp.text.count(
'Event &quot;label&quot; start date has changed. Do not forget to notify the registrants.'
)
== 1
)
assert (
resp.text.count(
'Event &quot;other_slug&quot; start date has changed. Do not forget to notify the registrants.'
)
== 1
)
def test_import_events_wrong_kind(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
app = login(app)
app.get('/manage/agendas/%s/import-events' % agenda.id, status=404)
agenda.kind = 'virtual'
agenda.save()
app.get('/manage/agendas/%s/import-events' % agenda.id, status=404)
def test_add_meetings_agenda(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp.form['kind'] = 'meetings'
resp = resp.form.submit()
agenda = Agenda.objects.get(label='Foo bar')
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
resp = resp.follow()
assert 'Foo bar' in resp.text
assert '<h2>Settings' in resp.text
assert 'Meeting Types' in resp.text
agenda = Agenda.objects.get(label='Foo bar')
assert agenda.kind == 'meetings'
def test_meetings_agenda_add_meeting_type(app, admin_user):
agenda = Agenda(label=u'Foo bar', kind='meetings')
agenda.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert "This agenda doesn't have any meeting type yet." in resp.text
resp = resp.click('New Meeting Type')
resp.form['label'] = 'Blah'
resp.form['duration'] = '60'
assert 'deleted' not in resp.form.fields
resp = resp.form.submit()
meeeting_type = MeetingType.objects.get(agenda=agenda)
assert meeeting_type.label == 'Blah'
assert meeeting_type.duration == 60
assert meeeting_type.deleted is False
resp = resp.follow()
assert 'Blah' in resp.text
# and edit
resp = resp.click('Blah')
resp.form['duration'] = '30'
assert 'deleted' not in resp.form.fields
resp = resp.form.submit()
meeeting_type = MeetingType.objects.get(agenda=agenda)
assert meeeting_type.duration == 30
def test_meetings_agenda_delete_meeting_type(app, admin_user):
agenda = Agenda(label=u'Foo bar', kind='meetings')
agenda.save()
meeting_type = MeetingType(agenda=agenda, label='Blah')
meeting_type.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Blah')
resp = resp.click('Delete')
assert 'Are you sure you want to delete this?' in resp.text
assert 'disabled' not in resp.text
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
meeting_type.refresh_from_db()
assert meeting_type.deleted is True
assert '__deleted__' in meeting_type.slug
# meeting type not showing up anymore
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'Meeting Type Foo' not in resp.text
# it is possible to add a new meeting type with the same slug
new_meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah')
assert new_meeting_type.slug == 'blah'
def test_meetings_agenda_add_time_period(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType.objects.create(agenda=agenda, label='Blah')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Add a time period$')
resp.form['weekdays-2'].checked = True
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '17:00'
resp = resp.form.submit()
assert TimePeriod.objects.get(desk=desk).weekday == 2
assert TimePeriod.objects.get(desk=desk).start_time.hour == 10
assert TimePeriod.objects.get(desk=desk).start_time.minute == 0
assert TimePeriod.objects.get(desk=desk).end_time.hour == 17
assert TimePeriod.objects.get(desk=desk).end_time.minute == 0
resp = resp.follow()
# add a second time period
resp = resp.click('Add a time period', index=0)
resp.form['weekdays-0'].checked = True
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '13:00'
resp = resp.form.submit()
resp = resp.follow()
assert u'Monday / 10 a.m. → 1 p.m.' in resp.text
assert u'Wednesday / 10 a.m. → 5 p.m.' in resp.text
assert resp.text.index('Monday') < resp.text.index('Wednesday')
# invert start and end
resp2 = resp.click('Add a time period', index=0)
resp2.form['weekdays-0'].checked = True
resp2.form['start_time'] = '13:00'
resp2.form['end_time'] = '10:00'
resp2 = resp2.form.submit()
assert 'End time must come after start time.' in resp2.text
# and edit
resp = resp.click(u'Wednesday / 10 a.m. → 5 p.m.')
assert 'Edit Time Period' in resp.text
resp.form['start_time'] = '9:00'
resp = resp.form.submit()
resp = resp.follow()
assert TimePeriod.objects.get(desk=desk, weekday=2).start_time.hour == 9
# and edit with inverted start/end
resp2 = resp.click(u'Wednesday / 9 a.m. → 5 p.m.')
resp2.form['start_time'] = '18:00'
resp2 = resp2.form.submit()
assert 'End time must come after start time.' in resp2.text
# and add same time periods on multiple days
resp = resp.click('Add a time period', index=0)
resp.form['weekdays-4'].checked = True
resp.form['weekdays-5'].checked = True
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '13:00'
resp = resp.form.submit()
assert TimePeriod.objects.filter(desk=desk).count() == 4
def test_meetings_agenda_delete_time_period(app, admin_user):
agenda = Agenda(label=u'Foo bar', kind='meetings')
agenda.save()
MeetingType(agenda=agenda, label='Blah').save()
desk = Desk.objects.create(agenda=agenda, label='Desk A')
time_period = TimePeriod(
desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
time_period.save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click('Wednesday')
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert TimePeriod.objects.count() == 0
def test_meetings_agenda_add_time_period_on_missing_desk(app, admin_user):
app = login(app)
agenda = Agenda(label=u'Foo bar', kind='meetings')
agenda.save()
MeetingType(agenda=agenda, label='Blah').save()
app.get('/manage/agendas/1/desk/777/add-time-period', status=404)
def test_meetings_agenda_add_time_period_as_manager(app, manager_user):
agenda = Agenda(label='Foo bar', kind='meetings')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
desk = Desk.objects.create(agenda=agenda, label='Desk A')
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%d/' % agenda.id)
assert not 'Settings' in resp.text
resp = app.get('/manage/agendas/%d/settings' % agenda.id, status=403)
MeetingType(agenda=agenda, label='Blah').save()
app.get('/manage/agendas/%d/desk/%d/add-time-period' % (agenda.id, desk.id), status=403)
time_period = TimePeriod(
desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
)
time_period.save()
resp = app.get('/manage/agendas/%d/' % agenda.id)
app.get('/manage/timeperiods/%d/edit' % time_period.id, status=403)
app.get('/manage/timeperiods/%d/delete' % time_period.id, status=403)
# grant edit right to manager
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'Add a time period' in resp.text
assert '/manage/timeperiods/%s/edit' % time_period.id in resp.text
assert '/manage/timeperiods/%s/delete' % time_period.id in resp.text
app.get('/manage/agendas/%d/desk/%d/add-time-period' % (agenda.id, desk.id), status=200)
app.get('/manage/timeperiods/%d/edit' % time_period.id, status=200)
def test_meetings_agenda_add_desk(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp.form['kind'] = 'meetings'
resp = resp.form.submit()
assert Desk.objects.count() == 1
assert str(Desk.objects.first()) == 'Desk 1'
agenda = Agenda.objects.get(slug='foo-bar')
MeetingType(agenda=agenda, label='Blah').save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('New Desk')
resp.form['label'] = 'Desk A'
resp = resp.form.submit().follow()
assert Desk.objects.count() == 2
desk = Desk.objects.latest('pk')
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
resp = resp.click('New Desk')
resp.form['label'] = 'Desk A'
resp = resp.form.submit().follow()
assert Desk.objects.count() == 3
assert Desk.objects.filter(slug='desk-a-1').count() == 1
assert 'Desk A' in resp.text
new_desk = Desk.objects.latest('pk')
assert new_desk.timeperiod_set.count() == 0
resp = resp.click('Desk A', index=1)
resp.form['label'] = 'Desk B'
resp = resp.form.submit().follow()
assert 'Desk A' in resp.text
assert 'Desk B' in resp.text
def test_meetings_agenda_add_desk_from_another(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
assert Desk.objects.count() == 1
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('New Desk')
resp.form['label'] = 'Desk B'
resp.form['copy_from'] = desk.pk
resp = resp.form.submit().follow()
assert Desk.objects.count() == 2
new_desk = Desk.objects.latest('pk')
assert new_desk.label == 'Desk B'
assert new_desk.timeperiod_set.count() == 1
def test_meetings_agenda_delete_desk(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp.form['kind'] = 'meetings'
resp = resp.form.submit()
assert Desk.objects.count() == 1
agenda = Agenda.objects.get(slug='foo-bar')
MeetingType(agenda=agenda, label='Blah').save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('New Desk')
resp.form['label'] = 'Desk A'
resp = resp.form.submit().follow()
assert Desk.objects.count() == 2
resp = resp.click('Desk A', index=0)
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert Desk.objects.count() == 1
def test_meetings_agenda_add_time_period_exception(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType.objects.create(agenda=agenda, label='Blah')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Add a time period exception')
today = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
tomorrow = make_aware(today + datetime.timedelta(days=1))
dt_format = '%Y-%m-%d %H:%M'
resp.form['label'] = 'Exception 1'
resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
resp.form['start_datetime_1'] = '08:00'
resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
resp.form['end_datetime_1'] = '16:00'
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 1
time_period_exception = TimePeriodException.objects.first()
assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(
hour=8
).strftime(dt_format)
assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(
hour=16
).strftime(dt_format)
# add an exception beyond 2 weeks and make sure it isn't listed
resp = resp.click('Add a time period exception', index=1)
future = tomorrow + datetime.timedelta(days=15)
resp.form['label'] = 'Exception 2'
resp.form['start_datetime_0'] = future.strftime('%Y-%m-%d')
resp.form['start_datetime_1'] = '00:00'
resp.form['end_datetime_0'] = future.strftime('%Y-%m-%d')
resp.form['end_datetime_1'] = '16:00'
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 2
assert 'Exception 1' in resp.text
assert 'Exception 2' not in resp.text
resp = resp.click(href="/manage/time-period-exceptions/%d/exception-extract-list" % desk.pk)
assert 'Exception 1' in resp.text
assert 'Exception 2' in resp.text
# add global time exception
# only one desk: no option to apply to all desks
resp = app.get('/manage/agendas/%s/desk/%s/add-time-period-exception' % (agenda.pk, desk.pk))
assert 'all_desks' not in resp.context['form'].fields
# more than one desk
agenda2 = Agenda.objects.create(label='Foo bar', kind='meetings')
Desk.objects.create(agenda=agenda2, label='Other Desk') # check exception is not created for this one
desk2 = Desk.objects.create(agenda=agenda, label='Desk B')
event = Event.objects.create(
agenda=agenda, places=1, desk=desk2, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
)
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%s/desk/%s/add-time-period-exception' % (agenda.pk, desk.pk))
future = tomorrow + datetime.timedelta(days=35)
resp.form['label'] = 'Exception 3'
resp.form['start_datetime_0'] = '2017-05-22'
resp.form['start_datetime_1'] = '08:00'
resp.form['end_datetime_0'] = '2017-05-26'
resp.form['end_datetime_1'] = '17:30'
resp.form['all_desks'] = True
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 4
assert 'Exceptions added. Note: one or several bookings exists within this time slot.' in resp.text
exception = TimePeriodException.objects.first()
resp = app.get('/manage/time-period-exceptions/%s/edit' % exception.pk)
assert 'all_desks' not in resp.context['form'].fields
def test_meetings_agenda_add_time_period_exception_when_booking_exists(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
event = Event.objects.create(
agenda=agenda, places=1, desk=desk, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
)
Booking.objects.create(event=event)
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Add a time period exception')
resp = resp.form.submit() # submit empty form
# fields should be marked with errors
assert resp.text.count('This field is required.') == 2
# try again with data in fields
resp.form['start_datetime_0'] = '2017-05-22'
resp.form['start_datetime_1'] = '08:00'
resp.form['end_datetime_0'] = '2017-05-26'
resp.form['end_datetime_1'] = '17:30'
resp = resp.form.submit().follow()
assert 'Exception added. Note: one or several bookings exists within this time slot.' in resp.text
assert TimePeriodException.objects.count() == 1
def test_meetings_agenda_add_time_period_exception_when_cancelled_booking_exists(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
event = Event.objects.create(
agenda=agenda, places=1, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
)
Booking.objects.create(
event=event, cancellation_datetime=make_aware(datetime.datetime(2017, 5, 20, 10, 30))
)
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Add a time period exception')
resp.form['start_datetime_0'] = '2017-05-22'
resp.form['start_datetime_1'] = '08:00'
resp.form['end_datetime_0'] = '2017-05-26'
resp.form['end_datetime_1'] = '17:30'
resp = resp.form.submit().follow()
assert 'Exception added. Note: one or several bookings exists within this time slot.' not in resp.text
assert TimePeriodException.objects.count() == 1
def test_meetings_agenda_add_invalid_time_period_exception():
form = TimePeriodExceptionForm(
data={
'start_datetime_0': '2017-05-26',
'start_datetime_1': '17:30',
'end_datetime_0': '2017-05-22',
'end_datetime_1': '08:00',
}
)
assert form.is_valid() is False
assert form.errors['end_datetime'] == ['End datetime must be greater than start datetime.']
# start_datetime is invalid
form = TimePeriodExceptionForm(
data={
'start_datetime_0': '2017-05-26',
'start_datetime_1': 'foo',
'end_datetime_0': '2017-05-22',
'end_datetime_1': '08:00',
}
)
assert form.is_valid() is False
# end_datetime is invalid
form = TimePeriodExceptionForm(
data={
'start_datetime_0': '2017-05-26',
'start_datetime_1': '17:30',
'end_datetime_0': 'bar',
'end_datetime_1': '08:00',
}
)
assert form.is_valid() is False
def test_meetings_agenda_delete_time_period_exception(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Add a time period exception')
today = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
tomorrow = make_aware(today + datetime.timedelta(days=15))
dt_format = '%Y-%m-%d %H:%M'
resp.form['label'] = 'Exception 1'
resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
resp.form['start_datetime_1'] = '08:00'
resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
resp.form['end_datetime_1'] = '16:00'
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 1
time_period_exception = TimePeriodException.objects.first()
assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(
hour=8
).strftime(dt_format)
assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(
hour=16
).strftime(dt_format)
resp = resp.click(href='/manage/time-period-exceptions/%d/edit' % time_period_exception.id)
resp = resp.click('Delete')
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 0
assert resp.request.url.endswith('/manage/agendas/%d/settings' % agenda.pk)
# stay on exception list
time_period_exception = TimePeriodException.objects.create(
label='Future Exception',
desk=desk,
start_datetime=now() + datetime.timedelta(days=1),
end_datetime=now() + datetime.timedelta(days=2),
)
resp = app.get('/manage/time-period-exceptions/%d/exception-list' % desk.pk)
resp = resp.click(href='/manage/time-period-exceptions/%d/delete' % time_period_exception.pk)
resp = resp.form.submit(
extra_environ={'HTTP_REFERER': str('/manage/time-period-exceptions/%d/exception-list' % desk.pk)}
).follow()
assert resp.request.url.endswith('/manage/time-period-exceptions/%d/exception-list' % desk.pk)
def test_exception_list(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
past_exception = TimePeriodException.objects.create(
label='Past Exception',
desk=desk,
start_datetime=now() - datetime.timedelta(days=2),
end_datetime=now() - datetime.timedelta(days=1),
)
current_exception = TimePeriodException.objects.create(
label='Current Exception',
desk=desk,
start_datetime=now() - datetime.timedelta(days=1),
end_datetime=now() + datetime.timedelta(days=1),
)
future_exception = TimePeriodException.objects.create(
label='Future Exception',
desk=desk,
start_datetime=now() + datetime.timedelta(days=1),
end_datetime=now() + datetime.timedelta(days=2),
)
login(app)
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
assert '/manage/time-period-exceptions/%d/edit' % past_exception.pk not in resp.text
assert '/manage/time-period-exceptions/%d/edit' % current_exception.pk in resp.text
assert '/manage/time-period-exceptions/%d/edit' % future_exception.pk in resp.text
resp = resp.click(href="/manage/time-period-exceptions/%d/exception-extract-list" % desk.pk)
assert '/manage/time-period-exceptions/%d/edit' % past_exception.pk not in resp.text
assert '/manage/time-period-exceptions/%d/edit' % current_exception.pk in resp.text
assert '/manage/time-period-exceptions/%d/edit' % future_exception.pk in resp.text
resp = resp.click(href="/manage/time-period-exceptions/%d/exception-list" % desk.pk)
assert '/manage/time-period-exceptions/%d/edit' % past_exception.pk not in resp.text
assert '/manage/time-period-exceptions/%d/edit' % current_exception.pk in resp.text
assert '/manage/time-period-exceptions/%d/edit' % future_exception.pk in resp.text
def test_agenda_import_time_period_exception_from_ics(app, admin_user):
agenda = Agenda.objects.create(label='Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Test Desk')
MeetingType.objects.create(agenda=agenda, label='Foo')
login(app)
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
assert 'Manage exception sources' in resp.text
resp = resp.click('manage exceptions')
assert "To add new exceptions, you can upload a file or specify an address to a remote calendar." in resp
resp = resp.form.submit(status=200)
assert 'Please provide an ICS File or an URL.' in resp.text
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_file'] = Upload('exceptions.ics', b'invalid content', 'text/calendar')
resp = resp.form.submit(status=200)
assert 'File format is invalid' in resp.text
ics_with_no_start_date = b"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTEND:20180101
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR"""
resp.form['ics_file'] = Upload('exceptions.ics', ics_with_no_start_date, 'text/calendar')
resp = resp.form.submit(status=200)
assert 'Event &quot;New Year&#39;s Eve&quot; has no start date.' in resp.text
ics_with_no_events = b"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
END:VCALENDAR"""
resp.form['ics_file'] = Upload('exceptions.ics', ics_with_no_events, 'text/calendar')
resp = resp.form.submit(status=200)
assert "The file doesn&#39;t contain any events." in resp.text
ics_with_exceptions = b"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR"""
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_file'] = Upload('exceptions.ics', ics_with_exceptions, 'text/calendar')
resp = resp.form.submit(status=302)
assert TimePeriodException.objects.filter(desk=desk).count() == 1
assert TimePeriodExceptionSource.objects.filter(desk=desk).count() == 1
source = TimePeriodExceptionSource.objects.latest('pk')
exception = TimePeriodException.objects.latest('pk')
assert exception.source == source
assert source.ics_filename == 'exceptions.ics'
assert 'exceptions.ics' in source.ics_file.name
assert source.ics_url is None
resp = resp.follow()
assert 'An exception has been imported.' in resp.text
@pytest.mark.freeze_time('2017-12-01')
def test_agenda_import_time_period_exception_from_ics_recurrent(app, admin_user):
agenda = Agenda.objects.create(label='Example', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Test Desk')
MeetingType(agenda=agenda, label='Foo').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
ics_with_recurrent_exceptions = b"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR"""
resp.form['ics_file'] = Upload('exceptions.ics', ics_with_recurrent_exceptions, 'text/calendar')
resp = resp.form.submit(status=302).follow()
assert TimePeriodException.objects.filter(desk=desk).count() == 2
@mock.patch('chrono.agendas.models.requests.get')
def test_agenda_import_time_period_exception_with_remote_ics(mocked_get, app, admin_user):
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')
login(app)
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
resp = resp.click('manage exceptions')
assert 'ics_file' in resp.form.fields
assert 'ics_url' in resp.form.fields
resp.form['ics_url'] = 'http://example.com/foo.ics'
mocked_response = mock.Mock()
mocked_response.text = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR"""
mocked_get.return_value = mocked_response
resp = resp.form.submit(status=302)
assert TimePeriodException.objects.filter(desk=desk).count() == 1
exception = TimePeriodException.objects.get(desk=desk)
assert TimePeriodExceptionSource.objects.filter(desk=desk).count() == 1
source = TimePeriodExceptionSource.objects.latest('pk')
exception = TimePeriodException.objects.latest('pk')
assert exception.source == source
assert source.ics_filename is None
assert source.ics_file.name == ''
assert source.ics_url == 'http://example.com/foo.ics'
@mock.patch('chrono.agendas.models.requests.get')
def test_agenda_import_time_period_exception_with_remote_ics_no_events(mocked_get, app, admin_user):
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')
login(app)
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_url'] = 'http://example.com/foo.ics'
mocked_response = mock.Mock()
mocked_response.text = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR"""
mocked_get.return_value = mocked_response
resp = resp.form.submit(status=302)
assert TimePeriodException.objects.filter(desk=desk).count() == 1
@mock.patch('chrono.agendas.models.requests.get')
def test_agenda_import_time_period_exception_from_remote_ics_with_connection_error(
mocked_get, app, admin_user
):
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')
login(app)
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
resp = resp.click('manage exceptions')
assert 'ics_file' in resp.form.fields
assert 'ics_url' in resp.form.fields
resp.form['ics_url'] = 'http://example.com/foo.ics'
mocked_response = mock.Mock()
mocked_get.return_value = mocked_response
def mocked_requests_connection_error(*args, **kwargs):
raise requests.exceptions.ConnectionError('unreachable')
mocked_get.side_effect = mocked_requests_connection_error
resp = resp.form.submit(status=200)
assert 'Failed to retrieve remote calendar (http://example.com/foo.ics, unreachable).' in resp.text
@mock.patch('chrono.agendas.models.requests.get')
def test_agenda_import_time_period_exception_from_forbidden_remote_ics(mocked_get, app, admin_user):
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')
login(app)
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_url'] = 'http://example.com/foo.ics'
mocked_response = mock.Mock()
mocked_response.status_code = 403
mocked_get.return_value = mocked_response
def mocked_requests_http_forbidden_error(*args, **kwargs):
raise requests.exceptions.HTTPError(response=mocked_response)
mocked_get.side_effect = mocked_requests_http_forbidden_error
resp = resp.form.submit(status=200)
assert 'Failed to retrieve remote calendar (http://example.com/foo.ics, HTTP error 403).' in resp.text
@mock.patch('chrono.agendas.models.requests.get')
def test_agenda_import_time_period_exception_from_remote_ics_with_ssl_error(mocked_get, app, admin_user):
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')
login(app)
resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_url'] = 'https://example.com/foo.ics'
mocked_response = mock.Mock()
mocked_get.return_value = mocked_response
def mocked_requests_http_ssl_error(*args, **kwargs):
raise requests.exceptions.SSLError('SSL error')
mocked_get.side_effect = mocked_requests_http_ssl_error
resp = resp.form.submit(status=200)
assert 'Failed to retrieve remote calendar (https://example.com/foo.ics, SSL error).' in resp.text
def test_meetings_agenda_delete_time_period_exception_source(app, admin_user, freezer):
freezer.move_to('2019-12-01')
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
login(app)
# import a source
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
ics_with_recurrent_exceptions = b"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR"""
resp.form['ics_file'] = Upload('exceptions.ics', ics_with_recurrent_exceptions, 'text/calendar')
resp = resp.form.submit(status=302).follow()
assert TimePeriodException.objects.filter(desk=desk).count() == 2
source1 = TimePeriodExceptionSource.objects.latest('pk')
assert source1.timeperiodexception_set.count() == 2
# import another one
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_file'] = Upload('exceptions.ics', ics_with_recurrent_exceptions, 'text/calendar')
resp = resp.form.submit(status=302).follow()
assert TimePeriodException.objects.filter(desk=desk).count() == 4
source2 = TimePeriodExceptionSource.objects.latest('pk')
assert source2.timeperiodexception_set.count() == 2
# delete the second one
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp = resp.click(href='/manage/time-period-exceptions-source/%d/delete' % source2.pk)
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 2
assert source1.timeperiodexception_set.count() == 2
assert TimePeriodExceptionSource.objects.filter(pk=source2.pk).exists() is False
# delete the first one
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp = resp.click(href='/manage/time-period-exceptions-source/%d/delete' % source1.pk)
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 0
assert TimePeriodExceptionSource.objects.filter(pk=source1.pk).exists() is False
def test_meetings_agenda_replace_time_period_exception_source(app, admin_user, freezer):
freezer.move_to('2019-12-01')
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
ics_file_content = b"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR"""
login(app)
# import a source from a file
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_file'] = Upload('exceptions.ics', ics_file_content, 'text/calendar')
resp = resp.form.submit(status=302).follow()
assert TimePeriodException.objects.filter(desk=desk).count() == 2
source = TimePeriodExceptionSource.objects.latest('pk')
assert source.timeperiodexception_set.count() == 2
exceptions = list(source.timeperiodexception_set.order_by('pk'))
old_ics_file_path = source.ics_file.path
# replace the source
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp = resp.click(href='/manage/time-period-exceptions-source/%d/replace' % source.pk)
resp.form['ics_newfile'] = Upload('exceptions.ics', ics_file_content, 'text/calendar')
resp = resp.form.submit().follow()
source.refresh_from_db()
assert source.ics_file.path != old_ics_file_path
assert os.path.exists(old_ics_file_path) is False
assert TimePeriodException.objects.count() == 2
assert source.timeperiodexception_set.count() == 2
new_exceptions = list(source.timeperiodexception_set.order_by('pk'))
assert exceptions[0].pk != new_exceptions[0].pk
assert exceptions[1].pk != new_exceptions[1].pk
@mock.patch('chrono.agendas.models.requests.get')
def test_meetings_agenda_refresh_time_period_exception_source(mocked_get, app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
ics_url_content = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo.bar//EN
BEGIN:VEVENT
DTSTART:20180101
DTEND:20180101
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR"""
login(app)
# import a source from an url
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
resp.form['ics_url'] = 'http://example.com/foo.ics'
mocked_response = mock.Mock()
mocked_response.text = ics_url_content
mocked_get.return_value = mocked_response
resp = resp.form.submit(status=302).follow()
assert TimePeriodException.objects.filter(desk=desk).count() == 1
source = TimePeriodExceptionSource.objects.latest('pk')
assert source.timeperiodexception_set.count() == 1
exceptions = list(source.timeperiodexception_set.order_by('pk'))
# refresh the source
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('manage exceptions')
mocked_response = mock.Mock()
mocked_response.text = ics_url_content
mocked_get.return_value = mocked_response
resp = resp.click(href='/manage/time-period-exceptions-source/%d/refresh' % source.pk)
assert TimePeriodException.objects.count() == 1
assert source.timeperiodexception_set.count() == 1
new_exceptions = list(source.timeperiodexception_set.order_by('pk'))
assert exceptions[0].pk != new_exceptions[0].pk
@override_settings(
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
)
def test_meetings_agenda_time_period_exception_source_from_settings(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
assert TimePeriodException.objects.exists()
login(app)
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
resp = resp.click('Settings')
resp = resp.click('manage exceptions')
assert 'Holidays' in resp.text
assert 'disabled' not in resp.text
assert 'refresh' not in resp.text
resp = resp.click('disable').follow()
assert not TimePeriodException.objects.exists()
resp = resp.click('manage exceptions')
assert 'Holidays' in resp.text
assert 'disabled' in resp.text
resp = resp.click('enable').follow()
assert TimePeriodException.objects.exists()
resp = resp.click('manage exceptions')
assert 'disabled' not in resp.text
def test_meetings_agenda_time_period_exception_source_try_disable_ics(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
source = TimePeriodExceptionSource.objects.create(desk=desk, ics_url='https://example.com/test.ics')
login(app)
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
resp = resp.click('Settings')
resp = resp.click('manage exceptions')
assert 'test.ics' in resp.text
assert app.get('/manage/time-period-exceptions-source/%s/toggle' % source.pk, status=404)
@override_settings(
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
)
def test_meetings_agenda_time_period_exception_source_external(app, admin_user, freezer):
freezer.move_to('2020-01-01')
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType(agenda=agenda, label='Blah').save()
TimePeriod.objects.create(
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
)
new_year = desk.timeperiodexception_set.filter(label='New year').first()
remove_url = reverse('chrono-manager-time-period-exception-delete', kwargs={'pk': new_year.pk})
edit_url = reverse('chrono-manager-time-period-exception-edit', kwargs={'pk': new_year.pk})
login(app)
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
resp = resp.click('Settings')
assert 'New year' in resp.text
assert not remove_url in resp.text and not edit_url in resp.text
resp = resp.click('see all')
assert 'New year' in resp.text
assert not remove_url in resp.text and not edit_url in resp.text
app.get(remove_url, status=404)
def test_agenda_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()
today = datetime.date.today()
meetingtype = MeetingType(agenda=agenda, label='Bar', duration=30)
meetingtype.save()
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(
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
timeperiod.save()
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
app.reset()
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
booking_url = resp.json['data'][0]['api']['fillslot_url']
booking_url2 = resp.json['data'][2]['api']['fillslot_url']
resp = app.post(booking_url)
resp = app.post_json(booking_url2, params={'label': 'foo', 'user': 'bar', 'url': 'http://baz/'})
app.reset()
login(app)
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') == 2
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 = MeetingType(agenda=agenda, label='Baz', duration=15)
meetingtype.save()
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.text.count('div class="booking') == 2
assert 'hourspan-4' in resp.text # table CSS class
# cancel a booking
app.reset()
app.authorization = ('Basic', ('john.doe', 'password'))
booking = Booking.objects.all()[0]
resp = app.post('/api/booking/%s/cancel/' % booking.id)
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 1
app.reset()
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.text.count('div class="booking') == 1
# wrong type
agenda2 = Agenda(label=u'Foo bar')
agenda2.save()
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda2.id, date.year, date.month, date.day), status=404)
# not enough permissions
agenda2.view_role = manager_user.groups.all()[0]
agenda2.save()
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=desk,
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) == 14
# 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'
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_day_view_late_meeting(app, admin_user, kind):
today = datetime.date.today()
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)
)
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/%s/' % (agenda.pk, today.year, today.month, today.day))
assert resp.text.count('<tr') == 15
assert '<th class="hour">11 p.m.</th>' in resp.text
@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/')
@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
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert 'No opening hours this day.' in resp.text
assert 'div class="booking' not in resp.text
# book some slots
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
# no time period - events are displayed
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert resp.text.count('div class="booking') == 2
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert 'No opening hours this day.' in resp.text
assert resp.text.count('div class="booking') == 0
# events outside time period
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(16, 0)
)
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert resp.text.count('div class="booking') == 2
assert '<div class="opening-hours"' in resp.text
assert 'style="height: 600%; top: 100%;"' in resp.text
def test_agenda_events_month_view(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
today = datetime.date.today()
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month))
assert "This month doesn't have any event configured." in resp.text
assert resp.request.url.endswith('%s/%s/' % (today.year, today.month))
# add event in a future month
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=40), places=10, agenda=agenda
)
with CaptureQueriesContext(connection) as ctx:
app.get(
'/manage/agendas/%s/%s/%s/' % (agenda.id, event.start_datetime.year, event.start_datetime.month)
)
assert len(ctx.captured_queries) == 6
# current month still doesn't have events
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
assert "This month doesn't have any event configured." in resp.text
def test_agenda_open_events_view(app, admin_user, manager_user):
agenda = Agenda.objects.create(
label='Events', kind='events', minimal_booking_delay=2, maximal_booking_delay=5
)
today = datetime.date.today()
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month))
assert 'Open events' in resp.text
resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk)
assert 'Month view' in resp.text
assert "This agenda doesn't have any open event configured." in resp.text
# create some events
# past event
Event.objects.create(
agenda=agenda, label='event A', start_datetime=now() - datetime.timedelta(days=1), places=42
)
# too late
Event.objects.create(
agenda=agenda, label='event B', start_datetime=now() + datetime.timedelta(days=1), places=42
)
# too soon
Event.objects.create(
agenda=agenda, label='event C', start_datetime=now() + datetime.timedelta(days=6), places=42
)
# in range
Event.objects.create(
agenda=agenda, label='event D', start_datetime=now() + datetime.timedelta(days=3), places=42
)
# publication date in future
Event.objects.create(
agenda=agenda,
label='event E',
start_datetime=now() + datetime.timedelta(days=3),
publication_date=today + datetime.timedelta(days=1),
places=42,
)
# publication date in past
Event.objects.create(
agenda=agenda,
label='event F',
start_datetime=now() + datetime.timedelta(days=3),
publication_date=today - datetime.timedelta(days=1),
places=42,
)
resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk)
assert 'event A' not in resp.text
assert 'event B' not in resp.text
assert 'event C' not in resp.text
assert 'event D' in resp.text
assert 'event E' not in resp.text
assert 'event F' in resp.text
# not enough permissions
app.reset()
app = login(app, username='manager', password='manager')
app.get('/manage/agendas/%s/events/open/' % agenda.pk, status=403)
# just enough permissions
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app.get('/manage/agendas/%s/events/open/' % agenda.pk)
# wrong kind
agenda.kind = 'meetings'
agenda.save()
app.get('/manage/agendas/%s/events/open/' % agenda.pk, status=404)
agenda.kind = 'virtual'
agenda.save()
app.get('/manage/agendas/%s/events/open/' % agenda.pk, status=404)
def test_agenda_month_view(app, admin_user, manager_user, api_user):
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
today = datetime.date.today()
meetingtype = MeetingType(agenda=agenda, label='passeport', duration=20)
meetingtype.save()
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 = TimePeriod(
desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
timeperiod.save()
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:97.0%;left:1.0%' in resp.text
# book some slots
app.reset()
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
booking_url = resp.json['data'][0]['api']['fillslot_url']
booking_url2 = resp.json['data'][2]['api']['fillslot_url']
booking = app.post(booking_url)
booking_2 = app.post_json(booking_url2, params={'label': 'foo', 'user': 'bar', 'url': 'http://baz/'})
app.reset()
login(app)
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:33.0%;') == 2 # booking cells
desk = Desk.objects.create(agenda=agenda, label='Desk B')
timeperiod = TimePeriod(
desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
timeperiod.save()
app.reset()
booking_3 = app.post(booking_url)
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
# count occurences of timeperiod weekday in current month
d = first_month_day
weekdays = 0
while d <= last_month_day:
if d.weekday() == timeperiod_weekday:
weekdays += 1
d += datetime.timedelta(days=1)
assert resp.text.count('<div class="opening-hours"') == 2 * weekdays
current_month = today.strftime('%Y-%m')
if current_month in booking_url or current_month in booking_url2:
assert resp.text.count('<div class="booking"') == 3
# cancel bookings
app.reset()
app.post(booking.json['api']['cancel_url'])
app.post(booking_2.json['api']['cancel_url'])
app.post(booking_3.json['api']['cancel_url'])
# make sure the are not
login(app)
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
assert resp.text.count('<div class="booking"') == 0
# 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=desk,
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) == 9
assert resp.pyquery.find('.exception-hours')[0].attrib == {
'class': 'exception-hours',
'style': 'height:800.0%;top:0.0%;width:48.0%;left:50.0%;',
'title': 'Exception for a December day',
}
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_month_view_weekend(app, admin_user, kind):
month, year = 1, 2019
monday = 0
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 '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
# When weekend is hidden, do not display an empty first week
month, year = 12, 2019 # month starts a Sunday
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, year, month))
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
month, year = 6, 2019 # month starts a Saturday
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, year, month))
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
saturday = 5
timeperiod_sat = TimePeriod.objects.create(
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 '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.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))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
timeperiod_sat.delete()
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, year, month))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
@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.objects.create(
desk=desk, weekday=weekday, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
)
login(app)
for date in ('2019-10-01', '2019-10-31'):
with freezegun.freeze_time(date):
resp = app.get('/manage/agendas/%s/2019/10/' % agenda.id)
# check all days are correctly aligned
assert resp.text.count('height:300.0%;top:0.0%') == 31
# book some slots
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)
resp = app.get('/manage/agendas/%s/2019/10/' % agenda.id)
assert resp.text.count('height:33.0%;top:100.0%;') == 2
@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):
date = datetime.date(year, 1, 1)
with freezegun.freeze_time(date):
login(app)
resp = app.get('/manage/agendas/%s/%s/1/' % (agenda.id, date.year))
assert resp.text.count('<th></th>') in (4, 5)
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_month_view_event_outside_timeperiod(app, admin_user, kind):
today = datetime.date.today()
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
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert 'No opening hours this month.' in resp.text
assert 'div class="booking' not in resp.text
# book some slots
middle_day = now().replace(day=15)
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now().replace(day=middle_day.day - middle_day.weekday() + 2)).replace(
hour=hour, minute=minute
),
)
Booking.objects.create(event=event)
# no time period - events are displayed
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 2
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert 'No opening hours this month.' in resp.text
assert resp.text.count('div class="booking') == 0
# events outside time period
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(16, 0)
)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 2
assert '<div class="opening-hours" style="height:600.0%;top:100.0%;width:97.0%;left:1.0%' in resp.text
assert 'Sunday' not in resp.text
assert 'Saturday' not in resp.text
# create an event on saturday
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(
day=middle_day.day - middle_day.weekday() + 5, hour=10, minute=0
),
)
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=5, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# create an event on sunday
middle_day = now().replace(day=15)
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(
day=middle_day.day - middle_day.weekday() + 6, hour=10, minute=0
),
)
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=6, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.pk, today.year, today.month))
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
def test_agenda_view_event(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
event = Event.objects.create(
label='xyz',
start_datetime=make_aware(datetime.datetime(2019, 12, 22, 17, 0)),
places=10,
agenda=agenda,
)
for i in range(8):
Booking.objects.create(event=event)
Booking.objects.create(event=event, cancellation_datetime=now())
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/2019/12/' % agenda.id, status=200)
resp = resp.click('xyz')
assert 'Bookings (8/10): 2 remaining places' in resp.text
assert 'Waiting' not in resp.text
assert 'This event is overbooked.' not in resp.text
assert 'This event is full.' not in resp.text
event.waiting_list_places = 5
event.save()
resp = app.get(resp.request.url)
assert 'Waiting List (0/5): 5 remaining places' in resp.text
booking = Booking.objects.order_by('pk')[0]
booking.in_waiting_list = True
booking.save()
booking = Booking.objects.order_by('pk')[1]
booking.in_waiting_list = True
booking.save()
resp = app.get(resp.request.url)
assert 'Waiting List (2/5): 3 remaining places' in resp.text
assert 'Bookings (6/10): 4 remaining places' in resp.text
assert list(resp.context['booked']) == list(Booking.objects.order_by('creation_datetime')[2:8])
assert list(resp.context['waiting']) == list(Booking.objects.order_by('creation_datetime')[0:2])
event.places = 5
event.save()
resp = app.get(resp.request.url)
assert 'This event is overbooked.' in resp.text
assert 'This event is full.' not in resp.text
event.places = 6
event.save()
resp = app.get(resp.request.url)
assert 'This event is overbooked.' not in resp.text
assert 'This event is full.' in resp.text
def test_agenda_view_edit_event(app, manager_user):
test_agenda_view_event(app, manager_user)
agenda = Agenda.objects.first()
resp = app.get('/manage/agendas/%s/2019/12/' % agenda.id, status=200)
resp = resp.click('xyz')
assert 'Options' not in resp.text
assert 'Delete' not in resp.text
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
event_url = resp.request.url
resp = app.get(event_url)
assert 'Options' in resp.text
resp = resp.click('Options')
resp.form['start_datetime_0'] = agenda.event_set.first().start_datetime.strftime('%Y-%m-%d')
resp.form['start_datetime_1'] = agenda.event_set.first().start_datetime.strftime('%H:%M')
resp = resp.form.submit(status=302).follow()
assert event_url == resp.request.url
resp = resp.click('Delete')
resp = resp.form.submit()
assert Event.objects.count() == 0
def test_import_agenda_as_manager(app, manager_user):
# open /manage/ access to manager_user, and check agenda import is not
# allowed.
agenda = Agenda(label='Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
app.get('/manage/', status=200)
app.get('/manage/agendas/import/', status=403)
def test_import_agenda(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
app = login(app)
with freezegun.freeze_time('2020-06-15'):
resp = app.get('/manage/agendas/%s/export' % agenda.id)
assert resp.headers['content-type'] == 'application/json'
assert resp.headers['content-disposition'] == 'attachment; filename="export_agenda_foo-bar_20200615.json"'
agenda_export = resp.text
# invalid json
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', b'garbage', 'application/json')
resp = resp.form.submit()
assert 'File is not in the expected JSON format.' in resp.text
# empty json
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', b'{}', 'application/json')
resp = resp.form.submit().follow()
assert 'No agendas were found.' in resp.text
# existing agenda
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', agenda_export.encode('utf-8'), 'application/json')
resp = resp.form.submit().follow()
assert 'No agenda created. An agenda has been updated.' in resp.text
assert Agenda.objects.count() == 1
# new agenda
Agenda.objects.all().delete()
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', agenda_export.encode('utf-8'), 'application/json')
resp = resp.form.submit().follow()
assert 'An agenda has been created. No agenda updated.' in resp.text
assert Agenda.objects.count() == 1
# multiple agendas
agendas = json.loads(agenda_export)
agendas['agendas'].append(copy.copy(agendas['agendas'][0]))
agendas['agendas'].append(copy.copy(agendas['agendas'][0]))
agendas['agendas'][1]['label'] = 'Foo bar 2'
agendas['agendas'][1]['slug'] = 'foo-bar-2'
agendas['agendas'][2]['label'] = 'Foo bar 3'
agendas['agendas'][2]['slug'] = 'foo-bar-3'
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', json.dumps(agendas).encode('utf-8'), 'application/json')
resp = resp.form.submit().follow()
assert '2 agendas have been created. An agenda has been updated.' in resp.text
assert Agenda.objects.count() == 3
Agenda.objects.all().delete()
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', json.dumps(agendas).encode('utf-8'), 'application/json')
resp = resp.form.submit().follow()
assert '3 agendas have been created. No agenda updated.' in resp.text
assert Agenda.objects.count() == 3
# reference to unknown group
agenda_export_dict = json.loads(force_text(agenda_export))
agenda_export_dict['agendas'][0]['permissions']['view'] = u'gé1'
agenda_export = json.dumps(agenda_export_dict).encode('utf-8')
Agenda.objects.all().delete()
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', agenda_export, 'application/json')
resp = resp.form.submit()
assert u'Missing roles: &quot;gé1&quot;' in resp.text
del agenda_export_dict['agendas'][0]['permissions']['view']
# missing field
del agenda_export_dict['agendas'][0]['kind']
agenda_export = json.dumps(agenda_export_dict).encode('utf-8')
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', agenda_export, 'application/json')
resp = resp.form.submit()
assert resp.context['form'].errors['agendas_json'] == ['Key "kind" is missing.']
def test_import_does_not_delete_bookings(app, admin_user):
agenda = Agenda.objects.create(label='Foo', kind='meetings')
meeting_type = MeetingType.objects.create(agenda=agenda, label='Meeting Type', duration=30)
desk = Desk.objects.create(agenda=agenda, label='Desk', slug='desk')
event = Event(start_datetime=now(), places=10, meeting_type=meeting_type, desk=desk, agenda=agenda)
event.save()
booking = Booking(event=event)
booking.save()
assert Booking.objects.count() == 1
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Export')
assert resp.headers['content-type'] == 'application/json'
agenda_export = resp.text
# existing agenda
resp = app.get('/manage/', status=200)
resp = resp.click('Import')
resp.form['agendas_json'] = Upload('export.json', agenda_export.encode('utf-8'), 'application/json')
resp = resp.form.submit().follow()
assert 'No agenda created. An agenda has been updated.' in resp.text
assert Agenda.objects.count() == 1
assert Booking.objects.count() == 1
def test_virtual_agenda_add(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('New')
resp.form['label'] = 'Virtual agenda'
resp.form['kind'] = 'virtual'
resp = resp.form.submit()
agenda = Agenda.objects.get(label='Virtual agenda')
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert agenda.minimal_booking_delay is None
assert agenda.maximal_booking_delay is None
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_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)
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):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'Include Agenda' in resp.text
assert 'Options' in resp.text
assert 'Export' in resp.text
assert 'Delete' in resp.text
assert 'Included Agendas' in resp.text
assert 'Add Excluded Period' in resp.text
assert "This virtual agenda doesn't include any agenda yet" in resp.text
# No meeting types yet
assert 'Meeting Types' not in resp.text
# No absence yet
assert 'Excluded Periods' not in resp.text
def test_virtual_agenda_settings(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)
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
TimePeriod.objects.create(
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert "This virtual agenda doesn't include any agenda yet" not in resp.text
for real_agenda in [meeting_agenda_1, meeting_agenda_2]:
assert real_agenda.label in resp.text
assert '/manage/agendas/%s/settings' % real_agenda.pk in resp.text
assert 'Meeting Types' in resp.text
assert 'MT' in resp.text
assert 'mt' in resp.text
assert '10' in resp.text
assert 'Excluded Periods' in resp.text
assert 'Monday' in resp.text
# Error message when incompatible meeting types
mt2.delete()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert "This virtual agenda doesn't have any meeting type." in resp.text
def test_virtual_agenda_settings_include(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
Agenda.objects.create(label='Event agenda', kind='events')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
MeetingType.objects.create(label='MT', duration=30, agenda=meeting_agenda_1)
Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Include Agenda')
# Only meetings agenda are proposed (2) + 1 empty choice = 3
assert len(resp.form['real_agenda'].options) == 3
# Include a real agenda
resp.form['real_agenda'].value = meeting_agenda_1.pk
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert VirtualMember.objects.get(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
resp = resp.follow()
resp = resp.click('Include Agenda')
# The previously include agenda is not proposed any more
assert len(resp.form['real_agenda'].options) == 2
def test_virtual_agenda_settings_add_excluded_period(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Add Excluded Period')
resp.form['weekdays-0'].checked = True
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '17:00'
resp = resp.form.submit()
tp = TimePeriod.objects.get(agenda=agenda)
assert tp.weekday == 0
assert tp.start_time.hour == 10
assert tp.start_time.minute == 0
assert tp.end_time.hour == 17
assert tp.end_time.minute == 0
resp = resp.follow()
assert u'Monday / 10 a.m. → 5 p.m.' in resp.text
def test_virtual_agenda_settings_edit_excluded_period(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
tp = TimePeriod.objects.create(
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
url = '/manage/timeperiods/%s/edit' % tp.pk
resp = resp.click(href=url)
resp.form['start_time'] = '11:00'
resp = resp.form.submit()
tp = TimePeriod.objects.get(agenda=agenda)
assert tp.weekday == 0
assert tp.start_time.hour == 11
assert tp.start_time.minute == 0
assert tp.end_time.hour == 18
assert tp.end_time.minute == 0
def test_virtual_agenda_settings_delete_excluded_period(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
tp = TimePeriod.objects.create(
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
url = '/manage/timeperiods/%s/delete' % tp.pk
resp = resp.click(href=url)
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert TimePeriod.objects.count() == 0
def test_virtual_agenda_settings_include_incompatible_agenda(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')
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
app = login(app)
# refused because different slug
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mtt', duration=10)
resp = app.get('/manage/agendas/%s/add-virtual-member' % agenda.pk)
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;MT&quot; (10 minutes) (identifier: mt) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
# refused because different duration
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=15)
resp = app.get('/manage/agendas/%s/add-virtual-member' % agenda.pk)
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;MT&quot; (10 minutes) (identifier: mt) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
# refused because different label
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MTT', slug='mt', duration=10)
resp = app.get('/manage/agendas/%s/add-virtual-member' % agenda.pk)
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;MT&quot; (10 minutes) (identifier: mt) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
# refused because has one more meeting type
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='AA', slug='aa', duration=30)
resp = app.get('/manage/agendas/%s/add-virtual-member' % agenda.pk)
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Extra meeting type, &quot;AA&quot;.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
# ok because mt2 is marked as deleted
mt2.deleted = True
mt2.save()
resp = app.get('/manage/agendas/%s/add-virtual-member' % agenda.pk)
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert meeting_agenda_2.virtual_agendas.count() == 1
VirtualMember.objects.filter(real_agenda=meeting_agenda_2).delete()
mt.delete()
mt2.delete()
# refused because has one less meeting type
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
mt2 = MeetingType.objects.create(agenda=meeting_agenda_1, label='AA', slug='aa', duration=30)
resp = app.get('/manage/agendas/%s/add-virtual-member' % agenda.pk)
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;AA&quot; (30 minutes) (identifier: aa) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
mt2.delete()
# deleted meeting type is not checked
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10, deleted=True)
resp = app.get('/manage/agendas/%s/add-virtual-member' % agenda.pk)
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;MT&quot; (10 minutes) (identifier: mt) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
def test_cant_delete_meetingtype_used_by_virtual_agenda(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')
mt1 = MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
# ok because there is only one agenda in the virtual agenda
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % meeting_agenda_1.pk)
resp = resp.click('MT')
resp = resp.click('Delete')
resp = resp.form.submit()
assert not meeting_agenda_1.iter_meetingtypes()
mt1.deleted = False
mt1.save()
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
resp = app.get('/manage/agendas/%s/settings' % meeting_agenda_2.pk)
resp = resp.click('MT')
resp = resp.click('Delete')
assert 'This cannot be removed as it used by a virtual agenda' in resp.text
assert 'disabled' in resp.text
resp = app.post('/manage/meetingtypes/%s/delete' % mt2.pk, status=403)
def test_cant_modify_meetingtype_used_by_virtual_agenda(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')
mt1 = MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
app = login(app)
# ok because there is only one agenda in the virtual agenda
resp = app.get('/manage/meetingtypes/%s/edit' % mt1.pk)
resp.form['label'].value = 'MTT'
resp = resp.form.submit()
assert MeetingType.objects.get(agenda=meeting_agenda_1, label='MTT', slug='mt', duration=10)
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MTT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
app = login(app)
resp = app.get('/manage/meetingtypes/%s/edit' % mt2.pk)
resp.form['label'].value = 'Oho'
resp = resp.form.submit()
assert 'This meetingtype is used by a virtual agenda' in resp.text
mt = MeetingType.objects.get(pk=mt2.pk)
assert mt.label == 'MTT'
def test_cant_add_meetingtype_if_virtual_agenda(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')
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
app = login(app)
# ok because there is only one agenda in the virtual agenda
resp = app.get('/manage/agendas/%s/add-meeting-type' % meeting_agenda_1.pk)
resp.form['duration'].value = '12'
resp.form['label'].value = 'Oho'
resp = resp.form.submit()
assert MeetingType.objects.filter(agenda=meeting_agenda_1).count() == 2
MeetingType.objects.get(agenda=meeting_agenda_1, label='Oho').delete()
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
app = login(app)
resp = app.get('/manage/agendas/%s/add-meeting-type' % meeting_agenda_1.pk)
resp.form['duration'].value = '12'
resp.form['label'].value = 'Oho'
resp = resp.form.submit()
assert 'Can&#39;t add a meetingtype to an agenda that is included in a virtual agenda.' in resp.text
assert MeetingType.objects.filter(agenda=meeting_agenda_1).count() == 1
def test_event_digit_slug(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.maximal_booking_delay = 0
agenda.save()
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
app = login(app)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['slug'] = 42
resp = resp.form.submit()
assert 'value cannot be a number' in resp.text
def test_duplicate_agenda(app, admin_user):
agenda = Agenda.objects.create(label=u'Foo Bar', slug='foo-bar', kind='meetings')
assert Agenda.objects.count() == 1
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Duplicate')
resp = resp.form.submit()
assert Agenda.objects.count() == 2
new_agenda = Agenda.objects.exclude(pk=agenda.pk).first()
assert resp.location == '/manage/agendas/%s/settings' % new_agenda.pk
assert new_agenda.pk != agenda.pk
resp = resp.follow()
assert 'copy-of-foo-bar' in resp.text
resp = resp.click('Duplicate')
resp.form['label'] = 'hop'
resp = resp.form.submit().follow()
assert 'hop' in resp.text
def test_booking_cancellation_meetings_agenda(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()
today = datetime.date(2018, 11, 10) # fixed day
timeperiod_weekday = today.weekday()
timeperiod = TimePeriod(
desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
timeperiod.save()
# book a slot
app.authorization = ('Basic', ('john.doe', 'password'))
bookings_resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
booking_url = bookings_resp.json['data'][0]['api']['fillslot_url']
booking_json = app.post_json(booking_url, params={'backoffice_url': 'http://example.org/'}).json
app.reset()
login(app)
booking = Booking.objects.get(pk=booking_json['booking_id'])
date = booking.event.start_datetime
month_view_url = '/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month)
resp = app.get(month_view_url)
assert len(resp.pyquery.find('div.booking a.cancel')) == 1 # cancel button is shown
resp = resp.click('Cancel')
# no callback url was provided at booking, warn user cancellation is forbidden
assert 'no callback url' in resp.text
assert not 'Proceed with cancellation' in resp.text
booking.delete()
# provide callback url this time
booking_url2 = bookings_resp.json['data'][1]['api']['fillslot_url']
booking_json2 = app.post_json(
booking_url2, params={'cancel_callback_url': 'http://example.org/jump/trigger/'}
).json
resp = app.get(month_view_url)
resp = resp.click('Cancel')
assert not 'no callback url' in resp.text
# a signed request is sent to callback_url
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
mock_response = mock.Mock(status_code=200)
mock_send.return_value = mock_response
resp = resp.form.submit()
url = mock_send.call_args[0][0].url
assert check_query(url.split('?', 1)[-1], 'chrono')
booking2 = Booking.objects.get(pk=booking_json2['booking_id'])
resp = resp.follow()
assert not resp.pyquery.find('div.booking')
assert booking2.cancellation_datetime
# request fails
booking_url3 = bookings_resp.json['data'][2]['api']['fillslot_url']
booking_json3 = app.post_json(
booking_url3, params={'cancel_callback_url': 'http://example.org/jump/trigger/'}
).json
booking3 = Booking.objects.get(pk=booking_json3['booking_id'])
def mocked_requests_connection_error(*args, **kwargs):
raise requests.exceptions.ConnectionError('unreachable')
resp = app.get(month_view_url)
resp = resp.click('Cancel')
assert resp.form['disable_trigger'].attrs['type'] == 'hidden'
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
mock_response = mock.Mock(status_code=200)
mock_send.return_value = mock_response
mock_send.side_effect = mocked_requests_connection_error
resp = resp.form.submit()
assert 'error' in resp.text
booking3.refresh_from_db()
assert not booking3.cancellation_datetime
# there is an option to force cancellation
resp.form['disable_trigger'] = True
resp = resp.form.submit()
booking3.refresh_from_db()
assert booking3.cancellation_datetime
# test day view
day_view_url = '/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day)
booking_url4 = bookings_resp.json['data'][3]['api']['fillslot_url']
booking_json4 = app.post(booking_url4).json
resp = app.get(day_view_url)
resp = resp.click('Cancel')
resp = resp.form.submit()
assert resp.location.endswith(day_view_url)
booking4 = Booking.objects.get(pk=booking_json4['booking_id'])
assert booking4.cancellation_datetime
def test_booking_cancellation_events_agenda(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event(label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda)
event.save()
booking = Booking.objects.create(event=event)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.id, event.id))
assert 'Bookings (1/10)' in resp.text
resp = resp.click('Cancel', href='bookings/')
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/events/%s/' % (agenda.id, event.id))
booking.refresh_from_db()
assert booking.cancellation_datetime
resp = resp.follow()
assert 'Bookings (0/10)' in resp.text
def test_event_cancellation(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
)
login(app)
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Bookings (0/10)' in resp.text
resp = resp.click('Cancel', href='/cancel')
assert 'related bookings' not in resp.text
Booking.objects.create(event=event)
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Bookings (2/10)' in resp.text
resp = resp.click('Cancel', href='manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
assert '2 related bookings will also be cancelled.' in resp.text
resp = resp.form.submit().follow()
assert 'Cancelled' in resp.text
assert 'Bookings (0/10)' in resp.text
assert Booking.objects.filter(event=event, cancellation_datetime__isnull=False).count() == 2
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert '/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk) not in resp.text
def test_event_cancellation_error_report(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
)
day = event.start_datetime
def mocked_requests_connection_error(*args, **kwargs):
raise requests.exceptions.ConnectionError('unreachable')
for i in range(5):
Booking.objects.create(event=event, cancel_callback_url='http://example.org/jump/trigger/')
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
resp = resp.click('Cancellation error reports')
assert 'No error report' in resp.text
resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
resp = resp.form.submit().follow()
assert 'Cancellation in progress' in resp.text
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
mock_response = mock.Mock(status_code=200)
mock_send.return_value = mock_response
mock_send.side_effect = mocked_requests_connection_error
call_command('cancel_events')
event.refresh_from_db()
assert not event.cancelled and not event.cancellation_scheduled
assert not Booking.objects.filter(cancellation_datetime__isnull=False).exists()
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
assert 'Errors occured during cancellation of event "xyz".' in resp.text
# warning doesn't go away
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
assert 'Errors occured during cancellation of event "xyz".' in resp.text
resp = resp.click('Details')
assert resp.text.count('unreachable') == 5
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
assert not 'Errors occured during cancellation of event "xyz".' in resp.text
resp = resp.click('Cancellation error reports')
assert '(5 failures)' in resp.text
resp = resp.click(str(event))
resp = resp.click('Force cancellation')
resp = resp.form.submit().follow()
event.refresh_from_db()
assert event.cancelled and not event.cancellation_scheduled
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 5
def test_event_cancellation_forbidden(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
)
Booking.objects.create(event=event)
Booking.objects.create(event=event, backoffice_url='http://example.org/backoffice/xx/')
login(app)
resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
assert 'event has bookings with no callback url configured' in resp.text
assert 'Proceed with cancellation' not in resp.text
def test_event_booking_form_url(settings, app, admin_user):
settings.TEMPLATE_VARS = {'eservices_url': 'http://demarches/'}
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
)
day = event.start_datetime
login(app)
assert agenda.get_booking_form_url() is None
resp = app.get('/manage/agendas/%d/%d/%d/' % (agenda.pk, day.year, day.month))
assert 'Booking form' not in resp.text
resp = app.get('/manage/agendas/%d/events/open/' % agenda.pk)
assert 'Booking form' not in resp.text
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Booking form' not in resp.text
agenda.booking_form_url = '{{ eservices_url }}backoffice/submission/inscription-aux-activites/'
agenda.save()
assert (
agenda.get_booking_form_url() == 'http://demarches/backoffice/submission/inscription-aux-activites/'
)
resp = app.get('/manage/agendas/%d/%d/%d/' % (agenda.pk, day.year, day.month))
assert (
'<a class="link-action-text" href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
% (agenda.slug, event.slug)
in resp.text
)
resp = app.get('/manage/agendas/%d/events/open/' % agenda.pk)
assert (
'<a class="link-action-text" href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
% (agenda.slug, event.slug)
in resp.text
)
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert (
'<a href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
% (agenda.slug, event.slug)
in resp.text
)
def test_agenda_notifications(app, admin_user, managers_group):
agenda = Agenda.objects.create(label='Events', kind='events')
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'Notifications' in resp.text
assert 'Notifications are disabled' in resp.text
resp = resp.click('Configure', href='notifications')
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'Notifications are disabled' in resp.text
resp = resp.click('Configure', href='notifications')
resp.form['cancelled_event'] = 'use-email-field'
resp = resp.form.submit().follow()
assert 'Notifications are disabled' in resp.text
agenda.view_role = managers_group
agenda.save()
resp = resp.click('Configure', href='notifications')
resp.form['cancelled_event_emails'] = 'hop@entrouvert.com, top@entrouvert.com'
resp.form['almost_full_event'] = 'edit-role'
option = resp.form['almost_full_event'].selectedIndex
assert resp.form['almost_full_event'].options[option][2] == 'Edit Role (undefined)'
resp.form['full_event'] = 'view-role'
option = resp.form['full_event'].selectedIndex
assert resp.form['full_event'].options[option][2] == 'View Role (Managers)'
resp = resp.form.submit().follow()
settings = agenda.notifications_settings
assert settings.almost_full_event == 'edit-role'
assert settings.full_event == 'view-role'
assert settings.cancelled_event == 'use-email-field'
assert settings.cancelled_event_emails == ['hop@entrouvert.com', 'top@entrouvert.com']
assert 'Cancelled event: hop@entrouvert.com, top@entrouvert.com will be notified' in resp.text
assert 'Almost full event (90%): Edit Role (undefined) will be notified' in resp.text
assert 'Full event: View Role (Managers) will be notified' in resp.text
agenda.edit_role = Group.objects.create(name='hop')
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'Almost full event (90%): Edit Role (hop) will be notified' in resp.text
def test_agenda_notifications_no_old_events(app, admin_user, mailoutbox):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, label='Old event')
event.cancelled = True
event.save()
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Configure', href='notifications')
resp.form['cancelled_event'] = 'use-email-field'
resp.form['cancelled_event_emails'] = 'hop@entrouvert.com'
resp.form.submit()
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, label='New event')
event.cancelled = True
event.save()
call_command('send_email_notifications')
# no notification is sent for old event
assert len(mailoutbox) == 1
assert 'New event' in mailoutbox[0].subject
def test_manager_reminders(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'Booking reminders' in resp.text
assert 'Reminders are disabled' in resp.text
resp = resp.click('Configure', href='reminder')
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'Reminders are disabled' in resp.text
resp = resp.click('Configure', href='reminder')
assert not 'SMS' in resp.text
resp.form['days'] = 3
resp.form['send_email'] = True
resp.form['email_extra_info'] = 'test'
resp = resp.form.submit().follow()
assert 'Users will be reminded of their booking by email, 3 days in advance.' in resp.text
with override_settings(SMS_URL='https://passerelle.test.org/sms/send/', SMS_SENDER='EO'):
resp = resp.click('Configure', href='reminder')
resp.form['send_sms'] = True
resp = resp.form.submit().follow()
assert 'Users will be reminded of their booking both by email and by SMS, 3 days in advance.' in resp.text
agenda = Agenda.objects.create(label='Meetings', kind='meetings')
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'Booking reminders' in resp.text
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert not 'Booking reminders' in resp.text
def test_manager_reminders_preview(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
AgendaReminderSettings.objects.create(
agenda=agenda,
days=1,
send_email=True,
email_extra_info='An ID will be required in order to process your form.',
send_sms=True,
sms_extra_info='Take ID card.',
)
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert (
'Users will be reminded of their booking both by email and by SMS, one day in advance.' in resp.text
)
resp = resp.click('Preview email')
assert 'Users will receive the following email:' in resp.text
assert len(resp.pyquery.find('p.email-subject')) == 1
assert '<strong>Subject:</strong> Reminder for your booking tomorrow at 2:30 p.m.' in resp.text
assert 'You have a booking for event "{{ event_label }}", on Tuesday 2 June at 2:30 p.m..' in resp.text
assert 'An ID will be required' in resp.text
resp = resp.click('Return to settings')
resp = resp.click('Preview SMS')
assert 'Users will receive the following SMS:' in resp.text
assert (
'Reminder: you have a booking for event "{{ event_label }}", on 02/06 at 2:30 p.m.. Take ID card.'
in resp.text
)
def test_no_unavailability_calendar(app, admin_user):
app = login(app)
# empty unavailability calendars list
resp = app.get('/manage/')
resp = resp.click('Unavailability calendars')
assert "This site doesn't have any unavailability calendar yet" in resp.text
# on the agenda settings page, no unavailability calendar reference
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'unavailability calendar' not in resp.text
def test_add_unavailability_calendar(app, admin_user):
app = login(app)
resp = app.get('/manage/')
resp = resp.click('Unavailability calendars')
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp = resp.form.submit()
unavailability_calendar = UnavailabilityCalendar.objects.latest('pk')
assert resp.location.endswith('/manage/unavailability-calendar/%s/' % unavailability_calendar.pk)
assert unavailability_calendar.label == 'Foo bar'
assert unavailability_calendar.slug == 'foo-bar'
resp = resp.follow()
assert 'This unavailability calendar is not used yet.' in resp.text
resp = app.get('/manage/unavailability-calendars/')
assert 'Foo bar' in resp.text
assert 'foo-bar' in resp.text
def test_used_unavailability_calendar(app, admin_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar')
agenda = Agenda.objects.create(label='Foo', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='desk')
app = login(app)
url = '/manage/unavailability-calendar/%s/' % unavailability_calendar.pk
resp = app.get(url)
assert 'This unavailability calendar is not used yet.' in resp.text
assert 'Foo' not in resp.text
desk.unavailability_calendars.add(unavailability_calendar)
resp = app.get(url)
assert 'This unavailability calendar is not used yet.' not in resp.text
assert 'Foo' in resp.text
def test_edit_unavailability_calendar(app, admin_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo')
app = login(app)
settings_url = '/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk
resp = app.get(settings_url)
resp = resp.click('Options')
resp.form['label'] = 'Bar'
resp = resp.form.submit()
assert resp.location.endswith(settings_url)
resp = resp.follow()
assert 'Bar' in resp.text
unavailability_calendar.refresh_from_db()
assert unavailability_calendar.label == 'Bar'
def test_delete_unavailability_calendar(app, admin_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo')
app = login(app)
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk)
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/manage/unavailability-calendars/')
resp = resp.follow()
assert 'Foo' not in resp.text
assert UnavailabilityCalendar.objects.count() == 0
def test_unavailability_calendar_add_time_period_exeptions(app, admin_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo')
app = login(app)
resp = app.get('/manage/unavailability-calendar/%s/' % unavailability_calendar.pk)
resp = resp.click('Settings')
assert 'There is no unavailabilities yet' in resp.text
resp = resp.click('Add Unavailability')
assert 'all_desks' not in resp.form.fields
today = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
tomorrow = make_aware(today + datetime.timedelta(days=1))
resp.form['label'] = 'Exception 1'
resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
resp.form['start_datetime_1'] = '08:00'
resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
resp.form['end_datetime_1'] = '16:00'
resp = resp.form.submit().follow()
assert 'Exception 1' in resp.text
time_period_exception = TimePeriodException.objects.first()
assert time_period_exception.unavailability_calendar == unavailability_calendar
assert time_period_exception.desk is None
assert time_period_exception.label == 'Exception 1'
def test_unavailability_calendar_edit_time_period_exeptions(app, admin_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo')
time_period_exception = TimePeriodException.objects.create(
unavailability_calendar=unavailability_calendar,
label='Exception 1',
start_datetime=now() - datetime.timedelta(days=2),
end_datetime=now() - datetime.timedelta(days=1),
)
app = login(app)
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk)
assert 'Exception 1' in resp.text
resp = resp.click(href='/manage/time-period-exceptions/%s/edit' % time_period_exception.pk)
assert 'all_desks' not in resp.form.fields
resp.form['label'] = 'Exception foo'
resp = resp.form.submit().follow()
assert 'Exception foo' in resp.text
time_period_exception.refresh_from_db()
assert 'Exception foo' == time_period_exception.label
# with an error
resp = app.get('/manage/time-period-exceptions/%s/edit' % time_period_exception.pk)
resp.form['start_datetime_0'] = now().strftime('%Y-%m-%d')
resp.form['end_datetime_0'] = now().strftime('%Y-%m-%d')
resp = resp.form.submit()
assert '<ul class="errorlist">' in resp.text
def test_unavailability_calendar_delete_time_period_exeptions(app, admin_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo')
time_period_exception = TimePeriodException.objects.create(
unavailability_calendar=unavailability_calendar,
label='Exception 1',
start_datetime=now() - datetime.timedelta(days=2),
end_datetime=now() - datetime.timedelta(days=1),
)
app = login(app)
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk)
assert 'Exception 1' in resp.text
resp = resp.click(href='/manage/time-period-exceptions/%s/delete' % time_period_exception.pk)
resp = resp.form.submit().follow()
assert 'Exception foo' not in resp.text
assert unavailability_calendar.timeperiodexception_set.count() == 0
def test_activate_unavailability_calendar_in_desk(app, admin_user):
app = login(app)
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='desk')
exceptions_url = '/manage/agendas/desk/%s/import-exceptions-from-ics/' % desk.pk
resp = app.get(exceptions_url)
assert 'calendar' in resp.text
assert 'enable' in resp.text
resp = resp.click(
href='/manage/desk/%s/unavailability-calendar/%s/toogle/' % (desk.pk, unavailability_calendar.pk)
)
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk)
resp = app.get(exceptions_url)
assert 'calendar' in resp.text
assert 'disable' in resp.text
assert desk.unavailability_calendars.get(pk=unavailability_calendar.pk)
def test_deactivate_unavailability_calendar_in_desk(app, admin_user):
app = login(app)
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='desk')
desk.unavailability_calendars.add(unavailability_calendar)
exceptions_url = '/manage/agendas/desk/%s/import-exceptions-from-ics/' % desk.pk
resp = app.get(exceptions_url)
assert 'calendar' in resp.text
assert 'disable' in resp.text
resp = resp.click(
href='/manage/desk/%s/unavailability-calendar/%s/toogle/' % (desk.pk, unavailability_calendar.pk)
)
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk)
resp = app.get(exceptions_url)
assert 'calendar' in resp.text
assert 'enable' in resp.text
assert desk.unavailability_calendars.count() == 0
def test_unavailability_calendar_homepage_permission(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
app = login(app, username='manager', password='manager')
resp = app.get('/manage/', status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
resp = app.get('/manage/')
resp = resp.click('Unavailability calendars')
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
resp = app.get('/manage/')
resp = resp.click('Unavailability calendars')
def test_unavailability_calendar_list_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
app = login(app, username='manager', password='manager')
app.get('/manage/unavailability-calendars/', status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
resp = app.get('/manage/unavailability-calendars/')
assert 'Calendar 1' in resp.text
assert 'New' not in resp.text
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
assert 'Calendar 1' in resp.text
assert 'New' not in resp.text
def test_unavailability_calendar_add_permissions(app, manager_user):
app = login(app, username='manager', password='manager')
url = '/manage/unavailability-calendar/add/'
app.get(url, status=403)
def test_unavailability_calendar_detail_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
app = login(app, username='manager', password='manager')
detail_url = '/manage/unavailability-calendar/%s/' % unavailability_calendar.pk
resp = app.get(detail_url, status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
resp = app.get(detail_url)
assert 'Settings' not in resp.text
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
resp = app.get(detail_url)
assert 'Settings' in resp.text
def test_unavailability_calendar_edit_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
app = login(app, username='manager', password='manager')
url = '/manage/unavailability-calendar/%s/edit/' % unavailability_calendar.pk
app.get(url, status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
app.get(url, status=403)
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
app.get(url)
def test_unavailability_calendar_delete_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
app = login(app, username='manager', password='manager')
url = '/manage/unavailability-calendar/%s/delete/' % unavailability_calendar.pk
app.get(url, status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
app.get(url, status=403)
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
app.get(url, status=403)
def test_unavailability_calendar_settings_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
app = login(app, username='manager', password='manager')
settings_url = '/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk
app.get(settings_url, status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
app.get(settings_url, status=403)
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
app.get(settings_url)
def test_unavailability_calendar_add_unavailability_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
app = login(app, username='manager', password='manager')
url = '/manage/unavailability-calendar/%s/add-unavailability' % unavailability_calendar.pk
app.get(url, status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
app.get(url, status=403)
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
app.get(url)
def test_unavailability_calendar_edit_unavailability_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
time_period_exception = TimePeriodException.objects.create(
unavailability_calendar=unavailability_calendar,
start_datetime=now() - datetime.timedelta(days=2),
end_datetime=now() - datetime.timedelta(days=1),
)
app = login(app, username='manager', password='manager')
url = '/manage/time-period-exceptions/%s/edit' % time_period_exception.pk
app.get(url, status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
app.get(url, status=403)
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
app.get(url)
def test_unavailability_calendar_delete_unavailability_permissions(app, manager_user):
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1')
time_period_exception = TimePeriodException.objects.create(
unavailability_calendar=unavailability_calendar,
start_datetime=now() - datetime.timedelta(days=2),
end_datetime=now() - datetime.timedelta(days=1),
)
app = login(app, username='manager', password='manager')
url = '/manage/time-period-exceptions/%s/delete' % time_period_exception.pk
app.get(url, status=403)
group = manager_user.groups.all()[0]
unavailability_calendar.view_role = group
unavailability_calendar.edit_role = None
unavailability_calendar.save()
app.get(url, status=403)
unavailability_calendar.view_role = None
unavailability_calendar.edit_role = group
unavailability_calendar.save()
app.get(url)