import datetime import pytest from django.db import connection from django.test import override_settings from django.test.utils import CaptureQueriesContext from django.utils.timezone import localtime, now from chrono.agendas.models import ( Agenda, Desk, Event, MeetingType, Resource, TimePeriod, TimePeriodException, TimePeriodExceptionSource, UnavailabilityCalendar, ) from tests.utils import login pytestmark = pytest.mark.django_db def test_options_meetings_agenda_delays(app, admin_user): agenda = Agenda.objects.create(label='Foo bar', kind='meetings', maximal_booking_delay=2) assert agenda.maximal_booking_delay == 2 app = login(app) url = '/manage/agendas/%s/booking-delays' % agenda.pk resp = app.get(url) assert 'minimal_booking_delay_in_working_days' not in resp.context['form'].fields resp.form['maximal_booking_delay'] = None resp = resp.form.submit() agenda.refresh_from_db() assert agenda.maximal_booking_delay == 2 def test_options_meetings_agenda_num_queries(app, admin_user, managers_group): agenda = Agenda.objects.create( label='Agenda', kind='meetings', edit_role=managers_group, view_role=managers_group ) resource = Resource.objects.create(label='Resource') agenda.resources.add(resource) for i in range(0, 10): MeetingType.objects.create(agenda=agenda, label='MT %s' % i) desk = Desk.objects.create(agenda=agenda, label='Desk') source = TimePeriodExceptionSource.objects.create(desk=desk, ics_url='https://example.com/test.ics') calendar = UnavailabilityCalendar.objects.create(label='foo') calendar.desks.add(desk) for weekday in (0, 6): TimePeriod.objects.create( weekday=weekday, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0) ) desk2 = desk.duplicate() assert agenda.is_available_for_simple_management() is True app = login(app) with CaptureQueriesContext(connection) as ctx: app.get('/manage/agendas/%s/settings' % agenda.pk) assert len(ctx.captured_queries) == 13 # check with different kind of exceptions: # exception starts and ends in the past exception = TimePeriodException.objects.create( desk=desk, start_datetime=now() - datetime.timedelta(days=2), end_datetime=now() - datetime.timedelta(days=1), ) desk2.delete() desk2 = desk.duplicate() assert agenda.is_available_for_simple_management() is True with CaptureQueriesContext(connection) as ctx: app.get('/manage/agendas/%s/settings' % agenda.pk) assert len(ctx.captured_queries) == 13 # exception starts in the past but ends in the futur exception.delete() exception = TimePeriodException.objects.create( desk=desk, source=source, start_datetime=now() - datetime.timedelta(days=1), end_datetime=now() + datetime.timedelta(days=1), ) desk2.delete() desk2 = desk.duplicate() assert agenda.is_available_for_simple_management() is True with CaptureQueriesContext(connection) as ctx: app.get('/manage/agendas/%s/settings' % agenda.pk) assert len(ctx.captured_queries) == 13 # exception in more than 2 weeks exception.delete() exception = TimePeriodException.objects.create( unavailability_calendar=calendar, start_datetime=now() + datetime.timedelta(days=20), end_datetime=now() + datetime.timedelta(days=21), ) desk2.delete() desk2 = desk.duplicate() assert agenda.is_available_for_simple_management() is True with CaptureQueriesContext(connection) as ctx: app.get('/manage/agendas/%s/settings' % agenda.pk) assert len(ctx.captured_queries) == 13 def test_meetings_agenda_add_meeting_type(app, admin_user): agenda = Agenda.objects.create(label='Foo bar', kind='meetings') 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'] = '0' assert 'deleted' not in resp.form.fields resp = resp.form.submit() assert 'Ensure this value is greater than or equal to 1.' in resp.text assert not MeetingType.objects.exists() resp.form['duration'] = '60' resp = resp.form.submit() assert resp.location.endswith('/manage/agendas/%s/settings#open:meeting-types' % agenda.pk) meeting_type = MeetingType.objects.get(agenda=agenda) assert meeting_type.label == 'Blah' assert meeting_type.duration == 60 assert meeting_type.deleted is False resp = resp.follow() assert 'Blah' in resp.text def test_meetings_agenda_edit_meeting_type(app, admin_user): agenda = Agenda.objects.create(label='Foo bar', kind='meetings') meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah') meeting_type2 = MeetingType.objects.create(agenda=agenda, label='Other') other_agenda = Agenda.objects.create(label='Foo bar', kind='meetings') other_meeting_type = MeetingType.objects.create(agenda=other_agenda, label='Blah') assert meeting_type.slug == other_meeting_type.slug app = login(app) resp = app.get('/manage/meetingtypes/%s/edit' % meeting_type.pk) resp.form['label'] = 'Foo' resp.form['duration'] = '120' assert 'deleted' not in resp.form.fields resp.form.submit().follow() meeting_type.refresh_from_db() assert meeting_type.label == 'Foo' assert meeting_type.slug == other_meeting_type.slug assert meeting_type.duration == 120 assert meeting_type.deleted is False # check slug edition resp = app.get('/manage/meetingtypes/%s/edit' % meeting_type.pk) resp.form['slug'] = meeting_type2.slug resp = resp.form.submit() assert resp.context['form'].errors['slug'] == ['Another meeting type exists with the same identifier.'] # check duration change desk = Desk.objects.create(agenda=agenda, label='Desk A') for hour in [12, 14]: Event.objects.create( agenda=agenda, places=1, desk=desk, meeting_type=meeting_type, start_datetime=localtime(now()).replace(hour=hour, minute=00), ) resp = app.get('/manage/meetingtypes/%s/edit' % meeting_type.pk) resp.form['duration'] = '180' resp = resp.form.submit() assert resp.context['form'].errors['duration'] == [ 'Not possible to change duration: existing events will overlap.' ] # no errors resp = app.get('/manage/meetingtypes/%s/edit' % meeting_type.pk) resp.form['duration'] = '120' resp = resp.form.submit() assert resp.location.endswith('/manage/agendas/%s/settings#open:meeting-types' % agenda.pk) resp = app.get('/manage/meetingtypes/%s/edit' % meeting_type.pk) resp.form['duration'] = '90' resp = resp.form.submit().follow() resp = app.get('/manage/meetingtypes/%s/edit' % meeting_type.pk) resp.form['duration'] = '120' resp = resp.form.submit().follow() def test_meetings_agenda_delete_meeting_type(app, admin_user): agenda = Agenda(label='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#open:meeting-types' % agenda.pk) 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_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') agenda.desk_simple_management = False agenda.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 # unknown pk app.get('/manage/agendas/0/add-desk', status=404) # wrong kind agenda.kind = 'virtual' agenda.save() app.get('/manage/agendas/%s/add-desk' % agenda.pk, status=404) agenda.kind = 'events' agenda.save() app.get('/manage/agendas/%s/add-desk' % agenda.pk, status=404) @override_settings( EXCEPTIONS_SOURCES={ 'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'}, } ) def test_meetings_agenda_add_desk_from_another(app, admin_user): agenda = Agenda.objects.create(label='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 assert desk.timeperiodexceptionsource_set.count() == 0 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 assert ( new_desk.timeperiodexceptionsource_set.count() == 0 ) # holidays not automatically added via duplication @override_settings( EXCEPTIONS_SOURCES={ 'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'}, } ) def test_meetings_agenda_add_desk_simple_management(app, admin_user): agenda = Agenda.objects.create(label='Foo bar', kind='meetings', desk_simple_management=True) 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 assert desk.timeperiodexceptionsource_set.count() == 0 app = login(app) resp = app.get('/manage/agendas/%s/add-desk' % agenda.pk) resp.form['label'] = 'Desk B' assert 'copy_from' not in resp.context['form'].fields 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 assert ( new_desk.timeperiodexceptionsource_set.count() == 0 ) # holidays not automatically added via duplication # ok if no desks (should not happen) Desk.objects.all().delete() resp = app.get('/manage/agendas/%s/add-desk' % agenda.pk) resp.form['label'] = 'Desk' assert 'copy_from' not in resp.context['form'].fields resp = resp.form.submit().follow() assert Desk.objects.count() == 1 new_desk = Desk.objects.latest('pk') assert new_desk.timeperiodexceptionsource_set.count() == 1 def test_meetings_agenda_edit_desk(app, admin_user): agenda = Agenda.objects.create(label='Foo bar', kind='meetings') desk = Desk.objects.create(agenda=agenda, label='Desk A') desk2 = Desk.objects.create(agenda=agenda, label='Desk B') other_agenda = Agenda.objects.create(label='Foo bar', kind='meetings') other_desk = Desk.objects.create(agenda=other_agenda, label='Desk A') assert other_desk.slug == desk.slug app = login(app) resp = app.get('/manage/desks/%s/edit' % desk.pk) resp.form['label'] = 'Desk C' resp.form['slug'] = desk.slug resp = resp.form.submit().follow() assert 'Desk A' not in resp.text assert 'Desk B' in resp.text assert 'Desk C' in resp.text desk.refresh_from_db() assert desk.label == 'Desk C' assert desk.slug == other_desk.slug # check slug edition resp = app.get('/manage/desks/%s/edit' % desk.pk) resp.form['slug'] = desk2.slug resp = resp.form.submit() assert resp.context['form'].errors['slug'] == ['Another desk exists with the same identifier.'] # unknown pk app.get('/manage/desks/0/edit', status=404) # wrong kind agenda.kind = 'virtual' agenda.save() app.get('/manage/desks/%s/edit' % desk.pk, status=404) agenda.kind = 'events' agenda.save() app.get('/manage/desks/%s/edit' % desk.pk, status=404) def test_meetings_agenda_delete_desk(app, admin_user): agenda = Agenda.objects.create(label='Foo bar', kind='meetings') Desk.objects.create(agenda=agenda, label='Desk A') desk_b = Desk.objects.create(agenda=agenda, label='Desk B') app = login(app) resp = app.get('/manage/agendas/%s/settings' % agenda.pk) resp = resp.click('Desk A') resp = resp.click('Delete') resp = resp.form.submit() assert resp.location.endswith('/manage/agendas/%s/settings#open:desks' % agenda.pk) assert Desk.objects.count() == 1 # only one desk app.get('/manage/desks/%s/delete' % desk_b.pk, status=404) desk_a = Desk.objects.create(agenda=agenda, label='Desk A') # unknown pk app.get('/manage/desks/0/delete', status=404) # wrong kind agenda.kind = 'virtual' agenda.save() app.get('/manage/desks/%s/delete' % desk_a.pk, status=404) agenda.kind = 'events' agenda.save() app.get('/manage/desks/%s/delete' % desk_a.pk, status=404)