api: add support for subscriptions in recurring fillslots (#58446)

This commit is contained in:
Valentin Deniaud 2021-12-01 11:42:29 +01:00
parent 270daa2202
commit 5269bc60c1
2 changed files with 128 additions and 17 deletions

View File

@ -674,14 +674,6 @@ def get_start_and_end_datetime_from_request(request):
return serializer.validated_data.get('date_start'), serializer.validated_data.get('date_end')
def get_agendas_from_request(request):
serializer = serializers.AgendaSlugsSerializer(data=request.query_params)
if not serializer.is_valid():
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
return serializer.validated_data.get('agendas')
def make_booking(event, payload, extra_data, primary_booking=None, in_waiting_list=False, color=None):
return Booking(
event_id=event.pk,
@ -1595,25 +1587,41 @@ class RecurringFillslots(APIView):
if not start_datetime or start_datetime < now():
start_datetime = now()
agenda_slugs = get_agendas_from_request(request)
agendas = get_objects_from_slugs(agenda_slugs, qs=Agenda.objects.filter(kind='events'))
serializer = serializers.AgendaOrSubscribedSlugsSerializer(
data=request.query_params, context={'user_external_id': request.data.get('user_external_id')}
)
if not serializer.is_valid():
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
data = serializer.validated_data
context = {'allowed_agenda_slugs': agenda_slugs, 'agendas': Agenda.prefetch_recurring_events(agendas)}
context = {
'allowed_agenda_slugs': data['agenda_slugs'],
'agendas': Agenda.prefetch_recurring_events(data['agendas']),
}
serializer = self.serializer_class(data=request.data, partial=True, context=context)
if not serializer.is_valid():
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
payload = serializer.validated_data
user_external_id = payload['user_external_id']
agendas = Agenda.prefetch_events_and_exceptions(agendas, user_external_id=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():
event_filter |= Q(
agenda__slug=agenda_slug,
primary_event__slug=event_slug,
start_datetime__week_day__in=days,
)
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)
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)

View File

@ -2360,6 +2360,109 @@ def test_recurring_events_api_fillslots_change_bookings(app, user, freezer):
assert Booking.objects.filter(user_external_id='user_id', event=normal_event).count() == 1
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_recurring_events_api_fillslots_subscribed(app, user):
category = Category.objects.create(label='Category A')
first_agenda = Agenda.objects.create(label='First agenda', kind='events', category=category)
Desk.objects.create(agenda=first_agenda, slug='_exceptions_holder')
category = Category.objects.create(label='Category B')
second_agenda = Agenda.objects.create(label='Second agenda', kind='events', category=category)
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
event = Event.objects.create(
slug='event',
start_datetime=now(),
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
places=2,
waiting_list_places=1,
agenda=first_agenda,
recurrence_end_date=now() + datetime.timedelta(days=364),
)
event.create_all_recurrences()
sunday_event = Event.objects.create(
slug='sunday-event',
start_datetime=now(),
recurrence_days=[6],
places=2,
waiting_list_places=1,
agenda=second_agenda,
recurrence_end_date=now() + datetime.timedelta(days=364),
)
sunday_event.create_all_recurrences()
Subscription.objects.create(
agenda=first_agenda,
user_external_id='xxx',
date_start=now() + datetime.timedelta(days=16), # Wednesday 22/09
date_end=now() + datetime.timedelta(days=44), # Wednesday 20/10
)
app.authorization = ('Basic', ('john.doe', 'password'))
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)
assert resp.json['booking_count'] == 8
assert Booking.objects.count() == 8
assert Booking.objects.filter(event__primary_event=event).count() == 8
assert Booking.objects.first().event.start_datetime.strftime('%d/%m') == '23/09'
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
)
# 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
)
# update bookings
Subscription.objects.create(
agenda=second_agenda,
user_external_id='xxx',
date_start=now() + datetime.timedelta(days=100), # Wednesday 15/12
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)
assert resp.json['booking_count'] == 11
assert resp.json['cancelled_booking_count'] == 8
assert Booking.objects.count() == 11
booked_events_first_agenda = Event.objects.filter(primary_event=event, booking__isnull=False)
assert [
x.strftime('%d/%m/%Y') for x in booked_events_first_agenda.values_list('start_datetime', flat=True)
] == ['28/09/2021', '05/10/2021', '12/10/2021', '19/10/2021']
booked_events_second_agenda = Event.objects.filter(primary_event=sunday_event, booking__isnull=False)
assert [
x.strftime('%d/%m/%Y') for x in booked_events_second_agenda.values_list('start_datetime', flat=True)
] == ['19/12/2021', '26/12/2021', '02/01/2022', '09/01/2022', '16/01/2022', '23/01/2022', '30/01/2022']
# other user
Subscription.objects.create(
agenda=second_agenda,
user_external_id='yyy',
date_start=now(),
date_end=now() + datetime.timedelta(days=10),
)
# disjoint subscription
Subscription.objects.create(
agenda=second_agenda,
user_external_id='yyy',
date_start=now() + datetime.timedelta(days=60),
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)
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')
assert [
x.strftime('%d/%m/%Y') for x in booked_events_user_yyy.values_list('start_datetime', flat=True)
] == ['12/09/2021', '07/11/2021', '14/11/2021']
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_recurring_events_api_fillslots_multiple_agendas(app, user):
agenda = Agenda.objects.create(label='First Agenda', kind='events')