Projet

Général

Profil

0002-api-use-custody-agendas-date-start-66330.patch

Valentin Deniaud, 30 juin 2022 15:29

Télécharger (19,7 ko)

Voir les différences:

Subject: [PATCH 2/2] api: use custody agendas date start (#66330)

 chrono/agendas/models.py                      |  27 +++-
 .../datetimes/test_events_multiple_agendas.py | 147 +++++++++++++++++-
 tests/api/datetimes/test_recurring_events.py  |  53 ++++++-
 .../fillslot/test_events_multiple_agendas.py  |  70 +++++++++
 tests/api/fillslot/test_recurring_events.py   |  33 +++-
 5 files changed, 317 insertions(+), 13 deletions(-)
chrono/agendas/models.py
837 837

  
838 838
    @staticmethod
839 839
    def filter_for_guardian(qs, guardian_external_id, child_external_id):
840
        agendas = SharedCustodyAgenda.objects.filter(children__user_external_id=child_external_id)
840 841
        qs = (
841 842
            qs.annotate(week=ExtractWeek('start_datetime'))
842 843
            .annotate(week_number=Cast('week', models.IntegerField()))
843 844
            .annotate(odd_week=F('week_number') % 2)
844 845
        )
846

  
847
        previous_date_start = None
848
        filtered_qs = Event.objects.none()
849
        for agenda in agendas.order_by('-date_start'):
850
            filtered_qs |= Agenda.filter_for_custody_agenda(
851
                qs, agenda, guardian_external_id, date_end=previous_date_start
852
            )
853
            previous_date_start = agenda.date_start
854

  
855
        return filtered_qs
856

  
857
    @staticmethod
858
    def filter_for_custody_agenda(qs, agenda, guardian_external_id, date_end=None):
845 859
        rules = (
846 860
            SharedCustodyRule.objects.filter(
847 861
                guardian__user_external_id=guardian_external_id,
848
                agenda__children__user_external_id=child_external_id,
862
                agenda=agenda,
849 863
            )
850 864
            .annotate(day=Func(F('days'), function='unnest'))
851 865
            .annotate(week_day=(F('day') + 1) % 7 + 1)  # convert ISO day number to db lookup day number
......
859 873
        )
860 874

  
861 875
        all_periods = SharedCustodyPeriod.objects.filter(
862
            agenda__children__user_external_id=child_external_id,
876
            agenda=agenda,
863 877
            date_start__lte=OuterRef('start_datetime'),
864 878
            date_end__gt=OuterRef('start_datetime'),
865 879
        )
......
879 893
        )
880 894

  
881 895
        rules_lookup = (rules_lookup | Q(in_holiday_period=True)) & Q(in_excluded_holiday_period=False)
882
        return qs.filter(
883
            (rules_lookup | Q(in_exceptional_period=True)) & Q(in_excluded_exceptional_period=False)
896
        qs = qs.filter(
897
            (rules_lookup | Q(in_exceptional_period=True)) & Q(in_excluded_exceptional_period=False),
898
            start_datetime__gte=agenda.date_start,
884 899
        )
900
        if date_end:
901
            qs = qs.filter(start_datetime__lt=date_end)
902

  
903
        return qs
885 904

  
886 905
    @staticmethod
887 906
    def prefetch_recurring_events(qs, with_overlaps=False):
tests/api/datetimes/test_events_multiple_agendas.py
420 420
    father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe')
421 421
    mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe')
422 422
    child = Person.objects.create(user_external_id='xxx', first_name='James', last_name='Doe')
423
    agenda = SharedCustodyAgenda.objects.create(
424
        first_guardian=father, second_guardian=mother, date_start=now() - datetime.timedelta(days=5)
425
    )
426
    agenda.children.add(child)
423
    for i in range(5):
424
        agenda = SharedCustodyAgenda.objects.create(
425
            first_guardian=father, second_guardian=mother, date_start=now() - datetime.timedelta(days=5 + i)
426
        )
427
        agenda.children.add(child)
427 428

  
428
    SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)), weeks='even')
429
    SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='odd')
429
        SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)), weeks='even')
430
        SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='odd')
430 431

  
431 432
    with CaptureQueriesContext(connection) as ctx:
432 433
        resp = app.get(
......
440 441
            },
441 442
        )
442 443
        assert len(resp.json['data']) == 30
443
        assert len(ctx.captured_queries) == 2
444
        assert len(ctx.captured_queries) == 3
