Projet

Général

Profil

0003-api-add-endpoint-to-book-multiple-events-independent.patch

Valentin Deniaud, 03 août 2021 14:43

Télécharger (6,28 ko)

Voir les différences:

Subject: [PATCH 3/4] api: add endpoint to book multiple events independently
 (#55367)

 chrono/api/urls.py         |  5 ++++
 chrono/api/views.py        | 59 ++++++++++++++++++++++++++++++++++++--
 tests/api/test_fillslot.py | 39 +++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 2 deletions(-)
chrono/api/urls.py
33 33
        name='api-fillslot',
34 34
    ),
35 35
    url(r'^agenda/(?P<agenda_identifier>[\w-]+)/fillslots/$', views.fillslots, name='api-agenda-fillslots'),
36
    url(
37
        r'^agenda/(?P<agenda_identifier>[\w-]+)/events/fillslots/$',
38
        views.events_fillslots,
39
        name='api-agenda-events-fillslots',
40
    ),
36 41
    url(
37 42
        r'^agenda/(?P<agenda_identifier>[\w-]+)/recurring_fillslots/$',
38 43
        views.recurring_fillslots,
chrono/api/views.py
1163 1163
        return attrs
1164 1164

  
1165 1165

  
1166
class EventSlotsSerializer(SlotsSerializer):
1166
class EventsSlotsSerializer(SlotsSerializer):
1167 1167
    def validate(self, attrs):
1168 1168
        super().validate(attrs)
1169 1169
        if not attrs.get('user_external_id'):
......
1551 1551

  
1552 1552
class RecurringFillslots(APIView):
1553 1553
    permission_classes = (permissions.IsAuthenticated,)
1554
    serializer_class = EventSlotsSerializer
1554
    serializer_class = EventsSlotsSerializer
1555 1555

  
1556 1556
    def post(self, request, agenda_identifier):
1557 1557
        if not settings.ENABLE_RECURRING_EVENT_BOOKING:
......
1640 1640
recurring_fillslots = RecurringFillslots.as_view()
1641 1641

  
1642 1642

  
1643
class EventsFillslots(APIView):
1644
    permission_classes = (permissions.IsAuthenticated,)
1645
    serializer_class = EventsSlotsSerializer
1646

  
1647
    def post(self, request, agenda_identifier):
1648
        agenda = get_object_or_404(Agenda, slug=agenda_identifier, kind='events')
1649
        serializer = self.serializer_class(data=request.data, partial=True)
1650
        if not serializer.is_valid():
1651
            raise APIError(
1652
                _('invalid payload'),
1653
                err_class='invalid payload',
1654
                errors=serializer.errors,
1655
                http_status=status.HTTP_400_BAD_REQUEST,
1656
            )
1657
        payload = serializer.validated_data
1658

  
1659
        events = get_events_from_slots(payload['slots'], request, agenda, payload)
1660
        events = Event.annotate_queryset(events)
1661

  
1662
        full_events = [str(event) for event in events.filter(full=True)]
1663
        if full_events:
1664
            raise APIError(
1665
                _('some events are full: %s') % ', '.join(full_events), err_class='some events are full'
1666
            )
1667

  
1668
        events = events.annotate(
1669
            in_waiting_list=ExpressionWrapper(
1670
                Q(booked_places_count__gte=F('places')) | Q(waiting_list_count__gt=0),
1671
                output_field=BooleanField(),
1672
            )
1673
        )
1674
        waiting_list_events = [event for event in events if event.in_waiting_list]
1675

  
1676
        extra_data = {k: v for k, v in request.data.items() if k not in payload}
1677
        bookings = [make_booking(event, payload, extra_data) for event in events]
1678

  
1679
        with transaction.atomic():
1680
            Booking.objects.bulk_create(bookings)
1681
            events.update(
1682
                full=Q(booked_places_count__gte=F('places'), waiting_list_places=0)
1683
                | Q(waiting_list_places__gt=0, waiting_list_count__gte=F('waiting_list_places')),
1684
                almost_full=Q(booked_places_count__gte=0.9 * F('places')),
1685
            )
1686

  
1687
        response = {
1688
            'err': 0,
1689
            'booking_count': len(bookings),
1690
            'waiting_list_events': [get_event_detail(request, x, agenda=agenda) for x in waiting_list_events],
1691
        }
1692
        return Response(response)
1693

  
1694

  
1695
events_fillslots = EventsFillslots.as_view()
1696

  
1697

  
1643 1698
class BookingSerializer(serializers.ModelSerializer):
1644 1699
    user_absence_reason = serializers.CharField(required=False, allow_blank=True, allow_null=True)
1645 1700

  
tests/api/test_fillslot.py
2224 2224
    resp = app.post_json('/api/agenda/%s/recurring_fillslots/' % agenda.slug, params=params)
2225 2225
    assert resp.json['booking_count'] == 5
2226 2226
    assert events.filter(waiting_list_count=2).count() == 5
2227

  
2228

  
2229
def test_api_events_fillslots(app, user, freezer):
2230
    freezer.move_to('2021-09-06 12:00')
2231
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2232
    event = Event.objects.create(
2233
        label='Event',
2234
        start_datetime=now() + datetime.timedelta(days=1),
2235
        places=2,
2236
        waiting_list_places=1,
2237
        agenda=agenda,
2238
    )
2239
    second_event = Event.objects.create(
2240
        label='Event 2', start_datetime=now() + datetime.timedelta(days=2), places=2, agenda=agenda
2241
    )
2242

  
2243
    app.authorization = ('Basic', ('john.doe', 'password'))
2244
    fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug
2245
    params = {'user_external_id': 'user_id', 'slots': 'event,event-2'}
2246
    resp = app.post_json(fillslots_url, params=params)
2247
    assert resp.json['booking_count'] == 2
2248
    assert len(resp.json['waiting_list_events']) == 0
2249

  
2250
    events = Event.annotate_queryset(Event.objects.all())
2251
    assert events.filter(booked_places_count=1).count() == 2
2252

  
2253
    resp = app.post_json(fillslots_url, params=params)
2254
    assert resp.json['booking_count'] == 2
2255
    assert len(resp.json['waiting_list_events']) == 0
2256

  
2257
    resp = app.post_json(fillslots_url, params=params)
2258
    assert resp.json['err'] == 1
2259
    assert resp.json['err_desc'] == 'some events are full: Event 2'
2260

  
2261
    params['slots'] = 'event'
2262
    resp = app.post_json(fillslots_url, params=params)
2263
    assert resp.json['booking_count'] == 1
2264
    assert resp.json['waiting_list_events'][0]['slug'] == event.slug
2265
    assert Booking.objects.filter(in_waiting_list=True, event=event).count() == 1
2227
-