api: use custody agendas date start (#66330)

This commit is contained in:
Valentin Deniaud 2022-06-29 17:15:03 +02:00
parent bf9463e678
commit ebe1884e17
5 changed files with 317 additions and 13 deletions

View File

@ -824,15 +824,29 @@ class Agenda(models.Model):
@staticmethod
def filter_for_guardian(qs, guardian_external_id, child_external_id):
agendas = SharedCustodyAgenda.objects.filter(children__user_external_id=child_external_id)
qs = (
qs.annotate(week=ExtractWeek('start_datetime'))
.annotate(week_number=Cast('week', models.IntegerField()))
.annotate(odd_week=F('week_number') % 2)
)
previous_date_start = None
filtered_qs = Event.objects.none()
for agenda in agendas.order_by('-date_start'):
filtered_qs |= Agenda.filter_for_custody_agenda(
qs, agenda, guardian_external_id, date_end=previous_date_start
)
previous_date_start = agenda.date_start
return filtered_qs
@staticmethod
def filter_for_custody_agenda(qs, agenda, guardian_external_id, date_end=None):
rules = (
SharedCustodyRule.objects.filter(
guardian__user_external_id=guardian_external_id,
agenda__children__user_external_id=child_external_id,
agenda=agenda,
)
.annotate(day=Func(F('days'), function='unnest'))
.annotate(week_day=(F('day') + 1) % 7 + 1) # convert ISO day number to db lookup day number
@ -846,7 +860,7 @@ class Agenda(models.Model):
)
all_periods = SharedCustodyPeriod.objects.filter(
agenda__children__user_external_id=child_external_id,
agenda=agenda,
date_start__lte=OuterRef('start_datetime'),
date_end__gt=OuterRef('start_datetime'),
)
@ -866,9 +880,14 @@ class Agenda(models.Model):
)
rules_lookup = (rules_lookup | Q(in_holiday_period=True)) & Q(in_excluded_holiday_period=False)
return qs.filter(
(rules_lookup | Q(in_exceptional_period=True)) & Q(in_excluded_exceptional_period=False)
qs = qs.filter(
(rules_lookup | Q(in_exceptional_period=True)) & Q(in_excluded_exceptional_period=False),
start_datetime__gte=agenda.date_start,
)
if date_end:
qs = qs.filter(start_datetime__lt=date_end)
return qs
@staticmethod
def prefetch_recurring_events(qs, with_overlaps=False):

View File

@ -418,13 +418,14 @@ def test_datetimes_multiple_agendas_queries(app):
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')
agenda = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=now() - datetime.timedelta(days=5)
)
agenda.children.add(child)
for i in range(5):
agenda = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=now() - datetime.timedelta(days=5 + i)
)
agenda.children.add(child)
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')
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(
@ -438,7 +439,7 @@ def test_datetimes_multiple_agendas_queries(app):
},
)
assert len(resp.json['data']) == 30
assert len(ctx.captured_queries) == 2
assert len(ctx.captured_queries) == 3
@pytest.mark.freeze_time('2021-05-06 14:00')
@ -1214,6 +1215,138 @@ def test_datetimes_multiple_agendas_shared_custody_holiday_rules(app):
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, date_start=now()
)
agenda.children.add(child)
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 = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=10)
)
agenda.children.add(child)
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 = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=17)
)
agenda.children.add(child)
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',
]
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,
date_start=datetime.date(year=2022, month=3, day=22),
)
agenda.children.add(child)
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',
]
def test_datetimes_multiple_agendas_with_status(app):
agenda = Agenda.objects.create(label='agenda', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')

View File

@ -253,6 +253,57 @@ def test_recurring_events_api_list_shared_custody(app):
assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1', 'foo-bar@event:2']
@pytest.mark.freeze_time('2021-12-13 14:00') # Monday of 50th week
def test_recurring_events_api_list_shared_custody_start_date(app):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
slug='event',
start_datetime=now(),
recurrence_days=[0, 1, 2],
recurrence_end_date=now() + datetime.timedelta(days=30),
places=5,
agenda=agenda,
)
event.create_all_recurrences()
resp = app.get('/api/agendas/recurring-events/', params={'agendas': agenda.slug})
assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1', 'foo-bar@event:2']
# add shared two custody agendas
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')
custody_agenda = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=now()
)
custody_agenda.children.add(child)
SharedCustodyRule.objects.create(agenda=custody_agenda, guardian=father, days=[0], weeks='even')
SharedCustodyRule.objects.create(agenda=custody_agenda, guardian=mother, days=[1, 2], weeks='odd')
custody_agenda2 = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=now() + datetime.timedelta(days=15)
)
custody_agenda2.children.add(child)
SharedCustodyRule.objects.create(agenda=custody_agenda2, guardian=father, days=[1], weeks='even')
SharedCustodyRule.objects.create(agenda=custody_agenda2, guardian=mother, days=[0, 2], weeks='odd')
resp = app.get(
'/api/agendas/recurring-events/',
params={'agendas': agenda.slug, 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
)
assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1']
resp = app.get(
'/api/agendas/recurring-events/',
params={'agendas': agenda.slug, 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
)
assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1', 'foo-bar@event:2']
@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')
@ -345,7 +396,7 @@ def test_recurring_events_api_list_multiple_agendas_queries(app):
'/api/agendas/recurring-events/?subscribed=category-a&user_external_id=xxx&guardian_external_id=father_id'
)
assert len(resp.json['data']) == 40
assert len(ctx.captured_queries) == 4
assert len(ctx.captured_queries) == 5
@pytest.mark.freeze_time('2021-09-06 12:00')

View File

@ -652,6 +652,76 @@ def test_api_events_fillslots_multiple_agendas_shared_custody(app, user):
assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-thursday'
@pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week
def test_api_events_fillslots_multiple_agendas_shared_custody_date_start(app, user):
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, date_start=now()
)
agenda.children.add(child)
SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)))
agenda2 = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=10)
)
agenda2.children.add(child)
SharedCustodyRule.objects.create(agenda=agenda2, guardian=mother, days=list(range(7)))
app.authorization = ('Basic', ('john.doe', 'password'))
params = {'user_external_id': 'child_id', 'slots': 'first-agenda@event-wednesday'}
resp = app.post_json(
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=father_id', params=params
)
assert resp.json['booking_count'] == 1
resp = app.post_json(
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id',
params=params,
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-wednesday'
params['slots'] = 'first-agenda@event-thursday'
resp = app.post_json(
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id', params=params
)
assert resp.json['booking_count'] == 1
params['slots'] = 'first-agenda@event-thursday'
resp = app.post_json(
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=father_id',
params=params,
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-thursday'
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_api_events_fillslots_multiple_agendas_overlapping_events(app, user, freezer):
agenda = Agenda.objects.create(label='Foo bar', kind='events')

View File

@ -1312,7 +1312,7 @@ def test_recurring_events_api_fillslots_multiple_agendas_queries(app, user):
params={'slots': events_to_book, 'user_external_id': 'xxx'},
)
assert resp.json['booking_count'] == 100
assert len(ctx.captured_queries) == 13
assert len(ctx.captured_queries) == 14
@pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week
@ -1377,6 +1377,37 @@ def test_recurring_events_api_fillslots_shared_custody(app, user, freezer):
'2022-03-20',
]
# give father full custody from 14/03/2022
agenda2 = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=14)
)
agenda2.children.add(child)
SharedCustodyRule.objects.create(agenda=agenda2, guardian=father, days=list(range(7)))
Booking.objects.all().delete()
resp = app.post_json(fillslots_url % 'father_id', params=params)
assert [x['date'] for x in resp.json['booked_events']] == [
'2022-03-10',
'2022-03-11',
'2022-03-12',
'2022-03-14',
'2022-03-15',
'2022-03-16',
'2022-03-17',
'2022-03-18',
'2022-03-19',
'2022-03-20',
]
resp = app.post_json(fillslots_url % 'mother_id', params=params)
assert [x['date'] for x in resp.json['booked_events']] == [
'2022-03-07',
'2022-03-08',
'2022-03-09',
'2022-03-13', # last date before new agenda rules apply
]
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_recurring_events_api_fillslots_overlapping_events(app, user):