444 445

  
445 446

  
446 447
@pytest.mark.freeze_time('2021-05-06 14:00')
......
1216 1217
    assert len(resp.json['data']) == 1
1217 1218

  
1218 1219

  
1220
@pytest.mark.freeze_time('2022-03-07 14:00')  # Monday of 10th week
1221
def test_datetimes_multiple_agendas_shared_custody_date_start(app):
1222
    agenda = Agenda.objects.create(label='First agenda', kind='events')
1223
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
1224
    start_datetime = make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0))
1225
    wednesday_event = Event.objects.create(
1226
        slug='event-wednesday',
1227
        start_datetime=start_datetime,
1228
        recurrence_days=[2],
1229
        recurrence_end_date=start_datetime + datetime.timedelta(days=30),
1230
        places=5,
1231
        agenda=agenda,
1232
    )
1233
    wednesday_event.create_all_recurrences()
1234
    Subscription.objects.create(
1235
        agenda=agenda,
1236
        user_external_id='child_id',
1237
        date_start=now(),
1238
        date_end=now() + datetime.timedelta(days=30),
1239
    )
1240

  
1241
    father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe')
1242
    mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe')
1243
    child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe')
1244
    agenda = SharedCustodyAgenda.objects.create(
1245
        first_guardian=father, second_guardian=mother, date_start=now()
1246
    )
1247
    agenda.children.add(child)
1248
    SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)))
1249

  
1250
    resp = app.get(
1251
        '/api/agendas/datetimes/',
1252
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
1253
    )
1254
    assert [d['id'] for d in resp.json['data']] == [
1255
        'first-agenda@event-wednesday--2022-03-09-1400',
1256
        'first-agenda@event-wednesday--2022-03-16-1400',
1257
        'first-agenda@event-wednesday--2022-03-23-1400',
1258
        'first-agenda@event-wednesday--2022-03-30-1400',
1259
    ]
1260

  
1261
    resp = app.get(
1262
        '/api/agendas/datetimes/',
1263
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
1264
    )
1265
    assert len(resp.json['data']) == 0
1266

  
1267
    agenda = SharedCustodyAgenda.objects.create(
1268
        first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=10)
1269
    )
1270
    agenda.children.add(child)
1271
    SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)))
1272

  
1273
    resp = app.get(
1274
        '/api/agendas/datetimes/',
1275
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
1276
    )
1277
    assert [d['id'] for d in resp.json['data']] == [
1278
        'first-agenda@event-wednesday--2022-03-09-1400',
1279
    ]
1280

  
1281
    resp = app.get(
1282
        '/api/agendas/datetimes/',
1283
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
1284
    )
1285
    assert [d['id'] for d in resp.json['data']] == [
1286
        'first-agenda@event-wednesday--2022-03-16-1400',
1287
        'first-agenda@event-wednesday--2022-03-23-1400',
1288
        'first-agenda@event-wednesday--2022-03-30-1400',
1289
    ]
1290

  
1291
    agenda = SharedCustodyAgenda.objects.create(
1292
        first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=17)
1293
    )
1294
    agenda.children.add(child)
1295
    SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)), weeks='odd')
1296
    SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='even')
1297

  
1298
    resp = app.get(
1299
        '/api/agendas/datetimes/',
1300
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
1301
    )
1302
    assert [d['id'] for d in resp.json['data']] == [
1303
        'first-agenda@event-wednesday--2022-03-09-1400',
1304
        'first-agenda@event-wednesday--2022-03-30-1400',
1305
    ]
1306

  
1307
    resp = app.get(
1308
        '/api/agendas/datetimes/',
1309
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
1310
    )
1311
    assert [d['id'] for d in resp.json['data']] == [
1312
        'first-agenda@event-wednesday--2022-03-16-1400',
1313
        'first-agenda@event-wednesday--2022-03-23-1400',
1314
    ]
1315

  
1316
    other_person = Person.objects.create(user_external_id='other_person', first_name='O', last_name='P')
1317
    agenda = SharedCustodyAgenda.objects.create(
1318
        first_guardian=other_person,
1319
        second_guardian=mother,
1320
        date_start=datetime.date(year=2022, month=3, day=22),
1321
    )
1322
    agenda.children.add(child)
1323
    SharedCustodyRule.objects.create(agenda=agenda, guardian=other_person, days=list(range(7)), weeks='odd')
1324
    SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='even')
1325

  
1326
    resp = app.get(
1327
        '/api/agendas/datetimes/',
1328
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
1329
    )
