Projet

Général

Profil

0001-api-allow-changing-recurrence-bookings-54746.patch

Valentin Deniaud, 05 août 2021 15:00

Télécharger (8,84 ko)

Voir les différences:

Subject: [PATCH] api: allow changing recurrence bookings (#54746)

 chrono/api/views.py        | 25 ++++++++---
 tests/api/test_fillslot.py | 87 +++++++++++++++++++++++++++++++++++---
 2 files changed, 99 insertions(+), 13 deletions(-)
chrono/api/views.py
1571 1571
                http_status=status.HTTP_400_BAD_REQUEST,
1572 1572
            )
1573 1573
        payload = serializer.validated_data
1574
        user_external_id = payload['user_external_id']
1574 1575

  
1575 1576
        open_event_slugs = set(agenda.get_open_recurring_events().values_list('slug', flat=True))
1576 1577
        slots = collections.defaultdict(list)
......
1600 1601

  
1601 1602
        events_to_book = Event.objects.filter(event_filter)
1602 1603
        events_to_book = events_to_book.filter(start_datetime__gte=start_datetime, cancelled=False)
1604
        if end_datetime:
1605
            events_to_book = events_to_book.filter(start_datetime__lte=end_datetime)
1606

  
1607
        events_to_unbook = list(
1608
            agenda.event_set.filter(booking__user_external_id=user_external_id, primary_event__isnull=False)
1609
            .exclude(pk__in=events_to_book)
1610
            .values_list('pk', flat=True)
1611
        )
1612
        events_to_book = events_to_book.exclude(booking__user_external_id=user_external_id)
1603 1613

  
1604 1614
        full_events = list(events_to_book.filter(full=True))
1605 1615
        events_to_book = events_to_book.filter(full=False)
1606
        if end_datetime:
1607
            events_to_book = events_to_book.filter(start_datetime__lte=end_datetime)
1608
        if not events_to_book.exists():
1609
            if full_events:
1610
                raise APIError(_('all events are all full'), err_class='all events are all full')
1611
            raise APIError(_('no event recurrences to book'), err_class='no event recurrences to book')
1612 1616

  
1613 1617
        events_to_book = Event.annotate_queryset(events_to_book)
