chrono/tests/api/datetimes/test_all.py

1558 lines
61 KiB
Python

import datetime
import urllib.parse as urlparse
import pytest
from django.db import connection
from django.test import override_settings
from django.test.utils import CaptureQueriesContext
from chrono.agendas.models import Agenda, Booking, Desk, Event, EventsType, TimePeriodException
from chrono.utils.timezone import localtime, make_aware, make_naive, now
from tests.utils import login
pytestmark = pytest.mark.django_db
def test_datetimes_api(app, some_data):
agenda = Agenda.objects.filter(label='Foo bar')[0]
def check_bookability(data):
for event in data:
assert Event.objects.get(slug=event['id']).in_bookable_period()
for event in agenda.event_set.all():
if not event.in_bookable_period():
assert event.slug not in [x['id'] for x in data]
resp = app.get('/api/agenda/xxx/datetimes/', status=404)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert 'data' in resp.json
assert len(resp.json['data']) == 3
check_bookability(resp.json['data'])
assert app.get('/api/agenda/%s/datetimes/' % agenda.id).json == resp.json
agenda.minimal_booking_delay = 5
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 0
check_bookability(resp.json['data'])
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'bypass_delays': True})
assert len(resp.json['data']) == 3
agenda.minimal_booking_delay = 2
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 2
check_bookability(resp.json['data'])
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'bypass_delays': True})
assert len(resp.json['data']) == 3
agenda.minimal_booking_delay = 0
agenda.maximal_booking_delay = 3
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 2
check_bookability(resp.json['data'])
assert resp.json['data'][0]['description'] is None
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'bypass_delays': True})
assert len(resp.json['data']) == 3
agenda.event_set.update(publication_datetime=now() + datetime.timedelta(minutes=1))
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 0
check_bookability(resp.json['data'])
agenda.event_set.update(publication_datetime=now())
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 2
check_bookability(resp.json['data'])
# add description, URL and pricing to events
for i, event in enumerate(agenda.event_set.all()):
event.description = 'Description %s' % i
event.url = 'https://www.example.net/%s' % i
event.pricing = '%s' % i
event.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['description']
def test_datetimes_api_wrong_kind(app):
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
app.get('/api/agenda/%s/datetimes/' % agenda.slug, status=404)
@pytest.mark.freeze_time('2023-03-10')
def test_datetimes_sort(app):
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
Event.objects.create(
slug='event-slug',
start_datetime=localtime(now() + datetime.timedelta(days=5)).replace(hour=17, minute=0),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-2',
start_datetime=localtime(now() + datetime.timedelta(days=4)).replace(hour=17, minute=0),
places=5,
agenda=agenda,
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['datetime'] == '2023-03-14 17:00:00'
assert resp.json['data'][1]['datetime'] == '2023-03-15 17:00:00'
def test_datetime_api_fr(app):
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
Event.objects.create(
slug='event-slug',
start_datetime=localtime(now() + datetime.timedelta(days=5)).replace(hour=17, minute=0),
places=5,
agenda=agenda,
)
with override_settings(LANGUAGE_CODE='fr-fr'):
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
# no seconds, hh:mm in 24-hour formats
assert resp.json['data'][0]['text'].endswith(' 17:00')
assert resp.json['data'][0]['datetime'].endswith(' 17:00:00')
assert 'data' in resp.json
@pytest.mark.freeze_time('2021-05-06 14:00')
def test_datetime_api_label(app):
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
event = Event.objects.create(
label='Hello world',
slug='event-slug',
start_datetime=(now() + datetime.timedelta(days=1)),
places=5,
agenda=agenda,
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['text'] == 'Hello world'
assert resp.json['data'][0]['label'] == 'Hello world'
agenda.event_display_template = '{{ event.label }} - {{ event.start_datetime }}'
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['text'] == 'Hello world - May 7, 2021, 4 p.m.'
assert resp.json['data'][0]['label'] == 'Hello world'
Booking.objects.create(event=event)
agenda.event_display_template = (
'{{ event.label }} ({{ event.remaining_places }}/{{ event.places }} places remaining)'
)
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['text'] == 'Hello world (4/5 places remaining)'
# invalid template yields default text
invalid_templates = [
'{{ syntax error }}',
'{{ event.label|invalidfilter }}',
'{{ event.label|default:notexist }}',
]
for template in invalid_templates:
agenda.event_display_template = template
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['text'] == 'Hello world'
def test_datetime_api_urls(app):
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
event = Event.objects.create(
slug='event-slug',
start_datetime=(now() + datetime.timedelta(days=5)).replace(hour=10, minute=0),
places=5,
waiting_list_places=5,
agenda=agenda,
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
for datum in resp.json['data']:
assert urlparse.urlparse(datum['api']['bookings_url']).path == '/api/agenda/%s/bookings/%s/' % (
agenda.slug,
event.slug,
)
assert urlparse.urlparse(datum['api']['fillslot_url']).path == '/api/agenda/%s/fillslot/%s/' % (
agenda.slug,
event.slug,
)
assert urlparse.urlparse(datum['api']['status_url']).path == '/api/agenda/%s/status/%s/' % (
agenda.slug,
event.slug,
)
def test_datetime_api_backoffice_url(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
event = Event.objects.create(
label='Example Event', start_datetime=now() + datetime.timedelta(days=5), places=5, agenda=agenda
)
app = login(app)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
url = resp.json['data'][0]['api']['backoffice_url']
assert urlparse.urlparse(url).path == '/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk)
assert event.label in app.get(url).text
def test_datetimes_api_min_places(app):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
event = Event.objects.create(start_datetime=now() + datetime.timedelta(days=7), places=5, agenda=agenda)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert not resp.json['data'][0]['disabled']
resp = app.get('/api/agenda/%s/datetimes/?min_places=5' % agenda.slug)
assert not resp.json['data'][0]['disabled']
Booking.objects.create(event=event)
resp = app.get('/api/agenda/%s/datetimes/?min_places=5' % agenda.slug)
assert resp.json['data'][0]['disabled']
resp = app.get('/api/agenda/%s/datetimes/?min_places=' % agenda.slug)
assert not resp.json['data'][0]['disabled']
resp = app.get('/api/agenda/%s/datetimes/?min_places=wrong' % agenda.slug, status=400)
assert resp.json['err'] == 1
def test_datetimes_api_(app):
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 bar', kind='events', events_type=events_type)
Event.objects.create(
slug='event-slug',
start_datetime=localtime(now() + datetime.timedelta(days=5)).replace(hour=17, minute=0),
places=5,
agenda=agenda,
custom_fields={
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
},
)
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(ctx.captured_queries) == 2
assert resp.json['data'][0]['custom_field_text'] == 'foo'
assert resp.json['data'][0]['custom_field_textarea'] == 'foo bar'
assert resp.json['data'][0]['custom_field_bool'] is True
@pytest.mark.freeze_time('2021-02-23')
def test_datetimes_api_exclude_slots(app):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
)
event = Event.objects.create(
slug='event-slug',
start_datetime=localtime().replace(hour=10, minute=0),
places=5,
agenda=agenda,
)
Booking.objects.create(event=event, user_external_id='42')
cancelled = Booking.objects.create(event=event, user_external_id='35')
cancelled.cancel()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['disabled'] is False
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '35'})
assert resp.json['data'][0]['disabled'] is False
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
assert resp.json['data'][0]['disabled'] is True
assert resp.json['meta']['first_bookable_slot'] is None
assert resp.json['meta']['no_bookable_datetimes'] is True
assert 'booked_for_external_user' not in resp.json['data'][0]
event.delete()
# recurrent event
start_datetime = localtime().replace(hour=12, minute=0)
event = Event.objects.create(
slug='recurrent',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=15),
places=2,
agenda=agenda,
)
event.create_all_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
assert resp.json['data'][0]['places']['full'] is False
assert resp.json['data'][0]['disabled'] is False
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
assert resp.json['data'][0]['places']['full'] is False
assert resp.json['data'][0]['disabled'] is False
first_recurrence = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
Booking.objects.create(event=first_recurrence, user_external_id='42')
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
assert resp.json['data'][0]['places']['full'] is False
assert resp.json['data'][0]['disabled'] is False
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
assert resp.json['data'][0]['places']['full'] is False
assert resp.json['data'][0]['disabled'] is True
Booking.objects.create(event=first_recurrence)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
assert resp.json['data'][0]['places']['full'] is True
assert resp.json['data'][0]['disabled'] is True
assert 'booked_for_external_user' not in resp.json['data'][0]
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
assert resp.json['data'][0]['places']['full'] is True
assert resp.json['data'][0]['disabled'] is True
@pytest.mark.freeze_time('2021-02-23')
def test_datetimes_api_user_external_id(app):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
)
event = Event.objects.create(
slug='event-slug',
start_datetime=localtime().replace(hour=10, minute=0),
places=5,
agenda=agenda,
)
booking = Booking.objects.create(event=event, user_external_id='42')
cancelled = Booking.objects.create(event=event, user_external_id='35')
cancelled.cancel()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert 'booked_for_external_user' not in resp.json['data'][0]
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'user_external_id': '35'})
assert 'booked_for_external_user' not in resp.json['data'][0]
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'user_external_id': '42'})
assert resp.json['data'][0]['booked_for_external_user'] == 'main-list'
assert resp.json['data'][0]['disabled'] is False
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={'user_external_id': '42', 'exclude_user_external_id': '42'},
)
assert resp.json['data'][0]['disabled'] is True
booking.in_waiting_list = True
booking.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'user_external_id': '42'})
assert resp.json['data'][0]['booked_for_external_user'] == 'waiting-list'
booking.cancel()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'user_external_id': '42'})
assert 'booked_for_external_user' not in resp.json['data'][0]
event.delete()
# recurrent event
start_datetime = localtime().replace(hour=12, minute=0)
event = Event.objects.create(
slug='recurrent',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=15),
places=2,
agenda=agenda,
)
event.create_all_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert 'booked_for_external_user' not in resp.json['data'][0]
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'user_external_id': '42'})
assert 'booked_for_external_user' not in resp.json['data'][0]
first_recurrence = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
booking = Booking.objects.create(event=first_recurrence, user_external_id='42')
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert 'booked_for_external_user' not in resp.json['data'][0]
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'user_external_id': '42'})
assert resp.json['data'][0]['booked_for_external_user'] == 'main-list'
booking.in_waiting_list = True
booking.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'user_external_id': '42'})
assert resp.json['data'][0]['booked_for_external_user'] == 'waiting-list'
# mix with exclude_user_external_id
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={'user_external_id': '42', 'exclude_user_external_id': '35'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['user_external_id'] == [
'user_external_id and exclude_user_external_id have different values'
]
def test_datetimes_api_hide_disabled(app):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
)
event = Event.objects.create(
slug='event-slug',
start_datetime=now() + datetime.timedelta(days=3),
places=1,
agenda=agenda,
)
Booking.objects.create(event=event)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['id'] == 'event-slug'
assert resp.json['data'][0]['disabled'] is True
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'hide_disabled': True})
assert resp.json['data'] == []
def test_waiting_list_datetimes(app, some_data, user):
agenda_id = Agenda.objects.filter(label='Foo bar')[0].id
event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
event.waiting_list_places = 5
event.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == 3
assert event.slug in [x['id'] for x in resp.json['data']]
for _ in range(event.places):
Booking(event=event).save()
# all places are booked but all the dates are still displayed as there is a
# waiting list.
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == 3
# fill the waiting list
for _ in range(event.waiting_list_places):
Booking(event=event, in_waiting_list=True).save()
# the event datetime should no longer be returned
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == 2
assert event.slug not in [x['id'] for x in resp.json['data'] if not x.get('disabled')]
assert event.slug in [x['id'] for x in resp.json['data'] if x.get('disabled')]
@pytest.mark.freeze_time('2017-05-20')
def test_agenda_api_date_range(app):
# test range limitation
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
first_datetime = localtime(now()).replace(hour=20, minute=0, second=0, microsecond=0)
first_datetime += datetime.timedelta(days=1)
for i in range(2):
event = Event.objects.create(
start_datetime=first_datetime + datetime.timedelta(days=i), places=20, agenda=agenda
)
base_datetime = agenda.event_set.last().start_datetime
base_datetime = base_datetime + datetime.timedelta(days=1)
for idx in range(7, 10):
if idx == 7:
day_events = ['9:00', '10:00', '11:00']
elif idx == 8:
day_events = ['13:00', '14:00']
else:
day_events = ['8:00']
day = base_datetime.date() + datetime.timedelta(days=idx)
for event in day_events:
event_dt = datetime.datetime.combine(day, datetime.datetime.strptime(event, '%H:%M').time())
Event.objects.create(agenda=agenda, start_datetime=make_aware(event_dt), places=2)
for value in ['foo', '2017-05-42']:
params = {'date_start': value}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_start'] == [
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
]
for value in ['foo', '2017-05-42']:
params = {'date_end': value}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_end'] == [
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
]
params = {'date_start': base_datetime.date().isoformat()}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 6
date_endtime = base_datetime + datetime.timedelta(days=7)
params = {'date_end': date_endtime.date().isoformat()}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['datetime'] == '2017-05-21 20:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-05-22 20:00:00'
params = {
'date_start': base_datetime.date() + datetime.timedelta(days=8),
'date_end': base_datetime.date() + datetime.timedelta(days=10),
}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 3
assert resp.json['data'][0]['datetime'] == '2017-05-31 13:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-06-01 08:00:00'
# with minimal booking delay changed
agenda.minimal_booking_delay = 3
agenda.save()
params = {'date_start': '2017-05-21'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 6
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-06-01 08:00:00'
# with maximal booking delay changed
agenda.maximal_booking_delay = 11
agenda.save()
params = {'date_end': '2017-06-01'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 3
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
# with time
params = {'date_start': '2017-05-21 foo'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_start'] == [
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
]
for start in ['2017-05-30 09:00', '2017-05-30 09:00:00']:
params = {'date_start': start}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 3
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
for start in ['2017-05-30 09:01', '2017-05-30 09:00:01', '2017-05-30 09:00:01+02:00']:
params = {'date_start': start}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['datetime'] == '2017-05-30 10:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
params = {'date_end': '2017-06-01 foo'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_end'] == [
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
]
for end in ['2017-05-30 11:01', '2017-05-30 11:00:01']:
params = {'date_end': end}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 3
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
for end in ['2017-05-30 11:00', '2017-05-30 11:00:00', '2017-05-30 11:00:00+02:00']:
params = {'date_end': end}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2017-05-30 10:00:00'
def test_datetimes_api_meta(app, freezer):
# 2017-05-20 -> saturday
freezer.move_to(make_aware(datetime.datetime(year=2017, month=5, day=20, hour=1, minute=12)))
agenda = Agenda.objects.create(label='Foo bar')
first_date = localtime(now()).replace(hour=17, minute=0, second=0, microsecond=0)
first_date += datetime.timedelta(days=1)
for i in range(3):
event = Event(start_datetime=first_date + datetime.timedelta(days=i), places=20, agenda=agenda)
event.save()
# a date in the past
event = Event(start_datetime=first_date - datetime.timedelta(days=10), places=10, agenda=agenda)
event.save()
events = Event.objects.filter(agenda_id=agenda.id).exclude(start_datetime__lt=now())
assert len(events) == 3
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
resp = app.get(api_url)
assert len(resp.json['data']) == 3
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 3,
'bookable_datetimes_number_available': 3,
'first_bookable_slot': resp.json['data'][0],
}
def simulate_booking(event, nb_places):
for _ in range(nb_places):
Booking(event=event, user_external_id='42').save()
simulate_booking(events[0], 10)
resp = app.get(api_url)
assert len(resp.json['data']) == 3
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 3,
'bookable_datetimes_number_available': 3,
'first_bookable_slot': resp.json['data'][0],
}
resp = app.get(api_url, params={'exclude_user_external_id': '42'})
assert len(resp.json['data']) == 3
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 3,
'bookable_datetimes_number_available': 2,
'first_bookable_slot': resp.json['data'][1],
}
resp = app.get(api_url + '?min_places=11')
assert len(resp.json['data']) == 3
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 3,
'bookable_datetimes_number_available': 2,
'first_bookable_slot': resp.json['data'][1],
}
simulate_booking(events[0], 10)
resp = app.get(api_url)
assert len(resp.json['data']) == 3
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 3,
'bookable_datetimes_number_available': 2,
'first_bookable_slot': resp.json['data'][1],
}
simulate_booking(events[1], 20)
simulate_booking(events[2], 20)
resp = app.get(api_url)
assert len(resp.json['data']) == 3
assert resp.json['meta'] == {
'no_bookable_datetimes': True,
'bookable_datetimes_number_total': 3,
'bookable_datetimes_number_available': 0,
'first_bookable_slot': None,
}
# recurring event
Event.objects.all().delete()
event = Event.objects.create(
slug='abc',
label='Test',
start_datetime=localtime(),
recurrence_days=[localtime().weekday()],
recurrence_end_date=localtime() + datetime.timedelta(days=15),
places=5,
agenda=agenda,
)
event.create_all_recurrences()
resp = app.get(api_url)
assert resp.json['meta']['first_bookable_slot']['text'] == 'Test (May 27, 2017, 1:12 a.m.)'
def test_recurring_events_api(app, user, freezer):
freezer.move_to('2021-01-12 12:05') # Tuesday
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
)
base_event = Event.objects.create(
slug='abc',
label="Rock'n roll",
start_datetime=localtime(),
recurrence_days=[localtime().weekday()],
recurrence_end_date=localtime() + datetime.timedelta(days=30),
places=5,
agenda=agenda,
)
base_event.create_all_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
data = resp.json['data']
assert len(data) == 4
assert data[0]['id'] == 'abc--2021-01-19-1305'
assert data[0]['datetime'] == '2021-01-19 13:05:00'
assert data[0]['text'] == "Rock'n roll (Jan. 19, 2021, 1:05 p.m.)"
assert data[3]['id'] == 'abc--2021-02-09-1305'
assert Event.objects.count() == 6
fillslot_url = data[0]['api']['fillslot_url']
app.authorization = ('Basic', ('john.doe', 'password'))
# book first event
resp = app.post(fillslot_url)
assert resp.json['err'] == 0
event = Booking.objects.get(pk=resp.json['booking_id']).event
assert event.slug == 'abc--2021-01-19-1305'
# cancelled recurrences do not appear
event.cancel()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 3
assert resp.json['data'][0]['id'] == 'abc--2021-01-26-1305'
# check querysets
with CaptureQueriesContext(connection) as ctx:
app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(ctx.captured_queries) == 3
# events follow agenda display template
agenda.event_display_template = '{{ event.label }} - {{ event.start_datetime }}'
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert resp.json['data'][0]['text'] == "Rock'n roll - Jan. 26, 2021, 1:05 p.m."
# check delays
base_event.recurrence_end_date += datetime.timedelta(days=30)
base_event.save()
agenda.update_event_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 3
assert [e['disabled'] for e in resp.json['data']] == [False] * 3
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'bypass_delays': True})
assert len(resp.json['data']) == 8
assert [e['disabled'] for e in resp.json['data']] == [False] * 8
agenda.minimal_booking_delay = 10
agenda.mmaximal_booking_delay = 20
agenda.save()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 3
assert [e['disabled'] for e in resp.json['data']] == [False] * 3
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'bypass_delays': True})
assert len(resp.json['data']) == 8
assert [e['disabled'] for e in resp.json['data']] == [False] * 8
def test_recurring_events_api_various_times(app, user, mock_now):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
event = Event.objects.create(
slug='abc',
start_datetime=localtime(),
recurrence_days=[localtime().weekday()],
recurrence_end_date=localtime() + datetime.timedelta(days=30),
places=5,
agenda=agenda,
)
event.create_all_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 5
fillslot_url = resp.json['data'][0]['api']['fillslot_url']
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post(fillslot_url)
assert resp.json['err'] == 0
new_event = Booking.objects.get(pk=resp.json['booking_id']).event
assert event.start_datetime == new_event.start_datetime
def test_datetimes_dst(app, freezer):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=10
)
event = Event.objects.create(
slug='event-slug',
start_datetime=make_aware(datetime.datetime(2021, 3, 28, 12, 30)),
places=5,
agenda=agenda,
)
freezer.move_to('Fri, 18 Mar 2021 23:59:39 +0100')
# maximal_booking_delay is 10days so last bookable event is on
# 2021-03-28T00:00, so event should not be bookable
assert not event.in_bookable_period()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 0
def test_recurring_events_api_exceptions(app, user, freezer):
freezer.move_to('2021-01-12 12:05') # Tuesday
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
slug='abc',
start_datetime=localtime(),
recurrence_days=[localtime().weekday()],
recurrence_end_date=localtime() + datetime.timedelta(days=30),
places=5,
agenda=agenda,
)
event.create_all_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
data = resp.json['data']
assert len(data) == 4
assert data[0]['datetime'] == '2021-01-19 13:05:00'
TimePeriodException.objects.create(
desk=agenda.desk_set.get(),
start_datetime=datetime.date(year=2021, month=1, day=18),
end_datetime=datetime.date(year=2021, month=1, day=20),
)
agenda.update_event_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 3
assert resp.json['data'][0]['datetime'] == '2021-01-26 13:05:00'
# try to book excluded event
fillslot_url = data[0]['api']['fillslot_url']
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post(fillslot_url, status=400)
assert resp.json['err'] == 1
def test_past_datetimes(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
event1 = Event.objects.create(
label='Today before now',
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
event2 = Event.objects.create(
label='Today after now',
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'future'})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
assert (
data[0]['api']['fillslot_url']
== 'http://testserver/api/agenda/foo-bar/fillslot/today-after-now/?events=future'
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past'})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert data[0]['disabled'] is False
assert (
data[0]['api']['fillslot_url']
== 'http://testserver/api/agenda/foo-bar/fillslot/today-before-now/?events=past'
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'all'})
data = resp.json['data']
assert len(data) == 2
assert data[0]['id'] == 'today-before-now'
assert data[1]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
assert (
data[0]['api']['fillslot_url']
== 'http://testserver/api/agenda/foo-bar/fillslot/today-before-now/?events=all'
)
assert (
data[1]['api']['fillslot_url']
== 'http://testserver/api/agenda/foo-bar/fillslot/today-after-now/?events=all'
)
# check canceled
event1.cancel()
event2.cancel()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'all'})
data = resp.json['data']
assert len(data) == 0
def test_past_datetimes_meta(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
Event.objects.create(
label='Today before now',
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
Event.objects.create(
label='Today after now',
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'future'})
assert len(resp.json['data']) == 1
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 1,
'bookable_datetimes_number_available': 1,
'first_bookable_slot': resp.json['data'][0],
}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past'})
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['id'] == 'today-before-now'
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 1,
'bookable_datetimes_number_available': 1,
'first_bookable_slot': resp.json['data'][0],
}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'all'})
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['id'] == 'today-before-now'
assert resp.json['data'][1]['id'] == 'today-after-now'
assert resp.json['meta'] == {
'no_bookable_datetimes': False,
'bookable_datetimes_number_total': 2,
'bookable_datetimes_number_available': 2,
'first_bookable_slot': resp.json['data'][0],
}
def test_past_datetimes_date_range(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
Event.objects.create(
label='Yesterday 18H',
start_datetime=localtime(now() - datetime.timedelta(days=1)).replace(hour=18, minute=0),
places=5,
agenda=agenda,
)
Event.objects.create(
label='Today before now',
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
Event.objects.create(
label='Today after now',
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
Event.objects.create(
label='Tomorrow 16H',
start_datetime=localtime(now() + datetime.timedelta(days=1)).replace(hour=16, minute=0),
places=5,
agenda=agenda,
)
# check date_start
params = {'date_start': localtime(now() - datetime.timedelta(hours=2)), 'events': 'future'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 2
assert data[0]['id'] == 'today-after-now'
assert data[1]['id'] == 'tomorrow-16h'
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
params = {'date_start': localtime(now() - datetime.timedelta(hours=2)), 'events': 'past'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert data[0]['disabled'] is False
params = {'date_start': localtime(now() - datetime.timedelta(hours=2)), 'events': 'all'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 3
assert data[0]['id'] == 'today-before-now'
assert data[1]['id'] == 'today-after-now'
assert data[2]['id'] == 'tomorrow-16h'
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
assert data[2]['disabled'] is False
params = {'date_start': localtime(now() + datetime.timedelta(hours=2)), 'events': 'future'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'tomorrow-16h'
assert data[0]['disabled'] is False
params = {'date_start': localtime(now() + datetime.timedelta(hours=2)), 'events': 'past'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 0
params = {'date_start': localtime(now() + datetime.timedelta(hours=2)), 'events': 'all'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'tomorrow-16h'
assert data[0]['disabled'] is False
# check date_end
params = {'date_end': localtime(now() + datetime.timedelta(hours=2)), 'events': 'future'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
params = {'date_end': localtime(now() + datetime.timedelta(hours=2)), 'events': 'past'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 2
assert data[0]['id'] == 'yesterday-18h'
assert data[1]['id'] == 'today-before-now'
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
params = {'date_end': localtime(now() + datetime.timedelta(hours=2)), 'events': 'all'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 3
assert data[0]['id'] == 'yesterday-18h'
assert data[1]['id'] == 'today-before-now'
assert data[2]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
assert data[2]['disabled'] is False
params = {'date_end': localtime(now() - datetime.timedelta(hours=2)), 'events': 'future'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 0
params = {'date_end': localtime(now() - datetime.timedelta(hours=2)), 'events': 'past'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'yesterday-18h'
assert data[0]['disabled'] is False
params = {'date_end': localtime(now() - datetime.timedelta(hours=2)), 'events': 'all'}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'yesterday-18h'
assert data[0]['disabled'] is False
# mix
params = {
'date_start': localtime(now() - datetime.timedelta(hours=2)),
'date_end': localtime(now() + datetime.timedelta(hours=2)),
'events': 'future',
}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
params = {
'date_start': localtime(now() - datetime.timedelta(hours=2)),
'date_end': localtime(now() + datetime.timedelta(hours=2)),
'events': 'past',
}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert data[0]['disabled'] is False
params = {
'date_start': localtime(now() - datetime.timedelta(hours=2)),
'date_end': localtime(now() + datetime.timedelta(hours=2)),
'events': 'all',
}
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
data = resp.json['data']
assert len(data) == 2
assert data[0]['id'] == 'today-before-now'
assert data[1]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
def test_past_datetimes_places(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
event1 = Event.objects.create(
label='Today before now',
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
places=1,
agenda=agenda,
)
event2 = Event.objects.create(
label='Today after now',
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
places=1,
agenda=agenda,
)
Booking.objects.create(event=event1)
Booking.objects.create(event=event2)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'future'})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert resp.json['data'][0]['places']['full'] is True
assert data[0]['disabled'] is True
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past'})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert resp.json['data'][0]['places']['full'] is True
assert data[0]['disabled'] is False # always available if past
assert resp.json['meta']['first_bookable_slot']['id'] == 'today-before-now'
event1.waiting_list_places = 1
event1.save()
event2.waiting_list_places = 1
event2.save()
Booking.objects.create(event=event1, in_waiting_list=True)
Booking.objects.create(event=event2, in_waiting_list=True)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'future'})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert resp.json['data'][0]['places']['full'] is True
assert data[0]['disabled'] is True
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past'})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert resp.json['data'][0]['places']['full'] is True
assert data[0]['disabled'] is False # always available if past
assert resp.json['meta']['first_bookable_slot']['id'] == 'today-before-now'
def test_past_datetimes_min_places(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
Event.objects.create(
label='Today before now',
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
places=1,
agenda=agenda,
)
Event.objects.create(
label='Today after now',
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
places=1,
agenda=agenda,
)
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'future', 'min_places': 2})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert data[0]['disabled'] is True
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past', 'min_places': 2})
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert data[0]['disabled'] is False # always available if past
assert resp.json['meta']['first_bookable_slot']['id'] == 'today-before-now'
def test_past_datetimes_exclude_slots(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
event1 = Event.objects.create(
label='Today before now',
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
event2 = Event.objects.create(
label='Today after now',
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
places=5,
agenda=agenda,
)
Booking.objects.create(event=event1, user_external_id='42')
Booking.objects.create(event=event2, user_external_id='42')
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={'events': 'future', 'exclude_user_external_id': '35'},
)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert data[0]['disabled'] is False
assert resp.json['meta']['first_bookable_slot']['id'] == 'today-after-now'
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past', 'exclude_user_external_id': '35'}
)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert data[0]['disabled'] is False
assert resp.json['meta']['first_bookable_slot']['id'] == 'today-before-now'
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={'events': 'future', 'exclude_user_external_id': '42'},
)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-after-now'
assert data[0]['disabled'] is True
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past', 'exclude_user_external_id': '42'}
)
data = resp.json['data']
assert len(data) == 1
assert data[0]['id'] == 'today-before-now'
assert data[0]['disabled'] is True
def test_past_datetimes_recurring_event(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
)
start_datetime = make_aware(make_naive(now()) - datetime.timedelta(days=3 * 7))
event = Event.objects.create(
label='Recurring',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=60),
places=5,
agenda=agenda,
)
event.create_all_recurrences()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'future'})
data = resp.json['data']
assert len(data) == 4
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
assert data[2]['disabled'] is False
assert data[3]['disabled'] is False
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'past'})
data = resp.json['data']
assert len(data) == 4
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
assert data[2]['disabled'] is False
assert data[3]['disabled'] is False
# same result with explicit date_start
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={'events': 'past', 'date_start': localtime(now() - datetime.timedelta(days=6 * 7))},
)
assert resp.json['data'] == data
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'events': 'all'})
data = resp.json['data']
assert len(data) == 8
assert data[0]['disabled'] is False
assert data[1]['disabled'] is False
assert data[2]['disabled'] is False
assert data[3]['disabled'] is False
assert data[4]['disabled'] is False
assert data[5]['disabled'] is False
assert data[6]['disabled'] is False
assert data[7]['disabled'] is False
# same result with explicit date_start
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={'events': 'all', 'date_start': localtime(now() - datetime.timedelta(days=6 * 7))},
)
assert resp.json['data'] == data
# same result with explicit date_start and date_end
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={
'events': 'all',
'date_start': localtime(now() - datetime.timedelta(days=6 * 7)),
'date_end': localtime(now() + datetime.timedelta(days=6 * 7)),
},
)
assert resp.json['data'] == data
# check user_external_id
first_recurrence = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
booking = Booking.objects.create(event=first_recurrence, user_external_id='42', in_waiting_list=True)
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={
'events': 'past',
'user_external_id': '42',
'date_start': localtime(now() - datetime.timedelta(days=6 * 7)),
},
)
data = resp.json['data']
assert data[0]['booked_for_external_user'] == 'waiting-list'
booking.in_waiting_list = False
booking.save()
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={
'events': 'past',
'user_external_id': '42',
'date_start': localtime(now() - datetime.timedelta(days=6 * 7)),
},
)
data = resp.json['data']
assert data[0]['booked_for_external_user'] == 'main-list'
# check exclude_user_external_id
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={
'events': 'past',
'exclude_user_external_id': '42',
'date_start': localtime(now() - datetime.timedelta(days=6 * 7)),
},
)
data = resp.json['data']
assert len(data) == 4
assert data[0]['disabled'] is True
assert data[1]['disabled'] is False
assert data[2]['disabled'] is False
assert data[3]['disabled'] is False
# check canceled
first_recurrence.cancel()
resp = app.get(
'/api/agenda/%s/datetimes/' % agenda.slug,
params={'events': 'all', 'date_start': localtime(now() - datetime.timedelta(days=6 * 7))},
)
data = resp.json['data']
assert len(data) == 7
@pytest.mark.freeze_time('2023-04-03')
def test_events_datetimes_min_booking_datetime_with_minimal_booking_time(app):
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
Event.objects.create(
slug='event-slug',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=3, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-2',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=3, hour=11)),
places=5,
agenda=agenda,
)
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
resp = app.get(api_url)
assert resp.json['data'][0]['datetime'] == '2023-04-03 09:00:00'
assert resp.json['data'][1]['datetime'] == '2023-04-03 11:00:00'
# set a minimal minimal_booking_time and check that it has no impact
agenda.minimal_booking_time = datetime.time(10, 0, 0)
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][0]['datetime'] == '2023-04-03 09:00:00'
assert resp.json['data'][1]['datetime'] == '2023-04-03 11:00:00'
# set a minimal minimal_booking_time to None check that it has no impact
agenda.minimal_booking_time = None
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][0]['datetime'] == '2023-04-03 09:00:00'
assert resp.json['data'][1]['datetime'] == '2023-04-03 11:00:00'
def test_events_datetimes_max_booking_datetime_with_minimal_booking_time(app, freezer):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=3
)
Event.objects.create(
slug='event-slug-0',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-1',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-2',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-3',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-4',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-5',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=11)),
places=5,
agenda=agenda,
)
# last slots visible are the one on J + maximal_booking_delay (3) -1
freezer.move_to('2023-04-03T00:00:00+02:00')
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# # move to noon, no changes
freezer.move_to('2023-04-03T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# set a minimal minimal_booking_time earlier than current time, no changes
agenda.minimal_booking_time = datetime.time(10, 0, 0)
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# set a minimal minimal_booking_time later than current time, slots of 2023-04-05 disappear
agenda.minimal_booking_time = datetime.time(14, 0, 0)
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-04 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-04 11:00:00'
# move to a time superior to minimal_booking_time (14:00), slots of 2023-04-05 re-appear
freezer.move_to('2023-04-03T15:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# move to the day after, prior to minimal_booking_time (14:00), no changes
freezer.move_to('2023-04-04T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# move to the day after, after minimal_booking_time (14:00), new slots available
freezer.move_to('2023-04-04T15:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-06 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-06 11:00:00'
def test_events_datetimes_max_booking_datetime_with_minimal_booking_time_to_none(app, freezer):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=3
)
Event.objects.create(
slug='event-slug-0',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-1',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-2',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-3',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-4',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-5',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=11)),
places=5,
agenda=agenda,
)
# last slots visible are the one on J + maximal_booking_delay (3) -1
freezer.move_to('2023-04-03T00:00:00+02:00')
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# set a minimal minimal_booking_time to None, 2023-04-05 disappear
# because current time is 00:00 and slots starts later
agenda.minimal_booking_time = None
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-04 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-04 11:00:00'
# move a few hours later, juste after the first slot time of a day
# a new slot becomes available
freezer.move_to('2023-04-03T09:01:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-1]['datetime'] == '2023-04-05 09:00:00'
# at 12:00, every slots are available
freezer.move_to('2023-04-03T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'