api: allow changing multiple event bookings (#55368)

This commit is contained in:
Valentin Deniaud 2021-07-29 14:42:14 +02:00
parent 21fa37d374
commit 92d209ed11
2 changed files with 56 additions and 1 deletions

View File

@ -1646,6 +1646,8 @@ class EventsFillslots(APIView):
def post(self, request, agenda_identifier):
agenda = get_object_or_404(Agenda, slug=agenda_identifier, kind='events')
start_datetime, end_datetime = get_start_and_end_datetime_from_request(request)
serializer = self.serializer_class(data=request.data, partial=True)
if not serializer.is_valid():
raise APIError(
@ -1655,10 +1657,20 @@ class EventsFillslots(APIView):
http_status=status.HTTP_400_BAD_REQUEST,
)
payload = serializer.validated_data
user_external_id = payload['user_external_id']
events = get_events_from_slots(payload['slots'], request, agenda, payload)
events = Event.annotate_queryset(events)
already_booked_events = agenda.event_set.filter(booking__user_external_id=user_external_id)
if start_datetime:
already_booked_events = already_booked_events.filter(start_datetime__gte=start_datetime)
if end_datetime:
already_booked_events = already_booked_events.filter(start_datetime__lt=end_datetime)
events_to_unbook = list(already_booked_events.exclude(pk__in=events).values_list('pk', flat=True))
events = events.exclude(booking__user_external_id=user_external_id)
full_events = [str(event) for event in events.filter(full=True)]
if full_events:
raise APIError(
@ -1676,9 +1688,15 @@ class EventsFillslots(APIView):
extra_data = {k: v for k, v in request.data.items() if k not in payload}
bookings = [make_booking(event, payload, extra_data) for event in events]
events_to_update = Event.annotate_queryset(
agenda.event_set.filter(pk__in=events_to_unbook + [event.pk for event in events])
)
with transaction.atomic():
deleted_count = Booking.objects.filter(
user_external_id=user_external_id, event__in=events_to_unbook
).delete()[0]
Booking.objects.bulk_create(bookings)
events.update(
events_to_update.update(
full=Q(booked_places_count__gte=F('places'), waiting_list_places=0)
| Q(waiting_list_places__gt=0, waiting_list_count__gte=F('waiting_list_places')),
almost_full=Q(booked_places_count__gte=0.9 * F('places')),
@ -1688,6 +1706,7 @@ class EventsFillslots(APIView):
'err': 0,
'booking_count': len(bookings),
'waiting_list_events': [get_event_detail(request, x, agenda=agenda) for x in waiting_list_events],
'cancelled_booking_count': deleted_count,
}
return Response(response)

View File

@ -2239,6 +2239,9 @@ def test_api_events_fillslots(app, user):
second_event = Event.objects.create(
label='Event 2', start_datetime=now() + datetime.timedelta(days=2), places=2, agenda=agenda
)
third_event = Event.objects.create(
label='Event 3', start_datetime=now() + datetime.timedelta(days=3), places=2, agenda=agenda
)
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug
@ -2250,10 +2253,12 @@ def test_api_events_fillslots(app, user):
events = Event.annotate_queryset(Event.objects.all())
assert events.filter(booked_places_count=1).count() == 2
params['user_external_id'] = 'user_id_2'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert len(resp.json['waiting_list_events']) == 0
params['user_external_id'] = 'user_id_3'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'some events are full: Event 2'
@ -2264,6 +2269,37 @@ def test_api_events_fillslots(app, user):
assert resp.json['waiting_list_events'][0]['slug'] == event.slug
assert Booking.objects.filter(in_waiting_list=True, event=event).count() == 1
# change bookings
params = {'user_external_id': 'user_id', 'slots': 'event-2,event-3'}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 1
assert resp.json['cancelled_booking_count'] == 1
user_bookings = Booking.objects.filter(user_external_id='user_id')
assert {b.event.slug for b in user_bookings} == {'event-2', 'event-3'}
assert event.booking_set.count() == 2
assert second_event.booking_set.count() == 2
assert third_event.booking_set.count() == 1
# increase waiting_list_places to make "Event" bookable again
event.waiting_list_places = 2
event.save()
# specify time range so that "Event 3" is not cancelled
params['slots'] = 'event,event-2'
resp = app.post_json(fillslots_url + '?date_start=2021-09-06&date_end=2021-09-09', params=params)
assert resp.json['booking_count'] == 1
assert resp.json['cancelled_booking_count'] == 0
user_bookings = Booking.objects.filter(user_external_id='user_id')
assert {b.event.slug for b in user_bookings} == {'event', 'event-2', 'event-3'}
assert event.booking_set.count() == 3
assert second_event.booking_set.count() == 2
assert third_event.booking_set.count() == 1
# new event booking went in waiting list despite free slots on main list
assert Booking.objects.filter(in_waiting_list=True, event=event).count() == 2
resp = app.post('/api/agenda/foobar/events/fillslots/', status=404)
resp = app.post('/api/agenda/0/events/fillslots/', status=404)