import datetime import pytest from django.db import connection from django.test.utils import CaptureQueriesContext from chrono.agendas.models import ( Agenda, Booking, Category, Desk, Event, EventsType, Person, SharedCustodyAgenda, SharedCustodyHolidayRule, SharedCustodyPeriod, SharedCustodyRule, Subscription, TimePeriodException, TimePeriodExceptionGroup, UnavailabilityCalendar, ) from chrono.utils.timezone import localtime, make_aware, now pytestmark = pytest.mark.django_db @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 = 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, ) event.create_all_recurrences() 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 = 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, ) event.create_all_recurrences() resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs, 'show_past_events': True}) assert len(resp.json['data']) == 8 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', '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, True, 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): events_type = EventsType.objects.create(label='Foo') category = Category.objects.create(label='Category A') for i in range(10): agenda = Agenda.objects.create( label=str(i), kind='events', category=category, events_type=events_type ) 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, 'check_overlaps': True, }, ) assert len(resp.json['data']) == 30 assert len(ctx.captured_queries) == 2 with CaptureQueriesContext(connection) as ctx: resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True, 'check_overlaps': True, }, ) assert len(resp.json['data']) == 30 assert len(ctx.captured_queries) == 2 with CaptureQueriesContext(connection) as ctx: resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'category-a', 'user_external_id': 'xxx', 'show_past_events': True, 'check_overlaps': True, }, ) assert len(resp.json['data']) == 30 assert len(ctx.captured_queries) == 2 father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe') mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe') child = Person.objects.create(user_external_id='xxx', first_name='James', last_name='Doe') for i in range(5): agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=now() - datetime.timedelta(days=5 + 2 * i), date_end=now() - datetime.timedelta(days=5 + 2 * i + 1), ) SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)), weeks='even') SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='odd') with CaptureQueriesContext(connection) as ctx: resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'category-a', 'user_external_id': 'xxx', 'guardian_external_id': 'mother_id', 'show_past_events': True, 'check_overlaps': True, }, ) assert len(resp.json['data']) == 30 assert len(ctx.captured_queries) == 3 @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 = Subscription.objects.create( agenda=first_agenda, user_external_id='xxx', date_start=now(), date_end=now() + datetime.timedelta(days=5), # first event after last day ) resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'}) assert len(resp.json['data']) == 0 subscription.date_start = now() + datetime.timedelta(days=10) subscription.date_end = now() + datetime.timedelta(days=21) # second event on subscription's last day subscription.save() 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-2' resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'xxx', # date_start before subscription's period, and includes the first event 'date_start': now().strftime('%Y-%m-%d'), }, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-2' subscription.date_start = now() subscription.date_end = now() + datetime.timedelta(days=6) # first event on subscription's last day subscription.save() 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' resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'xxx', # date_end after subscription's period, and includes the second event 'date_end': (now() + datetime.timedelta(days=30)).strftime('%Y-%m-%d'), }, ) 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] @pytest.mark.freeze_time('2021-05-06 14:00') def test_datetimes_multiple_agendas_recurring_subscribed_dates(app): agenda = Agenda.objects.create(label='Agenda', kind='events', minimal_booking_delay=0) Desk.objects.create(agenda=agenda, slug='_exceptions_holder') event = Event.objects.create( slug='recurring', start_datetime=now() + datetime.timedelta(days=-15, hours=1), recurrence_days=[localtime().weekday()], recurrence_end_date=now() + datetime.timedelta(days=15), places=5, agenda=agenda, ) event.create_all_recurrences() resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'}) assert len(resp.json['data']) == 0 subscription = Subscription.objects.create( agenda=agenda, user_external_id='xxx', date_start=datetime.date(2021, 4, 29), date_end=datetime.date(2021, 5, 14), ) resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx'}) assert len(resp.json['data']) == 2 assert resp.json['data'][0]['id'] == 'agenda@recurring--2021-05-06-1700' assert resp.json['data'][1]['id'] == 'agenda@recurring--2021-05-13-1700' resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True, 'date_start': subscription.date_start.strftime('%Y-%m-%d'), }, ) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True}, ) assert len(resp.json['data']) == 3 assert resp.json['data'][0]['id'] == 'agenda@recurring--2021-04-29-1700' assert resp.json['data'][1]['id'] == 'agenda@recurring--2021-05-06-1700' assert resp.json['data'][2]['id'] == 'agenda@recurring--2021-05-13-1700' resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True, 'date_start': subscription.date_start.strftime('%Y-%m-%d'), }, ) assert len(resp.json['data']) == 3 assert resp.json['data'][0]['id'] == 'agenda@recurring--2021-04-29-1700' assert resp.json['data'][1]['id'] == 'agenda@recurring--2021-05-06-1700' assert resp.json['data'][2]['id'] == 'agenda@recurring--2021-05-13-1700' # date_start before subscription's period, # date_end after subscription's period resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True, 'date_start': event.start_datetime.strftime('%Y-%m-%d'), 'date_end': (now() + datetime.timedelta(days=15)).strftime('%Y-%m-%d'), }, ) assert len(resp.json['data']) == 3 assert resp.json['data'][0]['id'] == 'agenda@recurring--2021-04-29-1700' assert resp.json['data'][1]['id'] == 'agenda@recurring--2021-05-06-1700' assert resp.json['data'][2]['id'] == 'agenda@recurring--2021-05-13-1700' # test subscription's limits subscription.date_start = datetime.date(2021, 4, 29) subscription.date_end = datetime.date(2021, 5, 13) subscription.save() resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'xxx', 'show_past_events': True, 'date_start': event.start_datetime.strftime('%Y-%m-%d'), 'date_end': (now() + datetime.timedelta(days=15)).strftime('%Y-%m-%d'), }, ) assert len(resp.json['data']) == 2 assert resp.json['data'][0]['id'] == 'agenda@recurring--2021-04-29-1700' assert resp.json['data'][1]['id'] == 'agenda@recurring--2021-05-06-1700' @pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week def test_datetimes_multiple_agendas_shared_custody(app): agenda = Agenda.objects.create(label='First agenda', kind='events') Desk.objects.create(agenda=agenda, slug='_exceptions_holder') Event.objects.create( slug='event-even-week', start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=10, hour=14, minute=0)), places=5, agenda=agenda, ) Event.objects.create( slug='event-odd-week', start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=17, hour=14, minute=0)), places=5, agenda=agenda, ) Subscription.objects.create( agenda=agenda, user_external_id='child_id', date_start=now(), date_end=now() + datetime.timedelta(days=14), ) resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id'}) assert len(resp.json['data']) == 2 # empty guardian_external_id parameter is ignored resp = app.get( '/api/agendas/datetimes/?guardian_external_id=', params={'subscribed': 'all', 'user_external_id': 'child_id'}, ) assert len(resp.json['data']) == 2 # guardian_external_id parameter is ignored if there is no custody agenda for child resp = app.get( '/api/agendas/datetimes/?guardian_external_id=xxx', params={'subscribed': 'all', 'user_external_id': 'child_id'}, ) assert len(resp.json['data']) == 2 # add shared custody agenda father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe') mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe') child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe') agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=now() ) father_rule = SharedCustodyRule.objects.create( agenda=agenda, guardian=father, days=list(range(7)), weeks='even' ) SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='odd') resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-even-week' resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-odd-week' # check date_start/date_end params resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id', 'date_start': '2022-03-09', 'date_end': '2022-03-20', }, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-even-week' # add father custody period spanning odd week event period = SharedCustodyPeriod.objects.create( agenda=agenda, guardian=father, date_start=datetime.date(2022, 3, 16), date_end=datetime.date(2022, 3, 18), ) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 2 assert resp.json['data'][0]['id'] == 'first-agenda@event-even-week' assert resp.json['data'][1]['id'] == 'first-agenda@event-odd-week' resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 0 # date_start is included period.date_start = datetime.date(2022, 3, 17) period.save() resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 2 # date_end is excluded period.date_start = datetime.date(2022, 3, 16) period.date_end = datetime.date(2022, 3, 17) period.save() resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 1 resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 1 # incomplete rules make some events not bookable father_rule.delete() resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 0 resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 1 # unknown children resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'unknown_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 0 # unknown guardian resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'unknown_id'}, ) assert len(resp.json['data']) == 0 @pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week def test_datetimes_multiple_agendas_shared_custody_other_rules(app): agenda = Agenda.objects.create(label='First agenda', kind='events') Desk.objects.create(agenda=agenda, slug='_exceptions_holder') Event.objects.create( slug='event-wednesday', start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0)), places=5, agenda=agenda, ) Event.objects.create( slug='event-thursday', start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=10, hour=14, minute=0)), places=5, agenda=agenda, ) Subscription.objects.create( agenda=agenda, user_external_id='child_id', date_start=now(), date_end=now() + datetime.timedelta(days=14), ) father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe') mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe') child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe') agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=now() ) father_rule = SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=[0, 1, 2]) mother_rule = SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=[3, 4, 5, 6]) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-wednesday' resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-thursday' father_rule.days = [0, 1] father_rule.save() other_father_rule = SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=[2]) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-wednesday' resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 1 assert resp.json['data'][0]['id'] == 'first-agenda@event-thursday' other_father_rule.delete() mother_rule.days = [2, 3, 4, 5, 6] mother_rule.save() resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 0 resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 2 @pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week def test_datetimes_multiple_agendas_shared_custody_recurring_event(app): event_agenda = Agenda.objects.create(label='First agenda', kind='events', maximal_booking_delay=30) Desk.objects.create(agenda=event_agenda, slug='_exceptions_holder') start_datetime = make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0)) wednesday_event = Event.objects.create( slug='event-wednesday', start_datetime=start_datetime, recurrence_days=[2], recurrence_end_date=start_datetime + datetime.timedelta(days=30), places=5, agenda=event_agenda, ) wednesday_event.create_all_recurrences() start_datetime = make_aware(datetime.datetime(year=2022, month=3, day=10, hour=14, minute=0)) thursday_event = Event.objects.create( slug='event-thursday', start_datetime=start_datetime, recurrence_days=[3], recurrence_end_date=start_datetime + datetime.timedelta(days=30), places=5, agenda=event_agenda, ) thursday_event.create_all_recurrences() Subscription.objects.create( agenda=event_agenda, user_external_id='child_id', date_start=now(), date_end=now() + datetime.timedelta(days=30), ) father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe') mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe') child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe') agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=now() ) father_rule = SharedCustodyRule.objects.create( agenda=agenda, guardian=father, days=list(range(7)), weeks='even' ) mother_rule = SharedCustodyRule.objects.create( agenda=agenda, guardian=mother, days=list(range(7)), weeks='odd' ) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-thursday--2022-03-10-1400', 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-thursday--2022-03-17-1400', 'first-agenda@event-wednesday--2022-03-23-1400', 'first-agenda@event-thursday--2022-03-24-1400', 'first-agenda@event-wednesday--2022-03-30-1400', 'first-agenda@event-thursday--2022-03-31-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-thursday--2022-03-10-1400', 'first-agenda@event-wednesday--2022-03-23-1400', 'first-agenda@event-thursday--2022-03-24-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-thursday--2022-03-17-1400', 'first-agenda@event-wednesday--2022-03-30-1400', 'first-agenda@event-thursday--2022-03-31-1400', ] # add father custody period spanning odd week event SharedCustodyPeriod.objects.create( agenda=agenda, guardian=father, date_start=datetime.date(2022, 3, 16), date_end=datetime.date(2022, 3, 18), ) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-thursday--2022-03-10-1400', 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-thursday--2022-03-17-1400', 'first-agenda@event-wednesday--2022-03-23-1400', 'first-agenda@event-thursday--2022-03-24-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-30-1400', 'first-agenda@event-thursday--2022-03-31-1400', ] # check date_start/date_end resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id', 'date_start': '2022-03-16', 'date_end': '2022-03-17', }, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-16-1400', ] resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id', 'date_start': '2022-03-17', 'date_end': '2022-03-18', }, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-thursday--2022-03-17-1400', ] # weirder rules father_rule.days = [0, 1] father_rule.weeks = '' father_rule.save() SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=[2]) mother_rule.weeks = '' mother_rule.days = [3, 4, 5, 6] mother_rule.save() resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-thursday--2022-03-17-1400', 'first-agenda@event-wednesday--2022-03-23-1400', 'first-agenda@event-wednesday--2022-03-30-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-thursday--2022-03-10-1400', 'first-agenda@event-thursday--2022-03-24-1400', 'first-agenda@event-thursday--2022-03-31-1400', ] thursday_event.delete() wednesday_event.recurrence_week_interval = 2 wednesday_event.save() event_agenda.update_event_recurrences() resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id'}) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-wednesday--2022-03-23-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-wednesday--2022-03-23-1400', ] # add mother custody period spanning even week event SharedCustodyPeriod.objects.create( agenda=agenda, guardian=mother, date_start=datetime.date(2022, 3, 9), date_end=datetime.date(2022, 3, 10), ) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-23-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', ] @pytest.mark.freeze_time('2021-12-13 14:00') # Monday of 50th week def test_datetimes_multiple_agendas_shared_custody_holiday_rules(app): agenda = Agenda.objects.create(label='First agenda', kind='events') Desk.objects.create(agenda=agenda, slug='_exceptions_holder') Event.objects.create( slug='event-wednesday', start_datetime=make_aware(datetime.datetime(year=2021, month=12, day=25, hour=14, minute=0)), places=5, agenda=agenda, ) Subscription.objects.create( agenda=agenda, user_external_id='child_id', date_start=now(), date_end=now() + datetime.timedelta(days=14), ) father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe') mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe') child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe') agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=now() ) SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='even', guardian=father) SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='odd', guardian=mother) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 0 resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 1 # add father custody during holidays calendar = UnavailabilityCalendar.objects.create(label='Calendar') christmas_holiday = TimePeriodExceptionGroup.objects.create( unavailability_calendar=calendar, label='Christmas', slug='christmas' ) TimePeriodException.objects.create( unavailability_calendar=calendar, start_datetime=make_aware(datetime.datetime(year=2021, month=12, day=18, hour=0, minute=0)), end_datetime=make_aware(datetime.datetime(year=2022, month=1, day=3, hour=0, minute=0)), group=christmas_holiday, ) rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=christmas_holiday) rule.update_or_create_periods() resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 1 resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 0 # check exceptional custody periods take precedence over holiday rules SharedCustodyPeriod.objects.create( agenda=agenda, guardian=mother, date_start=datetime.date(2021, 12, 22), date_end=datetime.date(2021, 12, 30), ) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert len(resp.json['data']) == 0 resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 1 @pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week def test_datetimes_multiple_agendas_shared_custody_date_start(app): agenda = Agenda.objects.create(label='First agenda', kind='events') Desk.objects.create(agenda=agenda, slug='_exceptions_holder') start_datetime = make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0)) wednesday_event = Event.objects.create( slug='event-wednesday', start_datetime=start_datetime, recurrence_days=[2], recurrence_end_date=start_datetime + datetime.timedelta(days=30), places=5, agenda=agenda, ) wednesday_event.create_all_recurrences() Subscription.objects.create( agenda=agenda, user_external_id='child_id', date_start=now(), date_end=now() + datetime.timedelta(days=30), ) father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe') mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe') child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe') agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=now() ) SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7))) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-wednesday--2022-03-23-1400', 'first-agenda@event-wednesday--2022-03-30-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert len(resp.json['data']) == 0 agenda.date_end = datetime.date(year=2022, month=3, day=9) agenda.save() agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=datetime.date(year=2022, month=3, day=10), ) SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7))) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-wednesday--2022-03-23-1400', 'first-agenda@event-wednesday--2022-03-30-1400', ] agenda.date_end = datetime.date(year=2022, month=3, day=16) agenda.save() agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=datetime.date(year=2022, month=3, day=17), ) SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)), weeks='odd') SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='even') resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', 'first-agenda@event-wednesday--2022-03-30-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-wednesday--2022-03-23-1400', ] # check date_start/date_end params resp = app.get( '/api/agendas/datetimes/', params={ 'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id', 'date_start': '2022-03-09', 'date_end': '2022-03-20', }, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', ] agenda.date_end = datetime.date(year=2022, month=3, day=21) agenda.save() other_person = Person.objects.create(user_external_id='other_person', first_name='O', last_name='P') agenda = SharedCustodyAgenda.objects.create( first_guardian=other_person, second_guardian=mother, child=child, date_start=datetime.date(year=2022, month=3, day=22), ) SharedCustodyRule.objects.create(agenda=agenda, guardian=other_person, days=list(range(7)), weeks='odd') SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='even') resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-09-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'other_person'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-30-1400', ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-16-1400', 'first-agenda@event-wednesday--2022-03-23-1400', ] @pytest.mark.freeze_time('2022-03-01 14:00') def test_datetimes_multiple_agendas_shared_custody_date_boundaries(app): agenda = Agenda.objects.create(label='First agenda', kind='events', maximal_booking_delay=0) Desk.objects.create(agenda=agenda, slug='_exceptions_holder') start_datetime = make_aware(datetime.datetime(year=2022, month=3, day=2, hour=14, minute=0)) wednesday_event = Event.objects.create( slug='event-wednesday', start_datetime=start_datetime, recurrence_days=[2], recurrence_end_date=datetime.datetime(year=2022, month=5, day=15), places=5, agenda=agenda, ) wednesday_event.create_all_recurrences() Subscription.objects.create( agenda=agenda, user_external_id='child_id', date_start=wednesday_event.start_datetime, date_end=wednesday_event.recurrence_end_date, ) father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe') mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe') child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe') agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=datetime.datetime(year=2022, month=3, day=15), # 15 days after recurring event start date_end=datetime.datetime(year=2022, month=3, day=30), # 30 days after recurring event start ) SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7))) agenda = SharedCustodyAgenda.objects.create( first_guardian=father, second_guardian=mother, child=child, date_start=datetime.datetime(year=2022, month=4, day=13), # 45 days after recurring event start date_end=datetime.datetime(year=2022, month=4, day=28), # 60 days after recurring event start ) SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7))) resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-02-1400', # no custody agenda 'first-agenda@event-wednesday--2022-03-09-1400', # no custody agenda 'first-agenda@event-wednesday--2022-03-16-1400', # has custody 'first-agenda@event-wednesday--2022-03-23-1400', # has custody 'first-agenda@event-wednesday--2022-03-30-1400', # has custody 'first-agenda@event-wednesday--2022-04-06-1400', # no custody agenda 'first-agenda@event-wednesday--2022-05-04-1400', # no custody agenda 'first-agenda@event-wednesday--2022-05-11-1400', # no custody agenda ] resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, ) assert [d['id'] for d in resp.json['data']] == [ 'first-agenda@event-wednesday--2022-03-02-1400', # no custody agenda 'first-agenda@event-wednesday--2022-03-09-1400', # no custody agenda 'first-agenda@event-wednesday--2022-04-06-1400', # no custody agenda 'first-agenda@event-wednesday--2022-04-13-1400', # has custody 'first-agenda@event-wednesday--2022-04-20-1400', # has custody 'first-agenda@event-wednesday--2022-04-27-1400', # has custody 'first-agenda@event-wednesday--2022-05-04-1400', # no custody agenda 'first-agenda@event-wednesday--2022-05-11-1400', # no custody agenda ] def test_datetimes_multiple_agendas_with_status(app): agenda = Agenda.objects.create(label='agenda', kind='events') Desk.objects.create(agenda=agenda, slug='_exceptions_holder') event_booked = Event.objects.create( slug='event-booked', start_datetime=now() - datetime.timedelta(days=15), places=5, agenda=agenda, ) Booking.objects.create(event=event_booked, user_external_id='xxx') Event.objects.create( slug='event-free', start_datetime=now() - datetime.timedelta(days=14), places=5, agenda=agenda, ) event_cancelled = Event.objects.create( slug='event-cancelled', start_datetime=now() - datetime.timedelta(days=13), places=5, agenda=agenda, ) Booking.objects.create(event=event_cancelled, user_external_id='xxx', cancellation_datetime=now()) event_absence = Event.objects.create( slug='event-absence', start_datetime=now() - datetime.timedelta(days=12), places=5, agenda=agenda, ) Booking.objects.create(event=event_absence, user_external_id='xxx', user_was_present=False) event_absence_with_reason = Event.objects.create( slug='event-absence_with_reason', start_datetime=now() - datetime.timedelta(days=11), places=5, agenda=agenda, ) Booking.objects.create( event=event_absence_with_reason, user_external_id='xxx', user_was_present=False, user_check_type_slug='foo-reason', ) event_presence = Event.objects.create( slug='event-presence', start_datetime=now() - datetime.timedelta(days=10), places=5, agenda=agenda, ) Booking.objects.create(event=event_presence, user_external_id='xxx', user_was_present=True) event_presence_with_reason = Event.objects.create( slug='event-presence_with_reason', start_datetime=now() - datetime.timedelta(days=9), places=5, agenda=agenda, ) Booking.objects.create( event=event_presence_with_reason, user_external_id='xxx', user_was_present=True, user_check_type_slug='foo-reason', ) event_booked_future = Event.objects.create( slug='event-booked-future', start_datetime=now() + datetime.timedelta(days=1), places=5, agenda=agenda, ) Booking.objects.create(event=event_booked_future, user_external_id='xxx') Event.objects.create( slug='event-free-future', start_datetime=now() + datetime.timedelta(days=2), places=5, agenda=agenda, ) resp = app.get( '/api/agendas/datetimes/', params={'agendas': 'agenda', 'show_past_events': True, 'user_external_id': 'xxx'}, ) assert [d['id'] for d in resp.json['data']] == [ 'agenda@event-booked', 'agenda@event-free', 'agenda@event-cancelled', 'agenda@event-absence', 'agenda@event-absence_with_reason', 'agenda@event-presence', 'agenda@event-presence_with_reason', 'agenda@event-booked-future', 'agenda@event-free-future', ] for d in resp.json['data']: assert 'status' not in d resp = app.get( '/api/agendas/datetimes/', params={ 'agendas': 'agenda', 'show_past_events': True, 'user_external_id': 'xxx', 'with_status': True, }, ) assert [d['id'] for d in resp.json['data']] == [ 'agenda@event-booked', 'agenda@event-free', 'agenda@event-cancelled', 'agenda@event-absence', 'agenda@event-absence_with_reason', 'agenda@event-presence', 'agenda@event-presence_with_reason', 'agenda@event-booked-future', 'agenda@event-free-future', ] assert [d['status'] for d in resp.json['data']] == [ 'booked', 'free', 'cancelled', 'absence', 'absence', 'booked', 'booked', 'booked', 'free', ] # other user resp = app.get( '/api/agendas/datetimes/', params={ 'agendas': 'agenda', 'show_past_events': True, 'user_external_id': 'yyy', 'with_status': True, }, ) assert [d['id'] for d in resp.json['data']] == [ 'agenda@event-booked', 'agenda@event-free', 'agenda@event-cancelled', 'agenda@event-absence', 'agenda@event-absence_with_reason', 'agenda@event-presence', 'agenda@event-presence_with_reason', 'agenda@event-booked-future', 'agenda@event-free-future', ] assert [d['status'] for d in resp.json['data']] == [ 'free', 'free', 'free', 'free', 'free', 'free', 'free', 'free', 'free', ] # check errors resp = app.get( '/api/agendas/datetimes/', params={'agendas': 'agenda', 'show_past_events': True, 'with_status': True}, status=400, ) assert 'required' in resp.json['errors']['user_external_id'][0] @pytest.mark.freeze_time('2021-09-06 12:00') def test_datetimes_multiple_agendas_overlapping_events(app): agenda = Agenda.objects.create(label='Foo bar', kind='events') Event.objects.create( label='Event 12-14', start_datetime=now() + datetime.timedelta(days=5), duration=120, places=5, agenda=agenda, ) Event.objects.create( label='Event containing all events', start_datetime=now() + datetime.timedelta(days=4, hours=23), duration=440, places=5, agenda=agenda, ) second_agenda = Agenda.objects.create(label='Foo bar 2', kind='events') Event.objects.create( label='Event 13-15', start_datetime=now() + datetime.timedelta(days=5, hours=1), duration=120, places=5, agenda=second_agenda, ) Event.objects.create( label='Event 14-16', start_datetime=now() + datetime.timedelta(days=5, hours=2), duration=120, places=5, agenda=second_agenda, ) Event.objects.create( label='Event no duration', start_datetime=now() + datetime.timedelta(days=5, hours=1), places=5, agenda=second_agenda, ) Event.objects.create( label='Event other day', start_datetime=now() + datetime.timedelta(days=6), places=5, agenda=second_agenda, ) resp = app.get('/api/agendas/datetimes/', params={'agendas': 'foo-bar,foo-bar-2', 'check_overlaps': True}) expected = [ ( 'foo-bar@event-containing-all-events', {'foo-bar@event-12-14', 'foo-bar-2@event-13-15', 'foo-bar-2@event-14-16'}, ), ('foo-bar@event-12-14', {'foo-bar@event-containing-all-events', 'foo-bar-2@event-13-15'}), ( 'foo-bar-2@event-13-15', {'foo-bar@event-containing-all-events', 'foo-bar@event-12-14', 'foo-bar-2@event-14-16'}, ), ('foo-bar-2@event-no-duration', set()), ('foo-bar-2@event-14-16', {'foo-bar@event-containing-all-events', 'foo-bar-2@event-13-15'}), ('foo-bar-2@event-other-day', set()), ] assert [(x['id'], set(x['overlaps'])) for x in resp.json['data']] == expected resp = app.get('/api/agendas/datetimes/', params={'agendas': 'foo-bar,foo-bar-2'}) assert ['overlaps' not in x for x in resp.json['data']] # no subscription found for this user resp = app.get( '/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'xxx', 'check_overlaps': True}, ) assert len(resp.json['data']) == 0