1945 lines
78 KiB
Python
1945 lines
78 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 django.utils.timezone import localtime, make_aware, make_naive, now
|
|
|
|
from chrono.agendas.models import Agenda, Booking, Category, Desk, Event, Subscription, TimePeriodException
|
|
|
|
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)
|
|
|
|
|
|
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_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
|
|
|
|
|
|
@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()],
|
|
places=2,
|
|
agenda=agenda,
|
|
)
|
|
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.get_or_create_event_recurrence(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()],
|
|
places=2,
|
|
agenda=agenda,
|
|
)
|
|
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.get_or_create_event_recurrence(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.objects.create(
|
|
slug='abc',
|
|
label='Test',
|
|
start_datetime=localtime(),
|
|
recurrence_days=[localtime().weekday()],
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
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()],
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
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() == 1
|
|
|
|
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
|
|
assert Event.objects.count() == 2
|
|
event = Booking.objects.get(pk=resp.json['booking_id']).event
|
|
assert event.slug == 'abc--2021-01-19-1305'
|
|
|
|
# first event is now a real event in datetimes
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
|
data = resp.json['data']
|
|
assert len(data) == 4
|
|
assert data[0]['id'] == event.slug
|
|
new_fillslot_url = data[0]['api']['fillslot_url']
|
|
|
|
# booking again with both old and new urls works
|
|
resp = app.post(fillslot_url)
|
|
assert resp.json['err'] == 0
|
|
resp = app.post(new_fillslot_url)
|
|
assert resp.json['err'] == 0
|
|
assert Event.objects.count() == 2
|
|
assert event.booking_set.count() == 3
|
|
|
|
# status and bookings api also create a real event
|
|
status_url = data[1]['api']['status_url']
|
|
resp = app.get(status_url)
|
|
assert resp.json['places']['total'] == 5
|
|
assert Event.objects.count() == 3
|
|
|
|
bookings_url = data[2]['api']['bookings_url']
|
|
resp = app.get(bookings_url, params={'user_external_id': '42'})
|
|
assert resp.json['data'] == []
|
|
assert Event.objects.count() == 4
|
|
|
|
# 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'
|
|
|
|
# publication date is accounted for
|
|
Event.objects.filter(primary_event=base_event).delete()
|
|
base_event.publication_datetime = now().replace(day=27)
|
|
base_event.save()
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
|
assert len(resp.json['data']) == 0
|
|
|
|
# events follow agenda display template
|
|
Event.objects.all().update(publication_datetime=None)
|
|
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. 19, 2021, 1:05 p.m."
|
|
|
|
# check querysets
|
|
agenda.event_display_template = ''
|
|
agenda.save()
|
|
base_event.publication_datetime = None
|
|
base_event.recurrence_end_date = datetime.date.today() + datetime.timedelta(days=365)
|
|
base_event.save()
|
|
base_event.create_all_recurrences()
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
|
assert len(ctx.captured_queries) == 5
|
|
|
|
# check delays
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
|
assert len(resp.json['data']) == 4
|
|
assert [e['disabled'] for e in resp.json['data']] == [False] * 4
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'bypass_delays': True})
|
|
assert len(resp.json['data']) == 53
|
|
assert [e['disabled'] for e in resp.json['data']] == [False] * 53
|
|
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']) == 53
|
|
assert [e['disabled'] for e in resp.json['data']] == [False] * 53
|
|
|
|
|
|
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()],
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
event.refresh_from_db()
|
|
|
|
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.objects.create(
|
|
slug='abc',
|
|
start_datetime=localtime(),
|
|
recurrence_days=[localtime().weekday()],
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
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),
|
|
)
|
|
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()],
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
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) == 0 # no date_start, not possible to compute recurring events in past
|
|
|
|
resp = app.get(
|
|
'/api/agenda/%s/datetimes/' % agenda.slug,
|
|
params={'events': 'past', 'date_start': localtime(now() - datetime.timedelta(days=6 * 7))},
|
|
)
|
|
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': 'all'})
|
|
data = resp.json['data']
|
|
assert len(data) == 4 # no date_start, not possible to compute recurring events in past
|
|
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': 'all', 'date_start': localtime(now() - datetime.timedelta(days=6 * 7))},
|
|
)
|
|
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
|
|
|
|
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)),
|
|
},
|
|
)
|
|
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
|
|
|
|
# check user_external_id
|
|
first_recurrence = event.get_or_create_event_recurrence(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
|
|
|
|
|
|
def test_recurring_events_api_list(app, freezer):
|
|
freezer.move_to('2021-09-06 12:00')
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
Event.objects.create(label='Normal event', start_datetime=now(), places=5, agenda=agenda)
|
|
event = Event.objects.create(
|
|
label='Example Event',
|
|
start_datetime=now(),
|
|
recurrence_days=[0, 3, 4], # Monday, Thursday, Friday
|
|
places=2,
|
|
agenda=agenda,
|
|
)
|
|
|
|
resp = app.get('/api/agendas/recurring-events/', status=400)
|
|
|
|
# recurring events without recurrence_end_date are not bookable
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug)
|
|
assert len(resp.json['data']) == 0
|
|
|
|
event.recurrence_end_date = now() + datetime.timedelta(days=30)
|
|
event.save()
|
|
start_datetime = now() + datetime.timedelta(days=15)
|
|
Event.objects.create(
|
|
label='Other',
|
|
start_datetime=start_datetime,
|
|
recurrence_days=[start_datetime.weekday()],
|
|
places=2,
|
|
agenda=agenda,
|
|
recurrence_end_date=now() + datetime.timedelta(days=45),
|
|
)
|
|
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug)
|
|
assert len(resp.json['data']) == 4
|
|
assert resp.json['data'][0]['id'] == 'foo-bar@example-event:0'
|
|
assert resp.json['data'][0]['text'] == 'Monday: Example Event'
|
|
assert resp.json['data'][0]['label'] == 'Example Event'
|
|
assert resp.json['data'][0]['day'] == 'Monday'
|
|
assert resp.json['data'][1]['id'] == 'foo-bar@other:1'
|
|
assert resp.json['data'][1]['text'] == 'Tuesday: Other'
|
|
assert resp.json['data'][1]['label'] == 'Other'
|
|
assert resp.json['data'][1]['day'] == 'Tuesday'
|
|
assert resp.json['data'][2]['id'] == 'foo-bar@example-event:3'
|
|
assert resp.json['data'][2]['text'] == 'Thursday: Example Event'
|
|
assert resp.json['data'][2]['label'] == 'Example Event'
|
|
assert resp.json['data'][2]['day'] == 'Thursday'
|
|
assert resp.json['data'][3]['id'] == 'foo-bar@example-event:4'
|
|
assert resp.json['data'][3]['text'] == 'Friday: Example Event'
|
|
assert resp.json['data'][3]['label'] == 'Example Event'
|
|
assert resp.json['data'][3]['day'] == 'Friday'
|
|
|
|
event.publication_datetime = now() + datetime.timedelta(days=2)
|
|
event.save()
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug)
|
|
assert len(resp.json['data']) == 1
|
|
|
|
freezer.move_to(event.recurrence_end_date)
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug)
|
|
assert len(resp.json['data']) == 1
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_recurring_events_api_list_multiple_agendas(app):
|
|
agenda = Agenda.objects.create(label='First Agenda', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
start, end = now(), now() + datetime.timedelta(days=30)
|
|
Event.objects.create(
|
|
label='A',
|
|
start_datetime=start,
|
|
places=2,
|
|
recurrence_end_date=end,
|
|
recurrence_days=[0, 2, 5],
|
|
agenda=agenda,
|
|
)
|
|
Event.objects.create(
|
|
label='B', start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1], agenda=agenda
|
|
)
|
|
agenda2 = Agenda.objects.create(label='Second Agenda', kind='events')
|
|
Desk.objects.create(agenda=agenda2, slug='_exceptions_holder')
|
|
Event.objects.create(
|
|
label='C',
|
|
start_datetime=start,
|
|
places=2,
|
|
recurrence_end_date=end,
|
|
recurrence_days=[2, 3],
|
|
agenda=agenda2,
|
|
)
|
|
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=first-agenda,second-agenda')
|
|
event_ids = [x['id'] for x in resp.json['data']]
|
|
assert event_ids == [
|
|
'first-agenda@a:0',
|
|
'first-agenda@b:1',
|
|
'first-agenda@a:2',
|
|
'second-agenda@c:2',
|
|
'second-agenda@c:3',
|
|
'first-agenda@a:5',
|
|
]
|
|
assert event_ids.index('first-agenda@a:2') < event_ids.index('second-agenda@c:2')
|
|
|
|
# sorting depends on querystring order
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=second-agenda,first-agenda')
|
|
event_ids = [x['id'] for x in resp.json['data']]
|
|
assert event_ids.index('first-agenda@a:2') > event_ids.index('second-agenda@c:2')
|
|
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=second-agenda')
|
|
assert [x['id'] for x in resp.json['data']] == ['second-agenda@c:2', 'second-agenda@c:3']
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_recurring_events_api_list_multiple_agendas_queries(app):
|
|
for i in range(20):
|
|
agenda = Agenda.objects.create(slug=f'{i}', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
start, end = now(), now() + datetime.timedelta(days=30)
|
|
Event.objects.create(
|
|
start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1, 2], agenda=agenda
|
|
)
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % ','.join(str(i) for i in range(20)))
|
|
assert len(resp.json['data']) == 40
|
|
assert len(ctx.captured_queries) == 3
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-05-06 14:00')
|
|
def test_datetimes_multiple_agendas(app):
|
|
first_agenda = Agenda.objects.create(
|
|
label='First agenda', kind='events', minimal_booking_delay=0, maximal_booking_delay=45
|
|
)
|
|
Desk.objects.create(agenda=first_agenda, slug='_exceptions_holder')
|
|
Event.objects.create(
|
|
slug='event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
places=5,
|
|
agenda=first_agenda,
|
|
)
|
|
Event.objects.create( # base recurring event not visible in datetimes api
|
|
slug='recurring',
|
|
start_datetime=now() + datetime.timedelta(hours=1),
|
|
recurrence_days=[localtime().weekday()],
|
|
recurrence_end_date=now() + datetime.timedelta(days=15),
|
|
places=5,
|
|
agenda=first_agenda,
|
|
) # recurrences do not exist in DB
|
|
second_agenda = Agenda.objects.create(
|
|
label='Second agenda', kind='events', minimal_booking_delay=0, maximal_booking_delay=45
|
|
)
|
|
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
|
|
event = Event.objects.create(
|
|
slug='event',
|
|
start_datetime=now() + datetime.timedelta(days=6),
|
|
places=5,
|
|
agenda=second_agenda,
|
|
)
|
|
Booking.objects.create(event=event)
|
|
|
|
agenda_slugs = '%s,%s' % (first_agenda.slug, second_agenda.slug)
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs})
|
|
assert len(resp.json['data']) == 5
|
|
assert resp.json['data'][0]['id'] == 'first-agenda@recurring:2021-05-06-1700'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@event'
|
|
assert resp.json['data'][1]['text'] == 'May 11, 2021, 4 p.m.'
|
|
assert resp.json['data'][1]['places']['available'] == 5
|
|
|
|
assert resp.json['data'][2]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][2]['text'] == 'May 12, 2021, 4 p.m.'
|
|
assert resp.json['data'][2]['places']['available'] == 4
|
|
|
|
assert resp.json['data'][3]['id'] == 'first-agenda@recurring:2021-05-13-1700'
|
|
assert resp.json['data'][4]['id'] == 'first-agenda@recurring:2021-05-20-1700'
|
|
|
|
# check user_external_id
|
|
Booking.objects.create(event=event, user_external_id='user')
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'user_external_id': 'user'})
|
|
assert resp.json['data'][1]['places']['available'] == 5
|
|
assert 'booked_for_external_user' not in resp.json['data'][1]
|
|
assert resp.json['data'][1]['disabled'] is False
|
|
|
|
assert resp.json['data'][2]['places']['available'] == 3
|
|
assert resp.json['data'][2]['booked_for_external_user'] == 'main-list'
|
|
assert resp.json['data'][2]['disabled'] is False
|
|
|
|
# check exclude_user_external_id
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'exclude_user_external_id': 'user'}
|
|
)
|
|
assert 'booked_for_external_user' not in resp.json['data'][1]
|
|
assert resp.json['data'][1]['disabled'] is False
|
|
|
|
assert 'booked_for_external_user' not in resp.json['data'][2]
|
|
assert resp.json['data'][2]['disabled'] is True
|
|
|
|
# check min_places
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'min_places': 4})
|
|
assert resp.json['data'][1]['disabled'] is False
|
|
assert resp.json['data'][2]['disabled'] is True
|
|
|
|
# check meta
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'min_places': 4})
|
|
assert resp.json['meta']['bookable_datetimes_number_total'] == 5
|
|
assert resp.json['meta']['bookable_datetimes_number_available'] == 4
|
|
assert resp.json['meta']['first_bookable_slot'] == resp.json['data'][0]
|
|
|
|
# check date_start
|
|
date_start = localtime() + datetime.timedelta(days=5, hours=1)
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'date_start': date_start})
|
|
assert len(resp.json['data']) == 3
|
|
assert resp.json['data'][0]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@recurring:2021-05-13-1700'
|
|
assert resp.json['data'][2]['id'] == 'first-agenda@recurring:2021-05-20-1700'
|
|
|
|
# check date_end
|
|
date_end = localtime() + datetime.timedelta(days=5, hours=1)
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'date_end': date_end})
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == 'first-agenda@recurring:2021-05-06-1700'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@event'
|
|
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'agendas': agenda_slugs, 'date_start': date_start, 'date_end': date_end},
|
|
)
|
|
assert len(resp.json['data']) == 0
|
|
|
|
# check delays
|
|
Agenda.objects.update(minimal_booking_delay=6, maximal_booking_delay=14)
|
|
date_end = localtime() + datetime.timedelta(days=60) # with a date end to have recurring events
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'date_end': date_end})
|
|
assert len(resp.json['data']) == 2
|
|
assert [d['disabled'] for d in resp.json['data']] == [False, False]
|
|
assert resp.json['data'][0]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@recurring:2021-05-13-1700'
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'agendas': agenda_slugs, 'date_end': date_end, 'bypass_delays': True},
|
|
)
|
|
assert len(resp.json['data']) == 5
|
|
assert resp.json['data'][0]['id'] == 'first-agenda@recurring:2021-05-06-1700'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@event'
|
|
assert resp.json['data'][2]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][3]['id'] == 'first-agenda@recurring:2021-05-13-1700'
|
|
assert resp.json['data'][4]['id'] == 'first-agenda@recurring:2021-05-20-1700'
|
|
assert [d['disabled'] for d in resp.json['data']] == [False, False, False, False, False]
|
|
Agenda.objects.update(minimal_booking_delay=0, maximal_booking_delay=45)
|
|
|
|
# invalid slugs
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': 'xxx'}, status=400)
|
|
assert resp.json['errors']['agendas'][0] == 'invalid slugs: xxx'
|
|
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': 'first-agenda,xxx,yyy'}, status=400)
|
|
assert resp.json['errors']['agendas'][0] == 'invalid slugs: xxx, yyy'
|
|
|
|
# missing agendas parameter
|
|
resp = app.get('/api/agendas/datetimes/', params={}, status=400)
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
|
|
# it's possible to show past events
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True})
|
|
assert len(resp.json['data']) == 5
|
|
assert resp.json['data'][0]['id'] == 'first-agenda@recurring:2021-05-06-1700'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@event'
|
|
assert resp.json['data'][2]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][3]['id'] == 'first-agenda@recurring:2021-05-13-1700'
|
|
assert resp.json['data'][4]['id'] == 'first-agenda@recurring:2021-05-20-1700'
|
|
assert [d['disabled'] for d in resp.json['data']] == [False, False, False, False, False]
|
|
|
|
# and events out of minimal_booking_delay
|
|
Agenda.objects.update(minimal_booking_delay=6, maximal_booking_delay=14)
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True})
|
|
assert len(resp.json['data']) == 4
|
|
assert resp.json['data'][0]['id'] == 'first-agenda@recurring:2021-05-06-1700'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@event'
|
|
assert resp.json['data'][2]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][3]['id'] == 'first-agenda@recurring:2021-05-13-1700'
|
|
assert [d['disabled'] for d in resp.json['data']] == [True, True, False, False]
|
|
Agenda.objects.update(minimal_booking_delay=0, maximal_booking_delay=45)
|
|
|
|
Event.objects.create(
|
|
slug='event-in-past',
|
|
start_datetime=now() - datetime.timedelta(days=5),
|
|
places=5,
|
|
agenda=first_agenda,
|
|
)
|
|
Event.objects.create( # base recurrring event not visible in datetimes api
|
|
slug='recurring-in-past',
|
|
start_datetime=now() - datetime.timedelta(days=15, hours=1),
|
|
recurrence_days=[localtime().weekday()],
|
|
recurrence_end_date=now(),
|
|
places=5,
|
|
agenda=first_agenda,
|
|
) # recurrences do not exist in DB
|
|
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True})
|
|
assert len(resp.json['data']) == 6
|
|
assert [d['id'] for d in resp.json['data']] == [
|
|
'first-agenda@event-in-past',
|
|
'first-agenda@recurring:2021-05-06-1700',
|
|
'first-agenda@event',
|
|
'second-agenda@event',
|
|
'first-agenda@recurring:2021-05-13-1700',
|
|
'first-agenda@recurring:2021-05-20-1700',
|
|
]
|
|
assert [d['disabled'] for d in resp.json['data']] == [True, False, False, False, False, False]
|
|
|
|
date_start = localtime() - datetime.timedelta(days=10)
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'agendas': agenda_slugs, 'date_start': date_start, 'show_past_events': True},
|
|
)
|
|
assert len(resp.json['data']) == 7
|
|
assert [d['id'] for d in resp.json['data']] == [
|
|
'first-agenda@recurring-in-past:2021-04-29-1500',
|
|
'first-agenda@event-in-past',
|
|
'first-agenda@recurring:2021-05-06-1700',
|
|
'first-agenda@event',
|
|
'second-agenda@event',
|
|
'first-agenda@recurring:2021-05-13-1700',
|
|
'first-agenda@recurring:2021-05-20-1700',
|
|
]
|
|
date_start = localtime() - datetime.timedelta(days=4)
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'agendas': agenda_slugs, 'date_start': date_start, 'show_past_events': True},
|
|
)
|
|
assert len(resp.json['data']) == 5
|
|
assert [d['id'] for d in resp.json['data']] == [
|
|
'first-agenda@recurring:2021-05-06-1700',
|
|
'first-agenda@event',
|
|
'second-agenda@event',
|
|
'first-agenda@recurring:2021-05-13-1700',
|
|
'first-agenda@recurring:2021-05-20-1700',
|
|
]
|
|
|
|
date_start = localtime() - datetime.timedelta(days=30) # with a date start to have past recurring events
|
|
date_end = localtime() + datetime.timedelta(days=5, hours=1)
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={
|
|
'agendas': agenda_slugs,
|
|
'date_start': date_start,
|
|
'date_end': date_end,
|
|
'show_past_events': True,
|
|
},
|
|
)
|
|
assert len(resp.json['data']) == 5
|
|
assert [d['id'] for d in resp.json['data']] == [
|
|
'first-agenda@recurring-in-past:2021-04-22-1500',
|
|
'first-agenda@recurring-in-past:2021-04-29-1500',
|
|
'first-agenda@event-in-past',
|
|
'first-agenda@recurring:2021-05-06-1700',
|
|
'first-agenda@event',
|
|
]
|
|
|
|
date_end = localtime() - datetime.timedelta(days=8)
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={
|
|
'agendas': agenda_slugs,
|
|
'date_start': date_start,
|
|
'date_end': date_end,
|
|
'show_past_events': True,
|
|
},
|
|
)
|
|
assert len(resp.json['data']) == 1
|
|
assert [d['id'] for d in resp.json['data']] == [
|
|
'first-agenda@recurring-in-past:2021-04-22-1500',
|
|
]
|
|
|
|
date_end = localtime() - datetime.timedelta(days=5)
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={
|
|
'agendas': agenda_slugs,
|
|
'date_start': date_start,
|
|
'date_end': date_end,
|
|
'show_past_events': True,
|
|
},
|
|
)
|
|
assert len(resp.json['data']) == 2
|
|
assert [d['id'] for d in resp.json['data']] == [
|
|
'first-agenda@recurring-in-past:2021-04-22-1500',
|
|
'first-agenda@recurring-in-past:2021-04-29-1500',
|
|
]
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-05-06 14:00')
|
|
def test_datetimes_multiple_agendas_sort(app):
|
|
category = Category.objects.create(label='Category A')
|
|
first_agenda = Agenda.objects.create(label='First agenda', kind='events', category=category)
|
|
Desk.objects.create(agenda=first_agenda, slug='_exceptions_holder')
|
|
Event.objects.create(label='10-05', start_datetime=now().replace(day=10), places=5, agenda=first_agenda)
|
|
second_agenda = Agenda.objects.create(label='Second agenda', kind='events', category=category)
|
|
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
|
|
Event.objects.create(label='09-05', start_datetime=now().replace(day=9), places=5, agenda=second_agenda)
|
|
Event.objects.create(label='04-05', start_datetime=now().replace(day=4), places=5, agenda=second_agenda)
|
|
category = Category.objects.create(label='Category B')
|
|
third_agenda = Agenda.objects.create(label='Third agenda', kind='events', category=category)
|
|
Desk.objects.create(agenda=third_agenda, slug='_exceptions_holder')
|
|
Event.objects.create(label='09-05', start_datetime=now().replace(day=9), places=5, agenda=third_agenda)
|
|
Event.objects.create(label='04-05', start_datetime=now().replace(day=4), places=5, agenda=third_agenda)
|
|
for agenda in Agenda.objects.all():
|
|
Subscription.objects.create(
|
|
agenda=agenda,
|
|
user_external_id='xxx',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=30),
|
|
)
|
|
|
|
# check events are ordered by start_datetime and then by agenda order in querystring
|
|
agenda_slugs = ','.join((first_agenda.slug, third_agenda.slug, second_agenda.slug))
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs})
|
|
assert len(resp.json['data']) == 3
|
|
assert resp.json['data'][0]['id'] == 'third-agenda@09-05'
|
|
assert resp.json['data'][1]['id'] == 'second-agenda@09-05'
|
|
assert resp.json['data'][2]['id'] == 'first-agenda@10-05'
|
|
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True})
|
|
assert len(resp.json['data']) == 5
|
|
assert resp.json['data'][0]['id'] == 'third-agenda@04-05'
|
|
assert resp.json['data'][1]['id'] == 'second-agenda@04-05'
|
|
assert resp.json['data'][2]['id'] == 'third-agenda@09-05'
|
|
assert resp.json['data'][3]['id'] == 'second-agenda@09-05'
|
|
assert resp.json['data'][4]['id'] == 'first-agenda@10-05'
|
|
|
|
# check subscribed events are ordered by start_datetime
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 3
|
|
assert resp.json['data'][0]['id'] == 'second-agenda@09-05'
|
|
assert resp.json['data'][1]['id'] == 'third-agenda@09-05'
|
|
assert resp.json['data'][2]['id'] == 'first-agenda@10-05'
|
|
|
|
# and by category slug if specified in querystring
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/', params={'subscribed': 'category-b,category-a', 'user_external_id': 'xxx'}
|
|
)
|
|
assert len(resp.json['data']) == 3
|
|
assert resp.json['data'][0]['id'] == 'third-agenda@09-05'
|
|
assert resp.json['data'][1]['id'] == 'second-agenda@09-05'
|
|
assert resp.json['data'][2]['id'] == 'first-agenda@10-05'
|
|
|
|
# order is stable if same date events are in the same category
|
|
Event.objects.create(label='09-05', start_datetime=now().replace(day=9), places=5, agenda=first_agenda)
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/', params={'subscribed': 'category-b,category-a', 'user_external_id': 'xxx'}
|
|
)
|
|
assert len(resp.json['data']) == 4
|
|
assert resp.json['data'][0]['id'] == 'third-agenda@09-05'
|
|
assert resp.json['data'][1]['id'] == 'first-agenda@09-05'
|
|
assert resp.json['data'][2]['id'] == 'second-agenda@09-05'
|
|
assert resp.json['data'][3]['id'] == 'first-agenda@10-05'
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-05-06 14:00')
|
|
def test_datetimes_multiple_agendas_queries(app):
|
|
category = Category.objects.create(label='Category A')
|
|
for i in range(10):
|
|
agenda = Agenda.objects.create(label=str(i), kind='events', category=category)
|
|
Subscription.objects.create(
|
|
agenda=agenda,
|
|
user_external_id='xxx',
|
|
date_start=now() - datetime.timedelta(days=10),
|
|
date_end=now() + datetime.timedelta(days=10),
|
|
)
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
Event.objects.create(start_datetime=now() - datetime.timedelta(days=5), places=5, agenda=agenda)
|
|
Event.objects.create(start_datetime=now() + datetime.timedelta(days=5), places=5, agenda=agenda)
|
|
Event.objects.create(start_datetime=now() + datetime.timedelta(days=5), places=5, agenda=agenda)
|
|
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'agendas': ','.join(str(i) for i in range(10)), 'show_past_events': True},
|
|
)
|
|
assert len(resp.json['data']) == 30
|
|
assert len(ctx.captured_queries) == 7
|
|
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True},
|
|
)
|
|
assert len(resp.json['data']) == 30
|
|
assert len(ctx.captured_queries) == 7
|
|
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'subscribed': 'category-a', 'user_external_id': 'xxx', 'show_past_events': True},
|
|
)
|
|
assert len(resp.json['data']) == 30
|
|
assert len(ctx.captured_queries) == 7
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-05-06 14:00')
|
|
def test_datetimes_multiple_agendas_subscribed(app):
|
|
first_agenda = Agenda.objects.create(label='First agenda', kind='events')
|
|
Desk.objects.create(agenda=first_agenda, slug='_exceptions_holder')
|
|
Event.objects.create(
|
|
slug='event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
places=5,
|
|
agenda=first_agenda,
|
|
)
|
|
Event.objects.create(
|
|
slug='event-2',
|
|
start_datetime=now() + datetime.timedelta(days=20),
|
|
places=5,
|
|
agenda=first_agenda,
|
|
)
|
|
category = Category.objects.create(label='Category A')
|
|
second_agenda = Agenda.objects.create(
|
|
label='Second agenda', kind='events', category=category, maximal_booking_delay=400
|
|
)
|
|
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
|
|
Event.objects.create(
|
|
slug='event',
|
|
start_datetime=now() + datetime.timedelta(days=20),
|
|
places=5,
|
|
agenda=second_agenda,
|
|
)
|
|
Event.objects.create(
|
|
slug='next-year-event',
|
|
start_datetime=now() + datetime.timedelta(days=365),
|
|
places=5,
|
|
agenda=second_agenda,
|
|
)
|
|
Subscription.objects.create(
|
|
agenda=first_agenda,
|
|
user_external_id='yyy',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=10),
|
|
)
|
|
|
|
# no subscription for user xxx
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 0
|
|
|
|
# add subscription to first agenda
|
|
Subscription.objects.create(
|
|
agenda=first_agenda,
|
|
user_external_id='xxx',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=10),
|
|
)
|
|
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 1
|
|
assert resp.json['data'][0]['id'] == 'first-agenda@event'
|
|
|
|
# no subscription to second agenda
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'category-a', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 0
|
|
|
|
# add subscription to second agenda
|
|
Subscription.objects.create(
|
|
agenda=second_agenda,
|
|
user_external_id='xxx',
|
|
date_start=now() + datetime.timedelta(days=15),
|
|
date_end=now() + datetime.timedelta(days=25),
|
|
)
|
|
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'category-a', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 1
|
|
assert resp.json['data'][0]['id'] == 'second-agenda@event'
|
|
|
|
# add new subscription (disjoint) to second agenda
|
|
Subscription.objects.create(
|
|
agenda=second_agenda,
|
|
user_external_id='xxx',
|
|
date_start=now() + datetime.timedelta(days=355),
|
|
date_end=now() + datetime.timedelta(days=375),
|
|
)
|
|
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'category-a', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][1]['id'] == 'second-agenda@next-year-event'
|
|
|
|
# view events from all subscriptions
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 3
|
|
assert resp.json['data'][0]['id'] == 'first-agenda@event'
|
|
assert resp.json['data'][1]['id'] == 'second-agenda@event'
|
|
assert resp.json['data'][2]['id'] == 'second-agenda@next-year-event'
|
|
|
|
# overlapping subscription changes nothing
|
|
Subscription.objects.create(
|
|
agenda=first_agenda,
|
|
user_external_id='xxx',
|
|
date_start=now() + datetime.timedelta(days=1),
|
|
date_end=now() + datetime.timedelta(days=11),
|
|
)
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'})
|
|
assert len(resp.json['data']) == 3
|
|
|
|
# check errors
|
|
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all'}, status=400)
|
|
assert 'required' in resp.json['errors']['user_external_id'][0]
|
|
|
|
resp = app.get(
|
|
'/api/agendas/datetimes/',
|
|
params={'subscribed': 'all', 'agendas': 'first-agenda', 'user_external_id': 'xxx'},
|
|
status=400,
|
|
)
|
|
assert 'mutually exclusive' in resp.json['errors']['non_field_errors'][0]
|