1330
    assert [d['id'] for d in resp.json['data']] == [
1331
        'first-agenda@event-wednesday--2022-03-09-1400',
1332
    ]
1333

  
1334
    resp = app.get(
1335
        '/api/agendas/datetimes/',
1336
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'other_person'},
1337
    )
1338
    assert [d['id'] for d in resp.json['data']] == [
1339
        'first-agenda@event-wednesday--2022-03-30-1400',
1340
    ]
1341

  
1342
    resp = app.get(
1343
        '/api/agendas/datetimes/',
1344
        params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
1345
    )
1346
    assert [d['id'] for d in resp.json['data']] == [
1347
        'first-agenda@event-wednesday--2022-03-16-1400',
1348
        'first-agenda@event-wednesday--2022-03-23-1400',
1349
    ]
1350

  
1351

  
1219 1352
def test_datetimes_multiple_agendas_with_status(app):
1220 1353
    group = CheckTypeGroup.objects.create(label='Foo bar')
1221 1354
    check_type_absence = CheckType.objects.create(label='Foo reason', group=group, kind='absence')
tests/api/datetimes/test_recurring_events.py
253 253
    assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1', 'foo-bar@event:2']
254 254

  
255 255

  
256
@pytest.mark.freeze_time('2021-12-13 14:00')  # Monday of 50th week
257
def test_recurring_events_api_list_shared_custody_start_date(app):
258
    agenda = Agenda.objects.create(
259
        label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
260
    )
261
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
262
    event = Event.objects.create(
263
        slug='event',
264
        start_datetime=now(),
265
        recurrence_days=[0, 1, 2],
266
        recurrence_end_date=now() + datetime.timedelta(days=30),
267
        places=5,
268
        agenda=agenda,
269
    )
270
    event.create_all_recurrences()
271

  
272
    resp = app.get('/api/agendas/recurring-events/', params={'agendas': agenda.slug})
273
    assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1', 'foo-bar@event:2']
274

  
275
    # add shared two custody agendas
276
    father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe')
277
    mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe')
278
    child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe')
279

  
280
    custody_agenda = SharedCustodyAgenda.objects.create(
281
        first_guardian=father, second_guardian=mother, date_start=now()
282
    )
283
    custody_agenda.children.add(child)
284
    SharedCustodyRule.objects.create(agenda=custody_agenda, guardian=father, days=[0], weeks='even')
285
    SharedCustodyRule.objects.create(agenda=custody_agenda, guardian=mother, days=[1, 2], weeks='odd')
286

  
287
    custody_agenda2 = SharedCustodyAgenda.objects.create(
288
        first_guardian=father, second_guardian=mother, date_start=now() + datetime.timedelta(days=15)
289
    )
290
    custody_agenda2.children.add(child)
291
    SharedCustodyRule.objects.create(agenda=custody_agenda2, guardian=father, days=[1], weeks='even')
292
    SharedCustodyRule.objects.create(agenda=custody_agenda2, guardian=mother, days=[0, 2], weeks='odd')
293

  
294
    resp = app.get(
295
        '/api/agendas/recurring-events/',
296
        params={'agendas': agenda.slug, 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
297
    )
298
    assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1']
299

  
300
    resp = app.get(
301
        '/api/agendas/recurring-events/',
302
        params={'agendas': agenda.slug, 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
303
    )
304
    assert [x['id'] for x in resp.json['data']] == ['foo-bar@event:0', 'foo-bar@event:1', 'foo-bar@event:2']
305

  
306

  
256 307
@pytest.mark.freeze_time('2021-09-06 12:00')
257 308
def test_recurring_events_api_list_multiple_agendas(app):
258 309
    agenda = Agenda.objects.create(label='First Agenda', kind='events')
......
345 396
            '/api/agendas/recurring-events/?subscribed=category-a&user_external_id=xxx&guardian_external_id=father_id'
346 397
        )
347 398
        assert len(resp.json['data']) == 40
348
        assert len(ctx.captured_queries) == 4
399
        assert len(ctx.captured_queries) == 5
349 400

  
350 401

  
351 402
@pytest.mark.freeze_time('2021-09-06 12:00')
tests/api/fillslot/test_events_multiple_agendas.py
652 652
    assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-thursday'
653 653

  
654 654

  
655
@pytest.mark.freeze_time('2022-03-07 14:00')  # Monday of 10th week
656
def test_api_events_fillslots_multiple_agendas_shared_custody_date_start(app, user):
657
    agenda = Agenda.objects.create(label='First agenda', kind='events')
658
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
659
    Event.objects.create(
660
        slug='event-wednesday',
661
        start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0)),
