api: exclude slots already booked by user - events (#51341)
This commit is contained in:
parent
ccd27f7619
commit
e15439bd3c
|
@ -513,6 +513,7 @@ class Agenda(models.Model):
|
|||
include_full=True,
|
||||
min_start=None,
|
||||
max_start=None,
|
||||
excluded_user_external_id=None,
|
||||
):
|
||||
assert self.kind == 'events'
|
||||
|
||||
|
@ -556,6 +557,9 @@ class Agenda(models.Model):
|
|||
else:
|
||||
entries = entries.filter(start_datetime__lt=max_start)
|
||||
|
||||
if excluded_user_external_id and not prefetched_queryset:
|
||||
entries = Event.annotate_queryset_for_user(entries, excluded_user_external_id)
|
||||
|
||||
if annotate_queryset and not prefetched_queryset:
|
||||
entries = Event.annotate_queryset(entries)
|
||||
|
||||
|
@ -1153,6 +1157,33 @@ class Event(models.Model):
|
|||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def annotate_queryset_for_user(qs, excluded_user_external_id):
|
||||
if django.VERSION < (2, 0):
|
||||
return qs.annotate(
|
||||
user_places_count=Count(
|
||||
Case(
|
||||
When(
|
||||
booking__cancellation_datetime__isnull=True,
|
||||
booking__in_waiting_list=False,
|
||||
booking__user_external_id=excluded_user_external_id,
|
||||
then='booking',
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
else:
|
||||
return qs.annotate(
|
||||
user_places_count=Count(
|
||||
'booking',
|
||||
filter=Q(
|
||||
booking__cancellation_datetime__isnull=True,
|
||||
booking__in_waiting_list=False,
|
||||
booking__user_external_id=excluded_user_external_id,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def booked_places(self):
|
||||
if hasattr(self, 'booked_places_count'):
|
||||
|
|
|
@ -352,6 +352,7 @@ def get_event_detail(request, event, agenda=None, min_places=1):
|
|||
event.label,
|
||||
date_format(localtime(event.start_datetime), 'DATETIME_FORMAT'),
|
||||
)
|
||||
full = bool(event.remaining_places < min_places and event.remaining_waiting_list_places < min_places)
|
||||
return {
|
||||
'id': event.slug,
|
||||
'slug': event.slug, # kept for compatibility
|
||||
|
@ -360,9 +361,7 @@ def get_event_detail(request, event, agenda=None, min_places=1):
|
|||
'description': event.description,
|
||||
'pricing': event.pricing,
|
||||
'url': event.url,
|
||||
'disabled': bool(
|
||||
event.remaining_places < min_places and event.remaining_waiting_list_places < min_places
|
||||
),
|
||||
'disabled': full or getattr(event, 'user_places_count', 0) > 0,
|
||||
'api': {
|
||||
'bookings_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
|
@ -565,7 +564,14 @@ class Datetimes(APIView):
|
|||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
entries = agenda.get_open_events(annotate_queryset=True, min_start=date_start, max_start=date_end)
|
||||
user_external_id = request.GET.get('exclude_user_external_id') or None
|
||||
|
||||
entries = agenda.get_open_events(
|
||||
annotate_queryset=True,
|
||||
min_start=date_start,
|
||||
max_start=date_end,
|
||||
excluded_user_external_id=user_external_id,
|
||||
)
|
||||
|
||||
response = {
|
||||
'data': [get_event_detail(request, x, agenda=agenda, min_places=min_places) for x in entries],
|
||||
|
|
|
@ -520,6 +520,71 @@ def test_datetime_api_min_places(app):
|
|||
assert resp.json['err'] == 1
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2021-02-23')
|
||||
def test_datetimes_api_exclude_slots(app):
|
||||
agenda = Agenda.objects.create(
|
||||
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
|
||||
)
|
||||
event = Event.objects.create(
|
||||
slug='event-slug',
|
||||
start_datetime=localtime().replace(hour=10, minute=0),
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
Booking.objects.create(event=event, user_external_id='42')
|
||||
cancelled = Booking.objects.create(event=event, user_external_id='35')
|
||||
cancelled.cancel()
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '35'})
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
|
||||
event.delete()
|
||||
|
||||
# recurrent event
|
||||
event = Event.objects.create(
|
||||
slug='recurrent',
|
||||
start_datetime=localtime().replace(hour=12, minute=0),
|
||||
repeat='weekly',
|
||||
places=2,
|
||||
agenda=agenda,
|
||||
)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['id'] == 'recurrent:2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['id'] == 'recurrent:2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
|
||||
first_recurrence = event.get_or_create_event_recurrence(event.start_datetime)
|
||||
Booking.objects.create(event=first_recurrence, user_external_id='42')
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
|
||||
Booking.objects.create(event=first_recurrence)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is True
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is True
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
|
||||
|
||||
def test_datetimes_api_meetings_agenda(app, meetings_agenda):
|
||||
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (meeting_type.agenda.slug, meeting_type.slug)
|
||||
|
|
Loading…
Reference in New Issue