api: allow getting all user bookings as ICS (#80685)
gitea/chrono/pipeline/head This commit looks good
Details
gitea/chrono/pipeline/head This commit looks good
Details
This commit is contained in:
parent
8b924ef670
commit
42cc548a33
|
@ -2852,9 +2852,7 @@ class Booking(models.Model):
|
|||
anonymization_datetime=now(),
|
||||
)
|
||||
|
||||
def get_ics(self, request=None):
|
||||
ics = vobject.iCalendar()
|
||||
ics.add('prodid').value = '-//Entr\'ouvert//NON SGML Publik'
|
||||
def get_vevent_ics(self, request=None):
|
||||
vevent = vobject.newFromBehavior('vevent')
|
||||
vevent.add('uid').value = '%s-%s-%s' % (
|
||||
self.event.start_datetime.isoformat(),
|
||||
|
@ -2882,6 +2880,12 @@ class Booking(models.Model):
|
|||
field_value = request and request.GET.get(field) or (self.extra_data or {}).get(field)
|
||||
if field_value:
|
||||
vevent.add(field).value = field_value
|
||||
return vevent
|
||||
|
||||
def get_ics(self, request=None):
|
||||
ics = vobject.iCalendar()
|
||||
ics.add('prodid').value = '-//Entr\'ouvert//NON SGML Publik'
|
||||
vevent = self.get_vevent_ics(request)
|
||||
ics.add(vevent)
|
||||
return ics.serialize()
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ urlpatterns = [
|
|||
name='api-agenda-subscription',
|
||||
),
|
||||
path('bookings/', views.bookings, name='api-bookings'),
|
||||
path('bookings/ics/', views.bookings_ics, name='api-bookings-ics'),
|
||||
path('booking/<int:booking_pk>/', views.booking, name='api-booking'),
|
||||
path('booking/<int:booking_pk>/cancel/', views.cancel_booking, name='api-cancel-booking'),
|
||||
path('booking/<int:booking_pk>/accept/', views.accept_booking, name='api-accept-booking'),
|
||||
|
|
|
@ -20,6 +20,7 @@ import datetime
|
|||
import json
|
||||
import uuid
|
||||
|
||||
import vobject
|
||||
from django.conf import settings
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import BooleanField, Count, ExpressionWrapper, F, Func, Prefetch, Q
|
||||
|
@ -2003,7 +2004,11 @@ class EventsFillslots(APIView):
|
|||
],
|
||||
'cancelled_booking_count': cancelled_count,
|
||||
'cancelled_events': cancelled_events,
|
||||
'bookings_ics_url': request.build_absolute_uri(reverse('api-bookings-ics'))
|
||||
+ '?user_external_id=%s' % user_external_id,
|
||||
}
|
||||
if not self.multiple_agendas:
|
||||
response['bookings_ics_url'] += '&agenda=%s' % self.agenda.slug
|
||||
return Response(response)
|
||||
|
||||
def get_events(self, request, payload, start_datetime, end_datetime):
|
||||
|
@ -2550,6 +2555,29 @@ class BookingsAPI(ListAPIView):
|
|||
bookings = BookingsAPI.as_view()
|
||||
|
||||
|
||||
class BookingsICS(BookingsAPI):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if not request.GET.get('user_external_id'):
|
||||
raise APIError(N_('missing param user_external_id'))
|
||||
|
||||
try:
|
||||
bookings = self.filter_queryset(self.get_queryset())
|
||||
except ValidationError as e:
|
||||
raise APIErrorBadRequest(N_('invalid payload'), errors=e.detail)
|
||||
|
||||
ics = vobject.iCalendar()
|
||||
ics.add('prodid').value = '-//Entr\'ouvert//NON SGML Publik'
|
||||
|
||||
for booking in bookings:
|
||||
vevent = booking.get_vevent_ics()
|
||||
ics.add(vevent)
|
||||
|
||||
return HttpResponse(ics.serialize(), content_type='text/calendar')
|
||||
|
||||
|
||||
bookings_ics = BookingsICS.as_view()
|
||||
|
||||
|
||||
class BookingAPI(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
serializer_class = serializers.BookingSerializer
|
||||
|
|
|
@ -48,6 +48,10 @@ def test_api_events_fillslots(app, user):
|
|||
assert len(resp.json['waiting_list_events']) == 0
|
||||
assert resp.json['cancelled_booking_count'] == 0
|
||||
assert len(resp.json['cancelled_events']) == 0
|
||||
assert (
|
||||
resp.json['bookings_ics_url']
|
||||
== 'http://testserver/api/bookings/ics/?user_external_id=user_id&agenda=foo-bar'
|
||||
)
|
||||
|
||||
events = Event.objects.all()
|
||||
assert events.filter(booked_places=1).count() == 2
|
||||
|
|
|
@ -178,6 +178,7 @@ def test_api_events_fillslots_multiple_agendas(app, user):
|
|||
)
|
||||
assert first_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
||||
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
||||
assert resp.json['bookings_ics_url'] == 'http://testserver/api/bookings/ics/?user_external_id=user_id'
|
||||
|
||||
# booking modification
|
||||
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@event'}
|
||||
|
|
|
@ -104,6 +104,40 @@ def test_booking_ics(app, some_data, meetings_agenda, user, settings, rf):
|
|||
assert 'DTEND:%sZ\r\n' % end in booking_ics
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2023-09-18 14:00')
|
||||
def test_bookings_ics(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(
|
||||
label='Event A', agenda=agenda, start_datetime=now() + datetime.timedelta(days=3), places=10
|
||||
)
|
||||
Booking.objects.create(event=event, user_external_id='enfant-1234')
|
||||
|
||||
agenda = Agenda.objects.create(label='Foo bar 2', kind='events')
|
||||
event = Event.objects.create(
|
||||
label='Event B', agenda=agenda, start_datetime=now() + datetime.timedelta(days=4), places=10
|
||||
)
|
||||
Booking.objects.create(event=event, user_external_id='enfant-1234')
|
||||
|
||||
resp = app.get('/api/bookings/ics/')
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'missing param user_external_id'
|
||||
|
||||
resp = app.get('/api/bookings/ics/', params={'user_external_id': 'enfant-1234'})
|
||||
assert 'BEGIN:VCALENDAR' in resp.text
|
||||
assert resp.text.count('UID') == 2
|
||||
assert 'DTSTART:20230921' in resp.text
|
||||
assert 'DTSTART:20230922' in resp.text
|
||||
|
||||
resp = app.get('/api/bookings/ics/', params={'user_external_id': 'enfant-1234', 'agenda': 'foo-bar'})
|
||||
assert resp.text.count('UID') == 1
|
||||
assert 'DTSTART:20230921' in resp.text
|
||||
assert 'DTSTART:20230922' not in resp.text
|
||||
|
||||
resp = app.get('/api/bookings/ics/', params={'user_external_id': 'enfant-1234', 'agenda': 'xxx'})
|
||||
assert 'BEGIN:VCALENDAR' in resp.text
|
||||
assert resp.text.count('UID') == 0
|
||||
|
||||
|
||||
def test_bookings_api(app, user):
|
||||
events_agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
events_event = Event.objects.create(
|
||||
|
|
Loading…
Reference in New Issue