api: add action parameter to recurring fillslots endpoint (#60255)

This commit is contained in:
Valentin Deniaud 2022-01-06 10:45:34 +01:00
parent 29413e3a35
commit ad019b0956
3 changed files with 73 additions and 47 deletions

View File

@ -244,6 +244,10 @@ class AgendaOrSubscribedSlugsSerializer(AgendaOrSubscribedSlugsMixin, serializer
pass
class RecurringFillslotsQueryStringSerializer(AgendaOrSubscribedSlugsSerializer):
action = serializers.ChoiceField(required=True, choices=['update'])
class RecurringEventsListSerializer(AgendaOrSubscribedSlugsSerializer):
sort = serializers.ChoiceField(required=False, choices=['day'])

View File

@ -1605,7 +1605,7 @@ class RecurringFillslots(APIView):
if not start_datetime or start_datetime < now():
start_datetime = now()
serializer = serializers.AgendaOrSubscribedSlugsSerializer(
serializer = serializers.RecurringFillslotsQueryStringSerializer(
data=request.query_params, context={'user_external_id': request.data.get('user_external_id')}
)
if not serializer.is_valid():
@ -1623,38 +1623,12 @@ class RecurringFillslots(APIView):
user_external_id = payload['user_external_id']
agendas = Agenda.prefetch_events_and_exceptions(data['agendas'], user_external_id=user_external_id)
event_filter = Q()
for agenda_slug, days_by_event in payload['slots'].items():
for event_slug, days in days_by_event.items():
lookups = {
'agenda__slug': agenda_slug,
'primary_event__slug': event_slug,
'start_datetime__week_day__in': days,
}
if 'subscribed' in request.query_params:
lookups.update(
{
'agenda__subscriptions__user_external_id': user_external_id,
'agenda__subscriptions__date_start__lt': F('start_datetime'),
'agenda__subscriptions__date_end__gt': F('start_datetime'),
}
)
event_filter |= Q(**lookups)
if data['action'] == 'update':
events_to_book = self.get_event_recurrences(
payload['slots'], start_datetime, end_datetime, user_external_id
)
events_to_unbook = self.get_events_to_unbook(agendas, events_to_book)
events_to_book = Event.objects.filter(event_filter) if event_filter else Event.objects.none()
events_to_book = events_to_book.filter(start_datetime__gte=start_datetime, cancelled=False)
if end_datetime:
events_to_book = events_to_book.filter(start_datetime__lte=end_datetime)
events_to_book_ids = set(events_to_book.values_list('pk', flat=True))
events_to_unbook = [
e.pk
for agenda in agendas
for e in agenda.prefetched_events
if (e.user_places_count or e.user_waiting_places_count)
and e.primary_event_id
and e.pk not in events_to_book_ids
]
events_to_book = events_to_book.exclude(booking__user_external_id=user_external_id)
full_events = list(events_to_book.filter(full=True))
@ -1688,6 +1662,44 @@ class RecurringFillslots(APIView):
]
return Response(response)
def get_event_recurrences(self, slots, start_datetime, end_datetime, user_external_id):
event_filter = Q()
for agenda_slug, days_by_event in slots.items():
for event_slug, days in days_by_event.items():
lookups = {
'agenda__slug': agenda_slug,
'primary_event__slug': event_slug,
'start_datetime__week_day__in': days,
}
if 'subscribed' in self.request.query_params:
lookups.update(
{
'agenda__subscriptions__user_external_id': user_external_id,
'agenda__subscriptions__date_start__lt': F('start_datetime'),
'agenda__subscriptions__date_end__gt': F('start_datetime'),
}
)
event_filter |= Q(**lookups)
events = Event.objects.filter(event_filter) if event_filter else Event.objects.none()
events = events.filter(start_datetime__gte=start_datetime, cancelled=False)
if end_datetime:
events = events.filter(start_datetime__lte=end_datetime)
return events
def get_events_to_unbook(self, agendas, events_to_book):
events_to_book_ids = set(events_to_book.values_list('pk', flat=True))
events_to_unbook = [
e.pk
for agenda in agendas
for e in agenda.prefetched_events
if (e.user_places_count or e.user_waiting_places_count)
and e.primary_event_id
and e.pk not in events_to_book_ids
]
return events_to_unbook
recurring_fillslots = RecurringFillslots.as_view()

View File

@ -2175,7 +2175,7 @@ def test_recurring_events_api_fillslots(app, user, freezer):
assert len(resp.json['data']) == 5
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=update' % agenda.slug
params = {'user_external_id': 'user_id'}
# Book Monday and Thursday of first event and Sunday of second event
params['slots'] = 'foo-bar@event:0,foo-bar@event:3,foo-bar@sunday-event:6'
@ -2254,6 +2254,17 @@ def test_recurring_events_api_fillslots(app, user, freezer):
assert resp.json['err'] == 1
assert resp.json['errors']['slots'] == ['Invalid format for slot foo-bar']
missing_action_url = '/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug
resp = app.post_json(missing_action_url, params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['action'] == ['This field is required.']
resp = app.post_json(missing_action_url + '&action=invalid', params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['action'] == ['"invalid" is not a valid choice.']
def test_recurring_events_api_fillslots_waiting_list(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
@ -2279,7 +2290,9 @@ def test_recurring_events_api_fillslots_waiting_list(app, user, freezer):
# check that new bookings are put in waiting list despite free slots on main list
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event:0'}
resp = app.post_json('/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug, params=params)
resp = app.post_json(
'/api/agendas/recurring-events/fillslots/?agendas=%s&action=update' % agenda.slug, params=params
)
assert resp.json['booking_count'] == 5
assert events.filter(booked_waiting_list_places=2).count() == 5
@ -2300,7 +2313,7 @@ def test_recurring_events_api_fillslots_change_bookings(app, user, freezer):
event.create_all_recurrences()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=update' % agenda.slug
params = {'user_external_id': 'user_id'}
# Book Monday and Thursday
params['slots'] = 'foo-bar@event:0,foo-bar@event:3'
@ -2417,10 +2430,11 @@ def test_recurring_events_api_fillslots_subscribed(app, user):
)
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?action=update&subscribed=%s'
params = {'user_external_id': 'xxx'}
# book Monday and Thursday of first event, in subscription range
params['slots'] = 'first-agenda@event:0,first-agenda@event:3'
resp = app.post_json('/api/agendas/recurring-events/fillslots/?subscribed=category-a', params=params)
resp = app.post_json(fillslots_url % 'category-a', params=params)
assert resp.json['booking_count'] == 8
assert Booking.objects.count() == 8
assert Booking.objects.filter(event__primary_event=event).count() == 8
@ -2428,15 +2442,11 @@ def test_recurring_events_api_fillslots_subscribed(app, user):
assert Booking.objects.last().event.start_datetime.strftime('%d/%m') == '18/10'
# wrong category
resp = app.post_json(
'/api/agendas/recurring-events/fillslots/?subscribed=category-b', params=params, status=400
)
resp = app.post_json(fillslots_url % 'category-b', params=params, status=400)
# not subscribed category
params['slots'] = 'second-agenda@sunday-event:6'
resp = app.post_json(
'/api/agendas/recurring-events/fillslots/?subscribed=category-b', params=params, status=400
)
resp = app.post_json(fillslots_url % 'category-b', params=params, status=400)
# update bookings
Subscription.objects.create(
@ -2446,7 +2456,7 @@ def test_recurring_events_api_fillslots_subscribed(app, user):
date_end=now() + datetime.timedelta(days=150), # Thursday 03/02
)
params['slots'] = 'first-agenda@event:1,second-agenda@sunday-event:6'
resp = app.post_json('/api/agendas/recurring-events/fillslots/?subscribed=all', params=params)
resp = app.post_json(fillslots_url % 'all', params=params)
assert resp.json['booking_count'] == 11
assert resp.json['cancelled_booking_count'] == 8
assert Booking.objects.count() == 11
@ -2474,7 +2484,7 @@ def test_recurring_events_api_fillslots_subscribed(app, user):
date_end=now() + datetime.timedelta(days=70),
)
params = {'user_external_id': 'yyy', 'slots': 'second-agenda@sunday-event:6'}
resp = app.post_json('/api/agendas/recurring-events/fillslots/?subscribed=category-b', params=params)
resp = app.post_json(fillslots_url % 'category-b', params=params)
assert resp.json['booking_count'] == 3
assert Booking.objects.count() == 14
booked_events_user_yyy = Event.objects.filter(primary_event=sunday_event, booking__user_external_id='yyy')
@ -2517,7 +2527,7 @@ def test_recurring_events_api_fillslots_multiple_agendas(app, user):
assert len(resp.json['data']) == 6
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s'
fillslots_url = '/api/agendas/recurring-events/fillslots/?action=update&agendas=%s'
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@a:0,first-agenda@a:5,second-agenda@c:3'}
resp = app.post_json(fillslots_url % 'first-agenda,second-agenda', params=params)
assert resp.json['booking_count'] == 13
@ -2557,13 +2567,13 @@ def test_recurring_events_api_fillslots_multiple_agendas_queries(app, user):
event.create_all_recurrences()
agenda_slugs = ','.join(str(i) for i in range(20))
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda_slugs)
resp = app.get('/api/agendas/recurring-events/?action=update&agendas=%s' % agenda_slugs)
events_to_book = [x['id'] for x in resp.json['data']]
app.authorization = ('Basic', ('john.doe', 'password'))
with CaptureQueriesContext(connection) as ctx:
resp = app.post_json(
'/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda_slugs,
'/api/agendas/recurring-events/fillslots/?action=update&agendas=%s' % agenda_slugs,
params={'slots': events_to_book, 'user_external_id': 'user'},
)
assert resp.json['booking_count'] == 180