662
        places=5,
663
        agenda=agenda,
664
    )
665
    Event.objects.create(
666
        slug='event-thursday',
667
        start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=10, hour=14, minute=0)),
668
        places=5,
669
        agenda=agenda,
670
    )
671
    Subscription.objects.create(
672
        agenda=agenda,
673
        user_external_id='child_id',
674
        date_start=now(),
675
        date_end=now() + datetime.timedelta(days=14),
676
    )
677

  
678
    father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe')
679
    mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe')
680
    child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe')
681

  
682
    agenda = SharedCustodyAgenda.objects.create(
683
        first_guardian=father, second_guardian=mother, date_start=now()
684
    )
685
    agenda.children.add(child)
686
    SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)))
687

  
688
    agenda2 = SharedCustodyAgenda.objects.create(
689
        first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=10)
690
    )
691
    agenda2.children.add(child)
692
    SharedCustodyRule.objects.create(agenda=agenda2, guardian=mother, days=list(range(7)))
693

  
694
    app.authorization = ('Basic', ('john.doe', 'password'))
695
    params = {'user_external_id': 'child_id', 'slots': 'first-agenda@event-wednesday'}
696
    resp = app.post_json(
697
        '/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=father_id', params=params
698
    )
699
    assert resp.json['booking_count'] == 1
700

  
701
    resp = app.post_json(
702
        '/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id',
703
        params=params,
704
        status=400,
705
    )
706
    assert resp.json['err'] == 1
707
    assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-wednesday'
708

  
709
    params['slots'] = 'first-agenda@event-thursday'
710
    resp = app.post_json(
711
        '/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id', params=params
712
    )
713
    assert resp.json['booking_count'] == 1
714

  
715
    params['slots'] = 'first-agenda@event-thursday'
716
    resp = app.post_json(
717
        '/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=father_id',
718
        params=params,
719
        status=400,
720
    )
721
    assert resp.json['err'] == 1
722
    assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-thursday'
723

  
724

  
655 725
@pytest.mark.freeze_time('2021-09-06 12:00')
656 726
def test_api_events_fillslots_multiple_agendas_overlapping_events(app, user, freezer):
657 727
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
tests/api/fillslot/test_recurring_events.py
1312 1312
            params={'slots': events_to_book, 'user_external_id': 'xxx'},
1313 1313
        )
1314 1314
        assert resp.json['booking_count'] == 100
1315
        assert len(ctx.captured_queries) == 13
1315
        assert len(ctx.captured_queries) == 14
1316 1316

  
1317 1317

  
1318 1318
@pytest.mark.freeze_time('2022-03-07 14:00')  # Monday of 10th week
......
1377 1377
        '2022-03-20',
1378 1378
    ]
1379 1379

  
1380
    # give father full custody from 14/03/2022
1381
    agenda2 = SharedCustodyAgenda.objects.create(
1382
        first_guardian=father, second_guardian=mother, date_start=datetime.date(year=2022, month=3, day=14)
1383
    )
1384
    agenda2.children.add(child)
1385

  
1386
    SharedCustodyRule.objects.create(agenda=agenda2, guardian=father, days=list(range(7)))
1387
    Booking.objects.all().delete()
1388

  
1389
    resp = app.post_json(fillslots_url % 'father_id', params=params)
1390
    assert [x['date'] for x in resp.json['booked_events']] == [
1391
        '2022-03-10',
1392
        '2022-03-11',
1393
        '2022-03-12',
1394
        '2022-03-14',
1395
        '2022-03-15',
1396
        '2022-03-16',
1397
        '2022-03-17',
1398
        '2022-03-18',
1399
        '2022-03-19',
1400
        '2022-03-20',
1401
    ]
1402

  
1403
    resp = app.post_json(fillslots_url % 'mother_id', params=params)
1404
    assert [x['date'] for x in resp.json['booked_events']] == [
1405
        '2022-03-07',
1406
        '2022-03-08',
1407
        '2022-03-09',
1408
        '2022-03-13',  # last date before new agenda rules apply
1409
    ]
1410

  
1380 1411

  
1381 1412
@pytest.mark.freeze_time('2021-09-06 12:00')
1382 1413
def test_recurring_events_api_fillslots_overlapping_events(app, user):
1383
-