chrono/tests/manager/test_event.py

3437 lines
140 KiB
Python

import codecs
import datetime
import json
from unittest import mock
import pytest
import requests
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 webtest import Upload
from chrono.agendas.models import Agenda, Booking, Desk, Event, EventsType, Lease, Subscription
from chrono.apps.snapshot.models import AgendaSnapshot
from chrono.utils.lingo import CheckType
from chrono.utils.timezone import localtime, make_aware, now
from tests.utils import login
pytestmark = pytest.mark.django_db
def test_add_event(app, admin_user):
events_type = EventsType.objects.create(
label='Foo', custom_fields=[{'varname': 'foo', 'label': 'Foo', 'field_type': 'text'}]
)
agenda = Agenda.objects.create(label='Foo bar', maximal_booking_delay=0, events_type=events_type)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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')
assert 'custom_field_foo' not in resp.context['form'].fields
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()
assert AgendaSnapshot.objects.count() == 1
event = Event.objects.get(places=10)
assert event.publication_datetime 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 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
)
@pytest.mark.freeze_time('2021-05-06 14:00')
def test_add_recurring_event(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click('New Event')
resp.form['start_datetime_0'] = '2021-06-01'
resp.form['start_datetime_1'] = '17:00'
resp.form['places'] = 10
resp.form['frequency'] = 'unique' # not a recurring event
resp.form['recurrence_days'] = [2]
resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 1
event = Event.objects.get()
assert event.recurrence_days is None
event.delete()
# add recurring event
resp.form['frequency'] = 'recurring'
resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 2
event = Event.objects.get(primary_event__isnull=True)
assert event.recurrence_days == [2]
assert Event.objects.filter(primary_event=event).count() == 49
event.delete()
# add recurring event with end date
resp.form['recurrence_end_date'] = '2021-07-01'
resp.form.submit().follow()
event = Event.objects.get(primary_event__isnull=True)
assert event.recurrence_days == [2]
assert Event.objects.filter(primary_event=event).count() == 5
# add recurring event with end date in a very long time
resp.form['recurrence_end_date'] = '2030-01-01'
resp = resp.form.submit()
assert 'Recurrence end date cannot be more than 3 years from now' in resp.text
def test_add_event_on_missing_agenda(app, admin_user):
app = login(app)
app.get('/manage/agendas/%s/add-event' % '0', status=404)
def test_add_event_as_manager(app, manager_user):
agenda = Agenda(label='Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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_add_event_third_millennium(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
assert Event.objects.filter(agenda=agenda).count() == 0
app = login(app)
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'] = '0022-02-15'
resp.form['start_datetime_1'] = '17:00'
resp.form['places'] = 10
resp.form['duration'] = 45
resp = resp.form.submit()
assert resp.context['form'].errors['start_datetime'] == ['Year must be after 2000.']
assert Event.objects.filter(agenda=agenda).count() == 0
def test_edit_event(settings, app, admin_user):
settings.LANGUAGE_CODE = 'fr-fr' # check date initial value format
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Foo',
start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
places=20,
agenda=agenda,
)
event2 = Event.objects.create(
label='Other',
start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
places=20,
agenda=agenda,
)
assert event.duration is None
assert event.end_datetime is None
other_agenda = Agenda.objects.create(label='Foo bar')
other_event = Event.objects.create(
label='Foo',
start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
places=20,
agenda=other_agenda,
)
assert event.slug == other_event.slug
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_datetime_0'].value == ''
assert resp.form['publication_datetime_1'].value == ''
assert resp.form['duration'].value == ''
assert resp.form['description'].value == ''
resp.form['start_datetime_0'] = '2016-02-16'
resp.form['start_datetime_1'] = '17:00'
resp.form['publication_datetime_0'] = '2020-05-11'
resp.form['publication_datetime_1'] = '12:00'
resp.form['duration'].value = 45
resp.form['places'] = 20
resp.form['description'] = 'A description'
resp = resp.form.submit()
settings.LANGUAGE_CODE = 'en'
resp = resp.follow()
assert AgendaSnapshot.objects.count() == 1
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 str(event.publication_datetime) == '2020-05-11 10:00:00+00:00'
assert str(event.publication_datetime.tzinfo) == 'UTC'
assert event.duration == 45
assert event.end_datetime == event.start_datetime + datetime.timedelta(minutes=45)
assert event.description == 'A description'
assert event.slug == other_event.slug
# check slug edition
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.pk, event.pk))
resp.form['slug'] = event2.slug
resp = resp.form.submit()
assert resp.context['form'].errors['slug'] == ['Another event exists with the same identifier.']
def test_event_digit_slug(app, admin_user):
agenda = Agenda(label='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_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='Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
places=20,
agenda=agenda,
publication_datetime=make_aware(datetime.datetime(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_datetime_0'].value == '2020-05-11'
assert resp.form['publication_datetime_1'].value == '00:00'
resp.form['start_datetime_0'] = '2016-02-16'
resp.form['start_datetime_1'] = '17:00'
resp.form['publication_datetime_0'] = ''
resp.form['publication_datetime_1'] = ''
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_datetime is None
def test_edit_event_with_custom_fields(app, admin_user):
events_type = EventsType.objects.create(
label='Foo',
custom_fields=[
{'varname': 'text', 'label': 'Text', 'field_type': 'text'},
{'varname': 'textarea', 'label': 'TextArea', 'field_type': 'textarea'},
{'varname': 'bool', 'label': 'Bool', 'field_type': 'bool'},
],
)
agenda = Agenda.objects.create(label='Foo', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
app = login(app)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.pk, event.pk))
assert 'custom_field_text' not in resp.context['form'].fields
assert 'custom_field_textarea' not in resp.context['form'].fields
assert 'custom_field_bool' not in resp.context['form'].fields
agenda.events_type = events_type
agenda.save()
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.pk, event.pk))
assert 'custom_field_text' in resp.context['form'].fields
assert 'custom_field_textarea' in resp.context['form'].fields
assert 'custom_field_bool' in resp.context['form'].fields
resp.form.submit().follow()
event.refresh_from_db()
assert event.custom_fields == {
'text': '',
'textarea': '',
'bool': None,
}
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.pk, event.pk))
resp.form['custom_field_text'] = 'foo'
resp.form['custom_field_textarea'] = 'foo bar'
resp.form['custom_field_bool'] = 'true'
resp.form.submit().follow()
event.refresh_from_db()
assert event.custom_fields == {
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
}
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.pk, event.pk))
assert resp.form['custom_field_text'].value == 'foo'
assert resp.form['custom_field_textarea'].value == 'foo bar'
assert resp.form['custom_field_bool'].value == 'true'
resp.form['custom_field_text'] = ''
resp.form['custom_field_textarea'] = ''
resp.form['custom_field_bool'] = 'false'
resp.form.submit().follow()
event.refresh_from_db()
assert event.custom_fields == {
'text': '',
'textarea': '',
'bool': False,
}
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.pk, event.pk))
resp.form['custom_field_bool'] = 'unknown'
resp.form.submit().follow()
event.refresh_from_db()
assert event.custom_fields == {
'text': '',
'textarea': '',
'bool': None,
}
def test_edit_recurring_event(settings, app, admin_user, freezer):
freezer.move_to('2021-01-12 12:10')
events_type = EventsType.objects.create(
label='Foo', custom_fields=[{'varname': 'foo', 'label': 'Foo', 'field_type': 'text'}]
)
agenda = Agenda.objects.create(
label='Foo bar',
kind='events',
minimal_booking_delay=15,
maximal_booking_delay=30,
events_type=events_type,
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(start_datetime=now(), places=10, agenda=agenda)
app = login(app)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['frequency'] = 'recurring'
resp.form['recurrence_days'] = [localtime().isoweekday()]
resp = resp.form.submit()
assert AgendaSnapshot.objects.count() == 1
# no end date, events are created for the year to come
assert Event.objects.count() == 54
assert Event.objects.last().start_datetime.strftime('%Y-%m-%d') == '2022-01-11'
# specifying end date removes events
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['recurrence_end_date'] = '2021-06-01'
resp = resp.form.submit()
assert Event.objects.count() == 21
assert Event.objects.last().start_datetime.strftime('%Y-%m-%d') == '2021-05-25'
# detail page doesn't exist
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.id, event.id), status=404)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert 'On Tuesdays at 1:10 p.m.' in resp.text
# event is bookable regardless of minimal_booking_delay, since it has bookable recurrences
assert len(resp.pyquery.find('.bookable')) == 1
# maximal_booking_delay is accounted for, because no recurrences are bookable
freezer.move_to('2020-11-12')
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert len(resp.pyquery.find('.not-bookable')) == 1
# editing recurring event updates event recurrences
event.refresh_from_db()
event_recurrence = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['places'] = 20
resp.form['custom_field_foo'] = 'bar'
resp = resp.form.submit().follow()
event_recurrence.refresh_from_db()
assert event_recurrence.places == 20
assert event_recurrence.custom_fields == {'foo': 'bar'}
event.refresh_from_db()
assert event.custom_fields == {'foo': 'bar'}
# but some fields should not be updated
assert event_recurrence.slug != event.slug
assert not event_recurrence.recurrence_days
# changing recurrence attribute removes event recurrences
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['frequency'] = 'unique'
resp = resp.form.submit().follow()
assert not Event.objects.filter(primary_event=event).exists()
# same goes with changing slug
event.recurrence_days = [2]
event.save()
event.create_all_recurrences()
event_recurrence = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
assert Event.objects.filter(primary_event=event, slug__startswith='foo-bar-event').exists()
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['slug'] = 'hop'
resp = resp.form.submit().follow()
assert not Event.objects.filter(primary_event=event, slug__startswith='foo-bar-event').exists()
# changing recurring attribute or slug is forbidden if there are bookings for future recurrences
event_recurrence = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=7)
)
Booking.objects.create(event=event_recurrence)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
assert 'disabled' in resp.form['frequency'].attrs
assert all('disabled' in resp.form.get('recurrence_days', index=i).attrs for i in range(7))
assert 'disabled' in resp.form['recurrence_week_interval'].attrs
assert 'disabled' in resp.form['slug'].attrs
assert 'disabled' in resp.form['start_datetime_0'].attrs
assert 'disabled' in resp.form['start_datetime_1'].attrs
# changing it anyway doesn't work
resp.form['slug'] = 'changed'
resp = resp.form.submit()
assert not Event.objects.filter(slug='changed').exists()
# deletion of event recurrence is not allowed
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.id, event_recurrence.id))
assert 'Delete' not in resp.text
resp = resp.click('Options')
assert 'Delete' not in resp.text
assert {
'slug',
'frequency',
'recurrence_days',
'recurence_weekly_interval',
'recurrence_end_date',
'publication_datetime_0',
'publication_datetime_1',
'custom_field_foo',
}.isdisjoint(resp.form.fields)
resp.form.submit().follow()
# custom fields not changed
event_recurrence.refresh_from_db()
assert event_recurrence.custom_fields == {'foo': 'bar'}
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['recurrence_end_date'] = 'bad-date'
resp = resp.form.submit() # no error
def test_edit_recurring_event_with_end_date(settings, app, admin_user, freezer):
freezer.move_to('2021-01-12 12:10')
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
start_datetime=now(), places=10, recurrence_days=list(range(1, 8)), agenda=agenda
)
app = login(app)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=5)).strftime('%Y-%m-%d')
resp = resp.form.submit()
# recurrences are created automatically
event = Event.objects.get(recurrence_days__isnull=False)
assert Event.objects.filter(primary_event=event).count() == 5
assert Event.objects.filter(primary_event=event, start_datetime=now()).exists()
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['start_datetime_1'] = (localtime() + datetime.timedelta(hours=1)).strftime('%H:%M')
resp = resp.form.submit()
assert Event.objects.filter(primary_event=event).count() == 5
assert Event.objects.filter(
primary_event=event, start_datetime=now() + datetime.timedelta(hours=1)
).exists()
# old recurrences were deleted
assert not Event.objects.filter(primary_event=event, start_datetime=now()).exists()
# if start datetime of a recurrence is edited, it stays that way
recurrence = event.recurrences.first()
recurrence.start_datetime += datetime.timedelta(hours=1)
recurrence.save()
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp = resp.form.submit()
assert Event.objects.filter(primary_event=event).count() == 5
assert Event.objects.filter(primary_event=event, start_datetime=recurrence.start_datetime).count() == 1
# ensure recurrence_end_date has not been propagated
assert not Event.objects.filter(primary_event=event, recurrence_end_date__isnull=False).exists()
# editing recurrence_end_date is permitted as long as bookings are not impacted
event_recurrence = Event.objects.get(primary_event=event, start_datetime__day=15)
Booking.objects.create(event=event_recurrence)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=6)).strftime('%Y-%m-%d')
resp = resp.form.submit()
assert Event.objects.filter(primary_event=event).count() == 6
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=4)).strftime('%Y-%m-%d')
resp = resp.form.submit()
assert Event.objects.filter(primary_event=event).count() == 4
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=2)).strftime('%Y-%m-%d')
resp = resp.form.submit()
assert Event.objects.filter(primary_event=event).count() == 4
assert 'Bookings exist after this date' in resp.text
def test_edit_booked_event_disable_frequency_choice(settings, app, admin_user, freezer):
freezer.move_to('2021-01-12 12:10')
agenda = Agenda.objects.create(label='Foo bar', kind='events')
event = Event.objects.create(start_datetime=now(), places=10, agenda=agenda)
app = login(app)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
assert 'disabled' not in resp.form['frequency'].attrs
assert 'This field will not be editable once event has bookings.' in resp.text
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
assert 'disabled' in resp.form['frequency'].attrs
assert 'cannot be modified' in resp.form['frequency'].attrs['title']
assert 'This field will not be editable once event has bookings.' not in resp.text
def test_booked_places(app, admin_user):
agenda = Agenda(label='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/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day), 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='Foo bar')
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
event.save()
for _ in range(2):
Booking(event=event).save()
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert 'full' not in resp.text
assert 'overbooking' not in resp.text
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert '<span class="tag">Full</span>' not in resp
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '<span class="tag">Full</span>' not in resp
for _ in range(8):
Booking(event=event).save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
assert 'full' in resp.text
assert 'overbooking' not in resp.text
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert '<span class="tag">Full</span>' in resp
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '<span class="tag">Full</span>' in resp
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_event_detail_backoffice_url_translation(app, admin_user):
agenda = Agenda(label='Foo bar')
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
event.save()
Booking.objects.create(event=event, backoffice_url='publik://default/foo/')
app = login(app)
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert 'http://example.org/foo/' in resp.text
def test_delete_event(app, admin_user):
agenda = Agenda(label='Foo bar')
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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
assert AgendaSnapshot.objects.count() == 1
def test_delete_busy_event(app, admin_user):
agenda = Agenda(label='Foo bar')
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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_recurring_event(app, admin_user, freezer):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
start_datetime = now().replace(hour=10) + datetime.timedelta(days=10)
event = Event.objects.create(
start_datetime=start_datetime,
places=10,
agenda=agenda,
recurrence_days=[start_datetime.isoweekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=15),
)
event.create_all_recurrences()
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
event_recurrence = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
booking = Booking.objects.create(event=event_recurrence)
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
booking.cancellation_datetime = None
booking.save()
freezer.move_to(now() + datetime.timedelta(days=11))
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
def test_delete_event_as_manager(app, manager_user):
agenda = Agenda(label='Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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/time,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/time,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,https://example.net/event,2016-10-16 00:00,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/time,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,https://example.net/event,2016-10-16 00:00,90\r\n'
)
def test_export_events_wrong_kind(app, admin_user):
agenda = Agenda.objects.create(label='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='Foo bar')
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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')
assert 'end time' not in sample_csv_resp.text
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()
assert AgendaSnapshot.objects.count() == 1
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
assert 'Wrong start date/time format. (1st event)' 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
assert 'Not enough columns. (1st event)' in resp.text
resp.form['events_csv_file'] = Upload('t.csv', b'date,time,etc.\n2016-14-16,18:00,10', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Wrong start date/time format. (1st event)' 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 'Wrong start date/time format. (1st event)' in resp.text
with override_settings(LANGUAGE_CODE='fr-fr'):
resp.form['events_csv_file'] = Upload('t.csv', b'2016-14-16,18:00,10', 'text/csv')
resp = resp.form.submit(status=200)
# ensure <sup> tag is not escaped
assert '1<sup>er</sup>' 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 'Number of places must be an integer. (1st event)' 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 'Number of places in waiting list must be an integer. (1st event)' 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', '2016-09-16,18:00,10,5,éléphant'.encode(), '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 == 'é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 + '2016-09-16,18:00,10,5,éléphant'.encode(), '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 == 'é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', '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 == 'é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', '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 == '\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_datetime 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,https://example.net/event,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 == 'https://example.net/event'
assert str(event.publication_datetime) == '2016-10-15 22:00:00+00:00'
assert str(event.publication_datetime.tzinfo) == 'UTC'
assert event.duration == 90
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,https://example.net/event,2016-10-16 10:00,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 == 'https://example.net/event'
assert str(event.publication_datetime) == '2016-10-16 08:00:00+00:00'
assert str(event.publication_datetime.tzinfo) == 'UTC'
assert event.duration == 90
# publication date/time 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,https://example.net/event,foobar',
'text/csv',
)
resp = resp.form.submit(status=200)
assert 'Wrong publication date/time format. (1st event)' 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,https://example.net/event,2016-09-16,foobar',
'text/csv',
)
resp = resp.form.submit(status=200)
assert 'Duration must be an integer. (1st event)' 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) == 31
assert Event.objects.count() == 5
assert set(Event.objects.values_list('slug', flat=True)) == {
'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 'Identifier: This value cannot be a number. (1st event)' in resp.text
def test_import_event_nested_quotes(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload(
't.csv',
','.join(
[
'2016-09-16',
'18:00',
'10',
'5',
'éléphant',
'elephant',
# the multiline description and final dot
# and new line after ""éléphants"" are needed to trigger the bug.
'''"Animation:
De nombreux ""éléphants"".
"''',
]
).encode(),
'text/csv',
)
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
assert 'De nombreux "éléphants"' in Event.objects.all()[0].description
def test_import_events_existing_event(app, admin_user, freezer):
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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='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_import_events_partial_bookings(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='events', partial_bookings=True)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
resp = app.get('/manage/agendas/%s/import-events' % agenda.pk)
sample_csv_resp = resp.click('Download sample file')
assert 'end time' in sample_csv_resp.text
assert 'duration' not in sample_csv_resp.text
resp.form['events_csv_file'] = Upload('t.csv', sample_csv_resp.content, 'text/csv')
resp = resp.form.submit(status=302)
event = Event.objects.get()
assert event.end_time == datetime.time(14, 00)
# no end time
resp = app.get('/manage/agendas/%s/import-events' % agenda.pk)
resp.form['events_csv_file'] = Upload(
't.csv',
b'2016-09-16,18:00,10,5,label,slug,description,pricing,https://example.net/event,2016-09-16',
'text/csv',
)
resp = resp.form.submit(status=200)
assert 'Missing end_time.' in resp.text
# invalid end time
resp = app.get('/manage/agendas/%s/import-events' % agenda.pk)
resp.form['events_csv_file'] = Upload(
't.csv',
b'2016-09-16,18:00,10,5,label,slug,description,pricing,https://example.net/event,2016-09-16,xxx',
'text/csv',
)
resp = resp.form.submit(status=200)
assert '“xxx” value has an invalid format' in resp.text
def test_import_events_multiple_errors(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
resp = app.get('/manage/agendas/%s/import-events' % agenda.pk)
resp.form['events_csv_file'] = Upload(
't.csv',
b'2016-09-17,18:00,10,5,label,slug\n' # valid event
b'2016-09-17,19:00,xxx,5,label2,slug2\n' # invalid places
b'2016-09-17,20:00,10,5,,1234\n', # invalid slug
'text/csv',
)
resp = resp.form.submit(status=200)
assert [x.text for x in resp.pyquery('.errorlist li')] == [
'Invalid file format:',
'Number of places must be an integer. (2nd event)',
'Identifier: This value cannot be a number. (3rd event)',
]
@pytest.mark.freeze_time('2022-05-24')
def test_event_detail(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,
waiting_list_places=2,
agenda=agenda,
)
Booking.objects.create(event=event, user_last_name="User's 1")
Booking.objects.create(event=event, user_last_name='User 2', in_waiting_list=True)
login(app)
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Bookings (1/10)' in resp.text
assert 'User&#x27;s 1, May 24, 2022, 2 a.m.' in resp.text
assert 'Waiting List (1/2): 1 remaining place' in resp.text
assert 'User 2, May 24, 2022, 2 a.m.' in resp.text
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Bookings (1/10)' in resp.text
assert '&lt;b&gt;User&#x27;s 1&lt;/b&gt; Foo Bar, May 24, 2022, 2 a.m.' in resp.text
assert 'Waiting List (1/2): 1 remaining place' in resp.text
assert '&lt;b&gt;User 2&lt;/b&gt; Foo Bar, May 24, 2022, 2 a.m.' in resp.text
def test_event_detail_redirect(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,
waiting_list_places=2,
agenda=agenda,
)
day = localtime(event.start_datetime)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.slug, event.slug), status=302)
assert resp.location.endswith('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
agenda.partial_bookings = True
agenda.save()
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.slug, event.slug), status=302)
assert resp.location.endswith(
'/manage/agendas/%s/day/%d/%02d/%02d/' % (agenda.pk, day.year, day.month, day.day)
)
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, user_last_name='User 1')
Booking.objects.create(event=event, user_last_name='User 2')
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 = localtime(event.start_datetime)
def mocked_requests_connection_error(*args, **kwargs):
raise requests.exceptions.ConnectionError('unreachable')
for _ in range(5):
Booking.objects.create(event=event, cancel_callback_url='http://example.org/jump/trigger/')
login(app)
resp = app.get('/manage/agendas/%s/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
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/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
assert 'Errors occured during cancellation of event "xyz".' in resp.text
# warning doesn't go away
resp = app.get('/manage/agendas/%s/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
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/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
assert 'Errors occured during cancellation of event "xyz".' not 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_error_report_backofice_url_translation(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 _ in range(5):
Booking.objects.create(
event=event,
cancel_callback_url='http://example.org/jump/trigger/',
backoffice_url='publik://default/',
)
login(app)
resp = app.get('/manage/agendas/%s/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
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/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
assert '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))
assert 'http://example.org/' in resp.text
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 = 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
booking.cancel()
resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
assert 'event has bookings with no callback url configured' not in resp.text
assert 'Proceed with cancellation' 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')
day = localtime(now()) + datetime.timedelta(days=1)
event = Event.objects.create(label='xyz', start_datetime=day, places=10, agenda=agenda)
login(app)
assert event.get_booking_form_url() is None
resp = app.get('/manage/agendas/%s/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
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 (
event.get_booking_form_url()
== 'http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s'
% (agenda.slug, event.slug)
)
resp = app.get('/manage/agendas/%s/month/%d/%d/%d/' % (agenda.id, day.year, day.month, day.day))
assert (
'<a class="link-action-text" href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s&ReturnURL=%s">Booking form</a>'
% (
agenda.slug,
event.slug,
'http://testserver/manage/agendas/%d/month/%d/%d/%d/' % (agenda.pk, day.year, day.month, day.day),
)
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&ReturnURL=%s">Booking form</a>'
% (agenda.slug, event.slug, 'http://testserver/manage/agendas/%d/events/open/' % agenda.pk)
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&ReturnURL=%s">Booking form</a>'
% (agenda.slug, event.slug, 'http://testserver/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
in resp.text
)
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
# again
app.get('/manage/agendas/%s/bookings/%s/cancel' % (agenda.pk, booking.pk), status=404)
# test secondary booking
primary = Booking.objects.create(event=event)
secondary = Booking.objects.create(event=event, primary_booking=primary)
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert 'Bookings (2/10)' in resp.text
assert '/manage/agendas/%s/bookings/%s/cancel' % (agenda.pk, primary.pk) in resp.text
assert '/manage/agendas/%s/bookings/%s/cancel' % (agenda.pk, secondary.pk) not in resp.text
app.get('/manage/agendas/%s/bookings/%s/cancel' % (agenda.pk, secondary.pk), status=404)
app.get('/manage/agendas/%s/bookings/%s/cancel' % (agenda.pk, primary.pk)).form.submit()
primary.refresh_from_db()
secondary.refresh_from_db()
assert primary.cancellation_datetime
assert secondary.cancellation_datetime
def test_event_check(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
agenda2 = Agenda.objects.create(label='Events', kind='events')
Desk.objects.create(agenda=agenda2, slug='_exceptions_holder')
event = Event.objects.create(
label='xyz',
start_datetime=localtime(now()) + datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
booking1 = Booking.objects.create(
event=event, user_external_id='user:1', user_first_name='User', user_last_name='42'
)
Booking.objects.create(
event=event, user_external_id='user:2', user_first_name="User's", user_last_name='01'
)
Booking.objects.create(
event=event, user_external_id='user:3', user_first_name='User', user_last_name='17'
)
Booking.objects.create(
event=event, user_external_id='user:4', user_first_name='User', user_last_name='35'
)
Booking.objects.create(
event=event, user_external_id='user:5', user_first_name='User', user_last_name='05'
)
booking6 = Booking.objects.create(
event=event, user_external_id='user:6', user_first_name='User', user_last_name='12 Cancelled'
)
booking6.cancel()
booking7 = Booking.objects.create(
event=event,
user_external_id='user:7',
user_first_name='User',
user_last_name='Waiting',
in_waiting_list=True,
)
booking8 = Booking.objects.create(
event=event,
user_external_id='user:8',
user_first_name='User',
user_last_name='Waiting and Cancelled',
in_waiting_list=True,
)
booking8.cancel()
booking9 = Booking.objects.create(
event=event,
user_external_id='user:1',
user_first_name='User',
user_last_name='Secondary',
primary_booking=booking1,
)
login(app)
# event not in past
assert agenda.enable_check_for_future_events is False
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk) not in resp
app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), status=404)
# not in past, but check for future events is enabled
agenda.enable_check_for_future_events = True
agenda.save()
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk) in resp
app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), status=200)
# event is today
agenda.enable_check_for_future_events = False
agenda.save()
event.start_datetime = localtime(now()).replace(hour=22, minute=0) # it's ok all the day
event.save()
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk) in resp
app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), status=200)
# unknown agenda
app.get('/manage/agendas/%s/events/%s/check' % (0, event.pk), status=404)
# wrong agenda
app.get('/manage/agendas/%s/events/%s/check' % (agenda2.pk, event.pk), status=404)
resp = resp.click('Check')
assert (
resp.text.index('Bookings (6/10)')
< resp.text.index('User&#x27;s 01')
< resp.text.index('User 05')
< resp.text.index('User 17')
< resp.text.index('User 35')
< resp.text.index('User 42')
< resp.text.index('Waiting List (1/5)')
< resp.text.index('User Waiting')
)
assert 'User 12 Cancelled' not in resp
assert 'User Waiting and Cancelled' not in resp
Subscription.objects.create(
agenda=agenda,
user_external_id='user:1',
user_first_name='Subscription',
user_last_name='42',
date_start=datetime.date.today(),
date_end=datetime.date.today() + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='user:9',
user_first_name='Subscription',
user_last_name='43',
date_start=datetime.date.today(),
date_end=datetime.date.today() + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='user:10',
user_first_name='Subscription',
user_last_name='14',
date_start=datetime.date.today(),
date_end=datetime.date.today() + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='user:7',
user_first_name='Subscription',
user_last_name='Waiting',
date_start=datetime.date.today(),
date_end=datetime.date.today() + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='user:42',
user_first_name='Subscription',
user_last_name='Too soon',
date_start=datetime.date.today() - datetime.timedelta(days=1),
date_end=datetime.date.today(),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='user:42',
user_first_name='Subscription',
user_last_name='Too late',
date_start=datetime.date.today() + datetime.timedelta(days=1),
date_end=datetime.date.today() + datetime.timedelta(days=2),
)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert (
resp.text.index('Bookings (6/10)')
< resp.text.index('User&#x27;s 01')
< resp.text.index('User 05')
< resp.text.index('User 12 Cancelled')
< resp.text.index('Subscription 14')
< resp.text.index('(Not booked)')
< resp.text.index('User 17')
< resp.text.index('User 35')
< resp.text.index('User 42')
< resp.text.index('Subscription 43')
< resp.text.index('Waiting List (1/5)')
< resp.text.index('User Waiting')
< resp.text.index('User Waiting and Cancelled')
)
assert 'Subscription Waiting' not in resp
assert 'Subscription 42' not in resp
assert 'Subscription too soon' not in resp
assert 'Subscription too late' not in resp
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '&lt;b&gt;User&#x27;s 01&lt;/b&gt; Foo Bar' in resp
assert '&lt;b&gt;Subscription 14&lt;/b&gt; Foo Bar' in resp
assert '&lt;b&gt;User Waiting&lt;/b&gt; Foo Bar' in resp
# booking in waiting list
token = resp.context['csrf_token']
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking7.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking7.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# secondary booking
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking9.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking9.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# unknown agenda
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (0, booking1.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (0, booking1.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# wrong agenda
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda2.pk, booking1.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda2.pk, booking1.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# cancelled event
event.cancel()
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk) not in resp
app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), status=404)
# partial bookings
event.cancellation_datetime = None
event.save()
agenda.partial_bookings = True
agenda.save()
app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk), status=404)
app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), status=404)
def test_event_checked(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events', booking_check_filters='foo,bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='xyz',
start_datetime=localtime(now()) - datetime.timedelta(days=1),
places=10,
agenda=agenda,
)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Mark the event as checked' not in resp
for i in range(8):
booking = Booking.objects.create(event=event)
if i < 3:
booking.mark_user_presence()
elif i < 7:
booking.mark_user_absence()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Mark the event as checked' in resp
assert event.checked is False
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'checked tag' not in resp
urls = [
'/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk),
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
'/manage/agendas/%s/month/%d/%d/%d/'
% (agenda.pk, event.start_datetime.year, event.start_datetime.month, event.start_datetime.day),
'/manage/agendas/%s/week/%d/%d/%d/'
% (agenda.pk, event.start_datetime.year, event.start_datetime.month, event.start_datetime.day),
'/manage/agendas/%s/day/%d/%d/%d/'
% (agenda.pk, event.start_datetime.year, event.start_datetime.month, event.start_datetime.day),
]
for url in urls:
resp = app.get(url)
assert '<span class="checked tag">Checked</span>' not in resp
assert 'check-locked' not in resp
assert 'invoiced' not in resp
assert '<span class="meta meta-success">Presents 3</span>' in resp
assert '<span class="meta meta-error">Absents 4</span>' in resp
assert '<span class="meta meta-disabled">Not checked 1</span>' in resp
token = resp.context['csrf_token']
resp = app.post(
'/manage/agendas/%s/events/%s/checked' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
)
assert resp.location.endswith('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
event.refresh_from_db()
assert event.checked is True
resp = resp.follow()
assert 'Mark the event as checked' not in resp
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'checked tag' in resp
for booking in Booking.objects.filter(user_checks__isnull=True):
booking.mark_user_presence()
for url in urls:
resp = app.get(url)
assert '<span class="checked tag">Checked</span>' in resp
assert 'check-locked' not in resp
assert 'invoiced' not in resp
assert '<span class="meta meta-success">Presents 4</span>' in resp
assert '<span class="meta meta-error">Absents 4</span>' in resp
assert 'meta meta-disabled' not in resp
# event not in past
agenda.disable_check_update = False
agenda.save()
assert agenda.enable_check_for_future_events is False
event.start_datetime = localtime(now()) + datetime.timedelta(days=1)
event.save()
app.post(
'/manage/agendas/%s/events/%s/checked' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# not in past, but check for future events is enabled
agenda.enable_check_for_future_events = True
agenda.save()
app.post(
'/manage/agendas/%s/events/%s/checked' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
# event check is locked
event.checked = False
event.check_locked = True
event.save()
urls = [
'/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk),
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
'/manage/agendas/%s/month/%d/%d/%d/'
% (agenda.pk, event.start_datetime.year, event.start_datetime.month, event.start_datetime.day),
'/manage/agendas/%s/week/%d/%d/%d/'
% (agenda.pk, event.start_datetime.year, event.start_datetime.month, event.start_datetime.day),
'/manage/agendas/%s/day/%d/%d/%d/'
% (agenda.pk, event.start_datetime.year, event.start_datetime.month, event.start_datetime.day),
]
for url in urls:
resp = app.get(url)
assert '<span class="check-locked tag">Check locked</span>' in resp
assert 'invoiced' not in resp
app.post(
'/manage/agendas/%s/events/%s/checked' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# event check is locked and envent is invoiced
event.invoiced = True
event.save()
for url in urls:
resp = app.get(url)
assert 'check-locked' not in resp
assert '<span class="invoiced tag">Invoiced</span>' in resp
def test_event_notify_checked(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events', booking_check_filters='foo,bar')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
agenda=agenda,
)
login(app)
for i in range(8):
booking = Booking.objects.create(
event=event,
presence_callback_url='https://example.invalid/presence/%s' % i,
absence_callback_url='https://example.invalid/absence/%s' % i,
)
if i < 3:
booking.mark_user_presence()
elif i < 7:
booking.mark_user_absence()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
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 = app.post(
'/manage/agendas/%s/events/%s/checked' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': resp.context['csrf_token']},
)
assert {x[0][0].url for x in mock_send.call_args_list} == {
'https://example.invalid/presence/0',
'https://example.invalid/presence/1',
'https://example.invalid/presence/2',
'https://example.invalid/absence/3',
'https://example.invalid/absence/4',
'https://example.invalid/absence/5',
'https://example.invalid/absence/6',
}
assert set(json.loads(mock_send.call_args_list[0][0][0].body).keys()) == {
'user_check_type_label',
'user_check_type_slug',
'user_was_present',
}
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_filters(check_types, app, admin_user):
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
]
agenda = Agenda.objects.create(
label='Events',
kind='events',
booking_check_filters='foo,bar',
)
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
agenda=agenda,
)
Booking.objects.create(
event=event, user_external_id='user:none', user_first_name='User', user_last_name='none'
)
Booking.objects.create(
event=event,
user_external_id='user:empty',
user_first_name='User',
user_last_name='empty',
extra_data={},
)
booking = Booking.objects.create(
event=event,
user_external_id='user:1',
user_first_name='User',
user_last_name='foo-val1 bar-none presence',
extra_data={'foo': 'val1', 'bar': ['val1']}, # bar is ignored, wrong value
)
booking.mark_user_presence()
booking = Booking.objects.create(
event=event,
user_external_id='user:2',
user_first_name='User',
user_last_name='foo-val2 bar-val1 absence',
extra_data={'foo': 'val2', 'bar': 'val1'},
)
booking.mark_user_absence()
Booking.objects.create(
event=event,
user_external_id='user:3',
user_first_name='User',
user_last_name='foo-val1 bar-val2 not-checked',
extra_data={'foo': 'val1', 'bar': 'val2'},
)
booking = Booking.objects.create(
event=event,
user_external_id='user:4',
user_first_name='User',
user_last_name='foo-none bar-val2 reason-foo',
extra_data={'bar': 'val2', 'foo': None}, # foo is ignored, empty value
)
booking.mark_user_absence(check_type_slug='foo-reason')
booking = Booking.objects.create(
event=event,
user_external_id='user:5',
user_first_name='User',
user_last_name='foo-none bar-val2 reason-bar',
extra_data={'bar': 'val2', 'foo': ''}, # foo is ignored, empty value
)
booking.mark_user_presence(check_type_slug='bar-reason')
booking = Booking.objects.create(
event=event,
user_external_id='user:6',
user_first_name='User',
user_last_name='foo-none bar-val2 cancelled-absence',
extra_data={'bar': 'val2'},
)
booking.mark_user_absence(check_type_slug='foo-reason')
booking.cancellation_datetime = now()
booking.save()
booking = Booking.objects.create(
event=event,
user_external_id='user:7',
user_first_name='User',
user_last_name='foo-none bar-val2 cancelled-presence',
extra_data={'bar': 'val2'},
)
booking.mark_user_presence(check_type_slug='bar-reason')
booking.cancellation_datetime = now()
booking.save()
Subscription.objects.create(
agenda=agenda,
user_external_id='subscription:none',
user_first_name='Subscription',
user_last_name='none',
date_start=event.start_datetime,
date_end=event.start_datetime + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='subscription:empty',
user_first_name='Subscription',
user_last_name='empty',
extra_data={},
date_start=event.start_datetime,
date_end=event.start_datetime + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='subscription:1',
user_first_name='Subscription',
user_last_name='foo-val1 bar-none',
extra_data={'foo': 'val1', 'bar': ['val1']}, # bar is ignored, wrong value
date_start=event.start_datetime,
date_end=event.start_datetime + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='subscription:2',
user_first_name='Subscription',
user_last_name='foo-val2 bar-val1',
extra_data={'foo': 'val2', 'bar': 'val1'},
date_start=event.start_datetime,
date_end=event.start_datetime + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='subscription:3',
user_first_name='Subscription',
user_last_name='foo-val1 bar-val2',
extra_data={'foo': 'val1', 'bar': 'val2'},
date_start=event.start_datetime,
date_end=event.start_datetime + datetime.timedelta(days=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='subscription:4',
user_first_name='Subscription',
user_last_name='foo-none bar-val2',
extra_data={'bar': 'val2'},
date_start=event.start_datetime,
date_end=event.start_datetime + datetime.timedelta(days=1),
)
login(app)
for params in [
{},
{'unknown': 'unknown'},
{'extra-data-unknown': 'unknown'},
{'extra-data-foo': 'unknown'},
]:
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params=params)
assert 'User none' in resp
assert 'User empty' in resp
assert 'User foo-val1 bar-none presence' in resp
assert 'User foo-val2 bar-val1 absence' in resp
assert 'User foo-val1 bar-val2 not-checked' in resp
assert 'User foo-none bar-val2 reason-foo' in resp
assert 'User foo-none bar-val2 reason-bar' in resp
assert 'User foo-none bar-val2 cancelled-absence' in resp
assert 'User foo-none bar-val2 cancelled-presence' in resp
assert 'Subscription none' in resp
assert 'Subscription empty' in resp
assert 'Subscription foo-val1 bar-none' in resp
assert 'Subscription foo-val2 bar-val1' in resp
assert 'Subscription foo-val1 bar-val2' in resp
assert 'Subscription foo-none bar-val2' in resp
with CaptureQueriesContext(connection) as ctx:
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'extra-data-foo': 'val1'}
)
assert len(ctx.captured_queries) == 11
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
params={'extra-data-foo': 'val1', 'extra-data-bar': 'val2'},
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
params={'extra-data-foo': 'val2', 'extra-data-bar': 'val2'},
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' not in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'booking-status': 'booked'}
)
assert 'User none' in resp
assert 'User empty' in resp
assert 'User foo-val1 bar-none presence' in resp
assert 'User foo-val2 bar-val1 absence' in resp
assert 'User foo-val1 bar-val2 not-checked' in resp
assert 'User foo-none bar-val2 reason-foo' in resp
assert 'User foo-none bar-val2 reason-bar' in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'booking-status': 'not-booked'}
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' not in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' in resp
assert 'Subscription empty' in resp
assert 'Subscription foo-val1 bar-none' in resp
assert 'Subscription foo-val2 bar-val1' in resp
assert 'Subscription foo-val1 bar-val2' in resp
assert 'Subscription foo-none bar-val2' in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'booking-status': 'cancelled'}
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' not in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' in resp
assert 'User foo-none bar-val2 cancelled-presence' in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'booking-status': 'not-checked'}
)
assert 'User none' in resp
assert 'User empty' in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'booking-status': 'presence'}
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' not in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'booking-status': 'absence'}
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' in resp
assert 'User foo-val1 bar-val2 not-checked' not in resp
assert 'User foo-none bar-val2 reason-foo' in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
params={'booking-status': 'absence::foo-reason'},
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' not in resp
assert 'User foo-none bar-val2 reason-foo' in resp
assert 'User foo-none bar-val2 reason-bar' not in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
resp = app.get(
'/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
params={'booking-status': 'presence::bar-reason'},
)
assert 'User none' not in resp
assert 'User empty' not in resp
assert 'User foo-val1 bar-none presence' not in resp
assert 'User foo-val2 bar-val1 absence' not in resp
assert 'User foo-val1 bar-val2 not-checked' not in resp
assert 'User foo-none bar-val2 reason-foo' not in resp
assert 'User foo-none bar-val2 reason-bar' in resp
assert 'User foo-none bar-val2 cancelled-absence' not in resp
assert 'User foo-none bar-val2 cancelled-presence' not in resp
assert 'Subscription none' not in resp
assert 'Subscription empty' not in resp
assert 'Subscription foo-val1 bar-none' not in resp
assert 'Subscription foo-val2 bar-val1' not in resp
assert 'Subscription foo-val1 bar-val2' not in resp
assert 'Subscription foo-none bar-val2' not in resp
def test_event_check_ordering(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), places=10, agenda=agenda
)
Booking.objects.create(
event=event,
user_first_name='BB',
user_last_name='XX',
user_external_id='user:1',
)
Booking.objects.create(
event=event,
user_first_name='AA',
user_last_name='YY',
user_external_id='user:2',
cancellation_datetime=now(),
)
Subscription.objects.create(
agenda=agenda,
user_first_name='CC',
user_last_name='WW',
user_external_id='user:3',
date_start=datetime.date(2022, 2, 1),
date_end=datetime.date(2022, 3, 1),
)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert resp.text.index('CC WW') < resp.text.index('BB XX') < resp.text.index('AA YY')
resp = app.get('/manage/agendas/%s/events/%s/check?sort=lastname,firstname' % (agenda.pk, event.pk))
assert resp.text.index('CC WW') < resp.text.index('BB XX') < resp.text.index('AA YY')
resp = app.get('/manage/agendas/%s/events/%s/check?sort=firstname,lastname' % (agenda.pk, event.pk))
assert resp.text.index('AA YY') < resp.text.index('BB XX') < resp.text.index('CC WW')
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_booking(check_types, app, admin_user):
check_types.return_value = []
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
booking = Booking.objects.create(event=event, user_first_name='User', user_last_name='42')
secondary_booking = Booking.objects.create(
event=event, user_first_name='User', user_last_name='42', primary_booking=booking
)
assert agenda.mark_event_checked_auto is False
def _test_reset():
resp = app.post(
'/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
).follow()
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) not in resp
booking.refresh_from_db()
assert not booking.user_check
secondary_booking.refresh_from_db()
assert not secondary_booking.user_check
event.refresh_from_db()
assert event.checked is False
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert resp.pyquery.find('td.booking-status')[0].text.strip() == '-'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 0
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) not in resp
# set as present
token = resp.context['csrf_token']
resp = app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
).follow()
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Present'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Presence'
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) in resp
booking.refresh_from_db()
assert booking.user_check.presence is True
assert booking.user_check.type_slug is None
assert booking.user_check.type_label is None
secondary_booking.refresh_from_db()
assert not secondary_booking.user_check
event.refresh_from_db()
assert event.checked is False
# reset
_test_reset()
agenda.mark_event_checked_auto = True
agenda.save()
# set as absent without check_type
resp = app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
).follow()
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Absent'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Absence'
booking.refresh_from_db()
assert booking.user_check.presence is False
assert booking.user_check.type_slug is None
assert booking.user_check.type_label is None
secondary_booking.refresh_from_db()
assert not secondary_booking.user_check
event.refresh_from_db()
assert event.checked is True
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
]
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert len(resp.pyquery.find('td.booking-actions form.absence select')) == 1
assert len(resp.pyquery.find('td.booking-actions form.presence select')) == 0
# reset
_test_reset()
# set as absent with check_type
resp = app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
).follow()
assert 'Foo reason' in resp
booking.refresh_from_db()
assert booking.user_check.presence is False
assert booking.user_check.type_slug == 'foo-reason'
assert booking.user_check.type_label == 'Foo reason'
secondary_booking.refresh_from_db()
assert not secondary_booking.user_check
event.refresh_from_db()
assert event.checked is True
# reset
_test_reset()
# set as present without check_type
resp = app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
).follow()
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Present'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Presence'
booking.refresh_from_db()
assert booking.user_check.presence is True
assert booking.user_check.type_slug is None
assert booking.user_check.type_label is None
secondary_booking.refresh_from_db()
assert not secondary_booking.user_check
event.refresh_from_db()
assert event.checked is True
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert len(resp.pyquery.find('td.booking-actions form.absence select')) == 1
assert len(resp.pyquery.find('td.booking-actions form.presence select')) == 0
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
CheckType(slug='bar-reason', label='Bar reason', kind='presence', unexpected_presence=True),
]
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert len(resp.pyquery.find('td.booking-actions form.absence select')) == 1
assert len(resp.pyquery.find('td.booking-actions form.presence select')) == 1
assert resp.pyquery.find('td.booking-actions form.presence option:selected').text() == '---------'
# reset
_test_reset()
# set as present with check_type
resp = app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
).follow()
assert 'Bar reason' in resp
booking.refresh_from_db()
assert booking.user_check.presence is True
assert booking.user_check.type_slug == 'bar-reason'
assert booking.user_check.type_label == 'Bar reason'
secondary_booking.refresh_from_db()
assert not secondary_booking.user_check
event.refresh_from_db()
assert event.checked is True
# event is checked
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) in resp
app.post(
'/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
# event not in past
assert agenda.enable_check_for_future_events is False
event.start_datetime = now() + datetime.timedelta(days=1)
event.save()
app.post(
'/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# not in past, but check for future events is enabled
agenda.enable_check_for_future_events = True
agenda.save()
app.post(
'/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
# event check is locked
event.check_locked = True
event.save()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) not in resp
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# now disable check update
event.check_locked = False
event.save()
agenda.disable_check_update = True
agenda.save()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) not in resp
app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_cancelled_booking(check_types, app, admin_user):
check_types.return_value = []
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
booking = Booking.objects.create(event=event, user_first_name='User', user_last_name='42')
secondary_booking = Booking.objects.create(
event=event, user_first_name='User', user_last_name='42', primary_booking=booking
)
booking.cancel()
Booking.objects.create(event=event, user_first_name='User', user_last_name='43')
# no suscription: cancelled bookings are not displayed
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert len(resp.pyquery.find('td.booking-status')) == 1
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) not in resp
token = resp.context['csrf_token']
resp = app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='user:1',
user_first_name='Subscription',
user_last_name='42',
date_start=now(),
date_end=now() + datetime.timedelta(days=1),
)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert len(resp.pyquery.find('td.booking-status')) == 2
assert resp.pyquery.find('td.booking-status')[0].text.strip() == '(Cancelled)'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 0
assert '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk) not in resp
resp = app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
).follow()
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Present'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Presence'
booking.refresh_from_db()
assert booking.cancellation_datetime is None
assert booking.user_check.presence is True
secondary_booking.refresh_from_db()
assert secondary_booking.cancellation_datetime is None
assert not secondary_booking.user_check
booking.cancel()
resp = app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
).follow()
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Absent'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Absence'
booking.refresh_from_db()
assert booking.cancellation_datetime is None
assert booking.user_check.presence is False
secondary_booking.refresh_from_db()
assert secondary_booking.cancellation_datetime is None
assert not secondary_booking.user_check
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_booking_ajax(check_types, app, admin_user):
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
]
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
booking = Booking.objects.create(event=event, user_first_name='User', user_last_name='42')
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
token = resp.context['csrf_token']
# set as present
resp = app.post(
'/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
extra_environ={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'},
)
assert '<tr>' not in resp # because this is a fragment
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Present\n \n (Bar reason)'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Presence'
assert '<option value="bar-reason" selected>Bar reason</option>' in resp
# set as absent
resp = app.post(
'/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
extra_environ={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'},
)
assert '<tr>' not in resp # because this is a fragment
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Absent\n \n (Foo reason)'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text.startswith('Absence')
assert '<option value="foo-reason" selected>Foo reason</option>' in resp
# reset
resp = app.post(
'/manage/agendas/%s/bookings/%s/reset' % (agenda.pk, booking.pk),
params={'csrfmiddlewaretoken': token},
extra_environ={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'},
)
assert '<tr>' not in resp # because this is a fragment
assert resp.pyquery.find('td.booking-status')[0].text.strip() == '-'
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_subscription(check_types, app, admin_user):
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
]
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
subscription = Subscription.objects.create(
agenda=agenda,
user_external_id='user:1',
user_first_name='Subscription',
user_last_name='42',
user_email='foo@bar.com',
user_phone_number='06',
extra_data={'foo': 'bar'},
date_start=now() - datetime.timedelta(days=1),
date_end=now(),
)
# existing booking: no check for subscription
booking = Booking.objects.create(
event=event, user_first_name='User', user_last_name='42', user_external_id='user:1'
)
booking2 = Booking.objects.create(
event=event, user_first_name='User', user_last_name='42', user_external_id='user:1'
)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert (
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk) not in resp
)
assert (
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk) not in resp
)
token = resp.context['csrf_token']
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
booking2.delete()
booking.cancel()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert (
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk) not in resp
)
assert (
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk) not in resp
)
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
booking.delete()
# set as present
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk) in resp
assert '/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk) in resp
assert resp.pyquery.find('td.booking-actions form.presence option:selected').text() == '---------'
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
CheckType(slug='baz-reason', label='Baz reason', kind='presence', unexpected_presence=True),
]
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert resp.pyquery.find('td.booking-actions form.presence option:selected').text() == 'Baz reason'
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
)
booking = Booking.objects.latest('pk')
assert booking.user_check.presence is True
assert booking.user_check.type_slug == 'bar-reason'
assert booking.user_check.type_label == 'Bar reason'
assert booking.event == event
assert booking.user_external_id == subscription.user_external_id
assert booking.user_first_name == subscription.user_first_name
assert booking.user_last_name == subscription.user_last_name
assert booking.user_email == subscription.user_email
assert booking.user_phone_number == subscription.user_phone_number
assert booking.extra_data == subscription.extra_data
booking.delete()
# set as absent
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk) in resp
assert '/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk) in resp
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
)
booking = Booking.objects.latest('pk')
assert booking.user_check.presence is False
assert booking.user_check.type_slug == 'foo-reason'
assert booking.user_check.type_label == 'Foo reason'
assert booking.event == event
assert booking.user_external_id == subscription.user_external_id
assert booking.user_first_name == subscription.user_first_name
assert booking.user_last_name == subscription.user_last_name
assert booking.user_email == subscription.user_email
assert booking.user_phone_number == subscription.user_phone_number
assert booking.extra_data == subscription.extra_data
booking.delete()
# mark the event as checked
assert agenda.disable_check_update is False
event.checked = True
event.save()
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
Booking.objects.all().delete()
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
Booking.objects.all().delete()
agenda.disable_check_update = True
agenda.save()
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# check subscription dates
agenda.disable_check_update = False
agenda.save()
subscription.date_start = now() - datetime.timedelta(days=2)
subscription.date_end = now() - datetime.timedelta(days=1)
subscription.save()
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
subscription.date_start = now()
subscription.date_end = now() + datetime.timedelta(days=1)
subscription.save()
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# event not in past
subscription.date_end = datetime.date.today() + datetime.timedelta(days=2)
subscription.save()
assert agenda.enable_check_for_future_events is False
event.start_datetime = localtime(now()) + datetime.timedelta(days=1)
event.save()
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# not in past, but check for future events is enabled
agenda.enable_check_for_future_events = True
agenda.save()
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
Booking.objects.all().delete()
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
Booking.objects.all().delete()
# event check is locked
event.check_locked = True
event.save()
app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_extra_user_block(check_types, app, admin_user):
check_types.return_value = []
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
subscription = Subscription.objects.create(
agenda=agenda,
user_external_id='user:1',
user_first_name='Subscription',
user_last_name='41',
date_start=now() - datetime.timedelta(days=1),
date_end=now(),
)
booking = Booking.objects.create(
event=event, user_first_name='User', user_last_name='42', user_external_id='user:2'
)
cancelled_booking = Booking.objects.create(
event=event, user_first_name='User', user_last_name='43', user_external_id='user:3'
)
cancelled_booking.cancel()
waiting_booking = Booking.objects.create(
event=event,
user_first_name='User',
user_last_name='44',
user_external_id='user:4',
in_waiting_list=True,
)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/subscriptions/%s/extra-user-block' % (agenda.pk, subscription.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, cancelled_booking.pk) not in resp
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, waiting_booking.pk) not in resp
agenda.booking_extra_user_block_template = '{{ booking.user_name }} Foo Bar'
agenda.save()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert '/manage/agendas/%s/subscriptions/%s/extra-user-block' % (agenda.pk, subscription.pk) in resp
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, cancelled_booking.pk) in resp
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, waiting_booking.pk) in resp
Subscription.objects.create(
agenda=agenda,
user_external_id='user:2',
user_first_name='Subscription',
user_last_name='42',
date_start=now() - datetime.timedelta(days=1),
date_end=now(),
)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
# booking url, not subscription url
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk) in resp
resp = app.get('/manage/agendas/%s/subscriptions/%s/extra-user-block' % (agenda.pk, subscription.pk))
assert resp.text == 'Subscription 41 Foo Bar'
resp = app.get('/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk))
assert resp.text == 'User 42 Foo Bar'
resp = app.get('/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, cancelled_booking.pk))
assert resp.text == 'User 43 Foo Bar'
resp = app.get('/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, waiting_booking.pk))
assert resp.text == 'User 44 Foo Bar'
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_subscription_ajax(check_types, app, admin_user):
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
]
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
subscription = Subscription.objects.create(
agenda=agenda,
user_external_id='user:1',
user_first_name='Subscription',
user_last_name='42',
date_start=now() - datetime.timedelta(days=1),
date_end=now(),
)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
token = resp.context['csrf_token']
# set as present
resp = app.post(
'/manage/agendas/%s/subscriptions/%s/presence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
extra_environ={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'},
)
assert '<tr>' not in resp # because this is a fragment
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Present\n \n (Bar reason)'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Presence'
assert '<option value="bar-reason" selected>Bar reason</option>' in resp
Booking.objects.all().delete()
# set as absent
resp = app.post(
'/manage/agendas/%s/subscriptions/%s/absence/%s' % (agenda.pk, subscription.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
extra_environ={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'},
)
assert '<tr>' not in resp # because this is a fragment
assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Absent\n \n (Foo reason)'
assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text.startswith('Absence')
assert '<option value="foo-reason" selected>Foo reason</option>' in resp
@mock.patch('chrono.manager.forms.get_agenda_check_types')
def test_event_check_all_bookings(check_types, app, admin_user):
check_types.return_value = [
CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
]
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
waiting_list_places=5,
agenda=agenda,
)
booking1 = Booking.objects.create(event=event, user_first_name='User', user_last_name='42')
assert agenda.mark_event_checked_auto is False
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
token = resp.context['csrf_token']
assert 'Mark all bookings without status' in resp
assert '/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk) in resp
assert '/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk) in resp
resp = app.post(
'/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk), params={'csrfmiddlewaretoken': token}
)
booking1.refresh_from_db()
assert booking1.user_check.presence is False
assert booking1.user_check.type_slug is None
assert booking1.user_check.type_label is None
event.refresh_from_db()
assert event.checked is False
agenda.mark_event_checked_auto = True
agenda.save()
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Mark all bookings without status' not in resp
assert '/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk) not in resp
assert '/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk) not in resp
booking2 = Booking.objects.create(event=event, user_first_name='User', user_last_name='35')
secondary_booking = Booking.objects.create(
event=event, user_first_name='User', user_last_name='35', primary_booking=booking2
)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Mark all bookings without status' in resp
assert '/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk) in resp
assert '/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk) in resp
app.post(
'/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk), params={'csrfmiddlewaretoken': token}
)
booking1.refresh_from_db()
assert booking1.user_check.presence is False
assert booking1.user_check.type_slug is None
assert booking1.user_check.type_label is None
booking2.refresh_from_db()
assert booking2.user_check.presence is True
assert booking2.user_check.type_slug is None
assert booking2.user_check.type_label is None
secondary_booking.refresh_from_db()
assert secondary_booking.user_check.presence is True
assert secondary_booking.user_check.type_slug is None
assert secondary_booking.user_check.type_label is None
event.refresh_from_db()
assert event.checked is True
# event is checked
booking3 = Booking.objects.create(event=event, user_first_name='User', user_last_name='51')
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Mark all bookings without status' in resp
app.post(
'/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
)
booking1.refresh_from_db()
assert booking1.user_check.presence is False
assert booking1.user_check.type_slug is None
assert booking1.user_check.type_label is None
booking2.refresh_from_db()
assert booking2.user_check.presence is True
assert booking2.user_check.type_slug is None
assert booking2.user_check.type_label is None
booking3.refresh_from_db()
assert booking3.user_check.presence is False
assert booking3.user_check.type_slug == 'foo-reason'
assert booking3.user_check.type_label == 'Foo reason'
booking4 = Booking.objects.create(event=event, user_first_name='User', user_last_name='52')
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Mark all bookings without status' in resp
app.post(
'/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
)
booking1.refresh_from_db()
assert booking1.user_check.presence is False
assert booking1.user_check.type_slug is None
assert booking1.user_check.type_label is None
booking2.refresh_from_db()
assert booking2.user_check.presence is True
assert booking2.user_check.type_slug is None
assert booking2.user_check.type_label is None
booking3.refresh_from_db()
assert booking3.user_check.presence is False
assert booking3.user_check.type_slug == 'foo-reason'
assert booking3.user_check.type_label == 'Foo reason'
booking4.refresh_from_db()
assert booking4.user_check.presence is True
assert booking4.user_check.type_slug == 'bar-reason'
assert booking4.user_check.type_label == 'Bar reason'
# now disable check update
agenda.disable_check_update = True
agenda.save()
Booking.objects.create(event=event, user_first_name='User', user_last_name='52')
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Mark all bookings without status' not in resp
app.post(
'/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
status=404,
)
resp = app.post(
'/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
status=404,
)
resp = app.post(
'/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# event not in past
agenda.disable_check_update = False
agenda.save()
assert agenda.enable_check_for_future_events is False
event.start_datetime = now() + datetime.timedelta(days=1)
event.save()
app.post(
'/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
app.post(
'/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=404,
)
# not in past, but check for future events is enabled
agenda.enable_check_for_future_events = True
agenda.save()
app.post(
'/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
app.post(
'/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk),
params={'csrfmiddlewaretoken': token},
status=302,
)
def test_event_check_primary_booking(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,
waiting_list_places=5,
agenda=agenda,
)
booking = Booking.objects.create(event=event, user_first_name='User', user_last_name='42')
Booking.objects.create(event=event, user_first_name='John', user_last_name='Doe')
booking_3 = Booking.objects.create(
event=event, user_first_name='Jane', user_last_name='Doe', in_waiting_list=True
)
# create secondary bookings
Booking.objects.create(event=event, user_first_name='User', user_last_name='42', primary_booking=booking)
Booking.objects.create(event=event, user_first_name='User', user_last_name='42', primary_booking=booking)
Booking.objects.create(
event=event,
user_first_name='Jane',
user_last_name='Doe',
primary_booking=booking_3,
in_waiting_list=True,
)
login(app)
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
assert 'Bookings (4/10)' in resp.text
user_bookings = resp.pyquery.find('td.booking-username.main-list')
assert len(user_bookings) == 2
assert user_bookings[0].text.replace('\n', '').strip() == 'User 42 (3 places)'
assert user_bookings[1].text.replace('\n', '').strip() == 'John Doe'
assert 'Waiting List (2/5)' in resp.text
user_bookings = resp.pyquery.find('td.booking-username.waiting')
assert len(user_bookings) == 1
assert user_bookings[0].text.replace('\n', '').strip() == 'Jane Doe (2 places)'
def test_duplicate_event(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', slug='foo-bar', kind='events')
event = Event.objects.create(
agenda=agenda,
start_datetime=now(),
places=10,
waiting_list_places=50,
label='Foo',
duration=45,
pricing='200€',
url='http://example.com',
description='foo',
)
assert Event.objects.count() == 1
new_datetime = localtime(event.start_datetime.replace(year=event.start_datetime.year + 1)).replace(
hour=17, minute=0
)
app = login(app)
resp = app.get(f'/manage/agendas/{agenda.pk}/settings')
resp = resp.click('Duplicate', href='events')
resp.form['label'] = 'Bar'
resp.form['start_datetime_0'] = str(new_datetime.date())
resp.form['start_datetime_1'] = '17:00'
resp = resp.form.submit().follow()
assert Event.objects.count() == 2
assert AgendaSnapshot.objects.count() == 1
duplicate = Event.objects.latest('pk')
assert duplicate.start_datetime == new_datetime
assert duplicate.agenda == event.agenda
updated_fields = {'label', 'start_datetime'}
identical_fields = {f.name for f in Event._meta.fields} - updated_fields - {'id', 'slug'}
for field in identical_fields:
assert getattr(duplicate, field) == getattr(event, field)
assert 'Event successfully duplicated.' in resp.text
def test_duplicate_event_creates_recurrences(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', slug='foo-bar', kind='events')
event_start = now().replace(hour=1, minute=0)
recurring_event = Event.objects.create(
agenda=agenda,
start_datetime=event_start,
recurrence_days=[1, 2, 3, 4, 5, 6, 7],
recurrence_end_date=event_start + datetime.timedelta(days=6),
places=10,
label='Foo',
)
assert Event.objects.count() == 1
app = login(app)
resp = app.get(f'/manage/agendas/{agenda.pk}/settings')
resp = resp.click('Duplicate', href='events')
resp.form['label'] = 'Bar'
resp.form['start_datetime_0'] = str(event_start.date())
resp.form['start_datetime_1'] = '17:00'
resp = resp.form.submit().follow()
duplicate = Event.objects.filter(primary_event__isnull=True).latest('pk')
assert AgendaSnapshot.objects.count() == 1
assert duplicate != recurring_event
assert duplicate.recurrences.count() == 6
event_recurrence = duplicate.recurrences.first()
app.get(f'/manage/agendas/{agenda.pk}/events/{event_recurrence.pk}/duplicate', status=404)
@pytest.mark.freeze_time('2022-05-24')
def test_event_detail_lease_display(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,
waiting_list_places=2,
agenda=agenda,
)
booking1 = Booking.objects.create(event=event, user_last_name="User's 1")
booking2 = Booking.objects.create(event=event, user_last_name='User 2', in_waiting_list=True)
Lease.objects.create(booking=booking1, lock_code='ABCD')
Lease.objects.create(booking=booking2, lock_code='ABCD')
login(app)
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Bookings (1/10)' in resp.text
assert 'Waiting List (1/2): 1 remaining place' in resp.text
assert resp.text.count('Currently being booked...') == 2