1614 1618
        events_to_book = events_to_book.annotate(
......
1621 1625
        extra_data = {k: v for k, v in request.data.items() if k not in payload}
1622 1626
        bookings = [make_booking(event, payload, extra_data) for event in events_to_book]
1623 1627

  
1628
        events_to_update = Event.annotate_queryset(
1629
            agenda.event_set.filter(pk__in=events_to_unbook + [event.pk for event in events_to_book])
1630
        )
1624 1631
        with transaction.atomic():
1632
            deleted_count = Booking.objects.filter(
1633
                user_external_id=user_external_id, event__in=events_to_unbook
1634
            ).delete()[0]
1625 1635
            Booking.objects.bulk_create(bookings)
1626
            events_to_book.update(
1636
            events_to_update.update(
1627 1637
                full=Q(booked_places_count__gte=F('places'), waiting_list_places=0)
1628 1638
                | Q(waiting_list_places__gt=0, waiting_list_count__gte=F('waiting_list_places')),
1629 1639
                almost_full=Q(booked_places_count__gte=0.9 * F('places')),
......
1632 1642
        response = {
1633 1643
            'err': 0,
1634 1644
            'booking_count': len(bookings),
1645
            'cancelled_booking_count': deleted_count,
1635 1646
            'full_events': [get_event_detail(request, x, agenda=agenda) for x in full_events],
1636 1647
        }
1637 1648
        return Response(response)
tests/api/test_fillslot.py
2168 2168
    assert resp.json['booking_count'] == 155
2169 2169
    assert len(resp.json['full_events']) == 1
2170 2170
    assert resp.json['full_events'][0]['slug'] == event.slug
2171
    resp = app.post_json(fillslots_url, params=params)
2171 2172

  
2173
    # events are full
2172 2174
    params['user_external_id'] = 'user_id_4'
2173 2175
    resp = app.post_json(fillslots_url, params=params)
2174
    assert resp.json['err'] == 1
2175
    assert resp.json['err_desc'] == 'all events are all full'
2176
    assert resp.json['booking_count'] == 0
2177

  
2178
    # no event in range
2179
    resp = app.post_json(fillslots_url + '?date_start=2020-10-06&date_end=2020-11-06', params=params)
2180
    assert resp.json['booking_count'] == 0
2176 2181

  
2177 2182
    params['slots'] = 'event:1'
2178 2183
    resp = app.post_json(fillslots_url + '?date_start=2021-10-06&date_end=2021-11-06', params=params)
2179 2184
    assert resp.json['booking_count'] == 4
2180 2185
    assert Booking.objects.filter(user_external_id='user_id_4').count() == 4
2181 2186

  
2182
    resp = app.post_json(fillslots_url + '?date_start=2020-10-06&date_end=2020-11-06', params=params)
2183
    assert resp.json['err'] == 1
2184
    assert resp.json['err_desc'] == 'no event recurrences to book'
2185

  
2186 2187
    del params['user_external_id']
2187 2188
    resp = app.post_json(fillslots_url, params=params, status=400)
2188 2189
    assert resp.json['err'] == 1
......
2226 2227
    assert events.filter(waiting_list_count=2).count() == 5
2227 2228

  
2228 2229

  
2230
def test_recurring_events_api_fillslots_change_bookings(app, user, freezer):
2231
    freezer.move_to('2021-09-06 12:00')
2232
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2233
    event = Event.objects.create(
2234
        label='Event',
2235
        start_datetime=now(),
2236
        recurrence_days=[0, 1, 3, 4],  # Monday, Tuesday, Thursday, Friday
2237
        places=1,
2238
        waiting_list_places=1,
2239
        agenda=agenda,
2240
        recurrence_end_date=now() + datetime.timedelta(days=364),
2241
    )
2242
    event.create_all_recurrences()
2243

  
2244
    app.authorization = ('Basic', ('john.doe', 'password'))
2245
    fillslots_url = '/api/agenda/%s/recurring-events/fillslots/' % agenda.slug
2246
    params = {'user_external_id': 'user_id'}
2247
    # Book Monday and Thursday
2248
    params['slots'] = 'event:0,event:3'
2249
    resp = app.post_json(fillslots_url, params=params)
2250
    assert resp.json['booking_count'] == 104
2251
    assert resp.json['cancelled_booking_count'] == 0
2252
    assert Booking.objects.count() == 104
2253
    assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 52
2254
    assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 52
2255

  
2256
    # Change booking to Monday and Tuesday
2257
    params['slots'] = 'event:0,event:1'
2258
    resp = app.post_json(fillslots_url, params=params)
2259
    assert resp.json['booking_count'] == 52
2260
    assert resp.json['cancelled_booking_count'] == 52
2261
    assert Booking.objects.count() == 104
2262
    assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 52
2263
    assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 52
2264

  
2265
    # Booking again does nothing
2266
    resp = app.post_json(fillslots_url, params=params)
2267
    assert resp.json['booking_count'] == 0
2268
    assert resp.json['cancelled_booking_count'] == 0
2269
    assert Booking.objects.count() == 104
2270

  
2271
    params = {'user_external_id': 'user_id_2'}
2272
    params['slots'] = 'event:0,event:3'
2273
    resp = app.post_json(fillslots_url, params=params)
2274
    assert resp.json['booking_count'] == 104
2275
    assert resp.json['cancelled_booking_count'] == 0
2276
    assert Booking.objects.count() == 208
2277
    assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 104
2278
    assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 52
2279
    events = Event.annotate_queryset(Event.objects.filter(primary_event__isnull=False))
2280
    assert events.filter(booked_places_count=1).count() == 156
2281
    assert events.filter(waiting_list_count=1).count() == 52
2282

  
2283
    params['slots'] = 'event:1,event:4'
2284
    resp = app.post_json(fillslots_url, params=params)
2285
    assert resp.json['booking_count'] == 104
2286
    assert resp.json['cancelled_booking_count'] == 104
2287
    assert Booking.objects.count() == 208
2288
    assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 104
2289
    assert Booking.objects.filter(event__start_datetime__week_day=6).count() == 52
2290
    events = Event.annotate_queryset(Event.objects.filter(primary_event__isnull=False))
2291
    assert events.filter(booked_places_count=1).count() == 156
2292
    assert events.filter(waiting_list_count=1).count() == 52
2293

  
2294
    # only recurring events are impacted
2295
    normal_event = Event.objects.create(
2296
        start_datetime=now() + datetime.timedelta(days=1), places=2, agenda=agenda
2297
    )
2298
    Booking.objects.create(event=normal_event, user_external_id='user_id')
2299
    resp = app.post_json(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event:0'})
2300
    assert resp.json['cancelled_booking_count'] == 52
2301
    assert Booking.objects.filter(user_external_id='user_id', event=normal_event).count() == 1
2302

  
2303

  
2229 2304
@pytest.mark.freeze_time('2021-09-06 12:00')
2230 2305
def test_api_events_fillslots(app, user):
2231 2306
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2232
-