api: add add-event endpoint (#47337)
This commit is contained in:
parent
06291d148f
commit
9bf248a095
|
@ -2,7 +2,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from chrono.agendas.models import AbsenceReason, Booking
|
||||
from chrono.agendas.models import AbsenceReason, Booking, Event
|
||||
|
||||
|
||||
class StringOrListField(serializers.ListField):
|
||||
|
@ -133,3 +133,26 @@ class MultipleAgendasDatetimesSerializer(DatetimesSerializer):
|
|||
agendas = CommaSeparatedStringField(
|
||||
required=True, child=serializers.SlugField(max_length=160, allow_blank=False)
|
||||
)
|
||||
|
||||
|
||||
class EventSerializer(serializers.ModelSerializer):
|
||||
recurrence_days = CommaSeparatedStringField(
|
||||
required=False, child=serializers.IntegerField(min_value=0, max_value=6)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
'start_datetime',
|
||||
'recurrence_days',
|
||||
'recurrence_week_interval',
|
||||
'recurrence_end_date',
|
||||
'duration',
|
||||
'publication_date',
|
||||
'places',
|
||||
'waiting_list_places',
|
||||
'label',
|
||||
'description',
|
||||
'pricing',
|
||||
'url',
|
||||
]
|
||||
|
|
|
@ -64,6 +64,11 @@ urlpatterns = [
|
|||
views.event_check,
|
||||
name='api-event-check',
|
||||
),
|
||||
url(
|
||||
r'^agenda/(?P<agenda_identifier>[\w-]+)/add-event/$',
|
||||
views.agenda_add_event,
|
||||
name='api-agenda-add-event',
|
||||
),
|
||||
url(
|
||||
r'^agenda/meetings/(?P<meeting_identifier>[\w-]+)/datetimes/$',
|
||||
views.meeting_datetimes,
|
||||
|
|
|
@ -440,7 +440,7 @@ def get_event_text(event, agenda, day=None):
|
|||
event.label,
|
||||
date_format(localtime(event.start_datetime), 'DATETIME_FORMAT'),
|
||||
)
|
||||
elif event.recurrence_days:
|
||||
elif day is not None:
|
||||
event_text = _('%(weekday)s: %(event)s') % {
|
||||
'weekday': WEEKDAYS[day].capitalize(),
|
||||
'event': event_text,
|
||||
|
@ -470,35 +470,48 @@ def get_event_detail(
|
|||
'pricing': event.pricing,
|
||||
'url': event.url,
|
||||
'duration': event.duration,
|
||||
'disabled': is_event_disabled(event, min_places=min_places, disable_booked=disable_booked),
|
||||
'api': {
|
||||
'bookings_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-event-bookings',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
'fillslot_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-fillslot',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
'status_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-event-status',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
'check_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-event-check',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
},
|
||||
'places': get_event_places(event),
|
||||
}
|
||||
if event.recurrence_days:
|
||||
details.update(
|
||||
{
|
||||
'recurrence_days': event.recurrence_days,
|
||||
'recurrence_week_interval': event.recurrence_week_interval,
|
||||
'recurrence_end_date': event.recurrence_end_date,
|
||||
}
|
||||
)
|
||||
else:
|
||||
details.update(
|
||||
{
|
||||
'disabled': is_event_disabled(event, min_places=min_places, disable_booked=disable_booked),
|
||||
'api': {
|
||||
'bookings_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-event-bookings',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
'fillslot_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-fillslot',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
'status_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-event-status',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
'check_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-event-check',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
},
|
||||
'places': get_event_places(event),
|
||||
}
|
||||
)
|
||||
if show_events is not None:
|
||||
details['api']['fillslot_url'] += '?events=%s' % show_events
|
||||
if booked_user_external_id:
|
||||
|
@ -2407,3 +2420,28 @@ class BookingsStatistics(APIView):
|
|||
|
||||
|
||||
bookings_statistics = BookingsStatistics.as_view()
|
||||
|
||||
|
||||
class AgendaAddEventView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
serializer_class = serializers.EventSerializer
|
||||
|
||||
def post(self, request, agenda_identifier):
|
||||
agenda = get_object_or_404(Agenda, slug=agenda_identifier, kind='events')
|
||||
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
raise APIError(
|
||||
_('invalid payload'),
|
||||
err_class='invalid payload',
|
||||
errors=serializer.errors,
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
payload = serializer.validated_data
|
||||
event = Event.objects.create(agenda=agenda, **payload)
|
||||
if event.recurrence_days and event.recurrence_end_date:
|
||||
event.create_all_recurrences()
|
||||
return Response({'err': 0, 'data': get_event_detail(request, event)})
|
||||
|
||||
|
||||
agenda_add_event = AgendaAddEventView.as_view()
|
||||
|
|
|
@ -158,3 +158,176 @@ def test_event_checked(app, user):
|
|||
agenda.kind = 'virtual'
|
||||
agenda.save()
|
||||
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug), status=404)
|
||||
|
||||
|
||||
def test_add_event(app, user):
|
||||
api_url = '/api/agenda/%s/add-event/' % ('999')
|
||||
|
||||
# no authentication
|
||||
resp = app.post(api_url, status=401)
|
||||
assert resp.json['detail'] == 'Authentication credentials were not provided.'
|
||||
|
||||
# wrong password
|
||||
app.authorization = ('Basic', ('john.doe', 'wrong'))
|
||||
resp = app.post(api_url, status=401)
|
||||
assert resp.json['detail'] == 'Invalid username/password.'
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
# missing agenda
|
||||
resp = app.post(api_url, status=404)
|
||||
assert resp.json['detail'] == 'Not found.'
|
||||
|
||||
# using meeting agenda
|
||||
meeting_agenda = Agenda(label='Foo bar Meeting', kind='meetings')
|
||||
meeting_agenda.save()
|
||||
api_url = '/api/agenda/%s/add-event/' % (meeting_agenda.slug)
|
||||
resp = app.post(api_url, status=404)
|
||||
assert resp.json['detail'] == 'Not found.'
|
||||
|
||||
agenda = Agenda(label='Foo bar')
|
||||
agenda.maximal_booking_delay = 0
|
||||
agenda.save()
|
||||
api_url = '/api/agenda/%s/add-event/' % (agenda.slug)
|
||||
|
||||
# missing fields
|
||||
resp = app.post(api_url, status=400)
|
||||
assert resp.json['err']
|
||||
assert resp.json['errors'] == {
|
||||
'start_datetime': ['This field is required.'],
|
||||
'places': ['This field is required.'],
|
||||
}
|
||||
|
||||
# add with errors in datetime parts
|
||||
params = {
|
||||
'start_datetime': '2021-11-15 minuit',
|
||||
'places': 10,
|
||||
}
|
||||
resp = app.post(api_url, params=params, status=400)
|
||||
assert resp.json['err']
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert 'Datetime has wrong format' in resp.json['errors']['start_datetime'][0]
|
||||
|
||||
# add an event
|
||||
params = {
|
||||
'start_datetime': '2021-11-15 15:38',
|
||||
'places': 10,
|
||||
}
|
||||
resp = app.post(api_url, params=params)
|
||||
assert not resp.json['err']
|
||||
assert resp.json['data']['id'] == 'foo-bar-event'
|
||||
assert {'api', 'disabled', 'places'}.issubset(resp.json['data'].keys())
|
||||
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.isdisjoint(
|
||||
resp.json['data'].keys()
|
||||
)
|
||||
event = Event.objects.filter(agenda=agenda).get(slug='foo-bar-event')
|
||||
assert str(event.start_datetime) == '2021-11-15 14:38:00+00:00'
|
||||
assert str(event.start_datetime.tzinfo) == 'UTC'
|
||||
assert event.places == 10
|
||||
assert event.publication_date is None
|
||||
|
||||
# add with almost all optional managed fields
|
||||
params = {
|
||||
'start_datetime': '2021-11-15 15:38',
|
||||
'duration': 42,
|
||||
'publication_date': '2021-09-20',
|
||||
'places': 11,
|
||||
'waiting_list_places': 3,
|
||||
'label': 'FOO camp',
|
||||
'description': 'An event',
|
||||
'pricing': 'free',
|
||||
'url': 'http://example.org/foo/bar/?',
|
||||
}
|
||||
resp = app.post(api_url, params=params)
|
||||
assert not resp.json['err']
|
||||
assert resp.json['data']['id'] == 'foo-camp'
|
||||
assert {'api', 'disabled', 'places'}.issubset(resp.json['data'].keys())
|
||||
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.isdisjoint(
|
||||
resp.json['data'].keys()
|
||||
)
|
||||
event = Event.objects.filter(agenda=agenda).get(slug='foo-camp')
|
||||
assert event.duration == 42
|
||||
assert event.waiting_list_places == 3
|
||||
assert event.label == 'FOO camp'
|
||||
assert event.description == 'An event'
|
||||
assert event.pricing == 'free'
|
||||
assert event.url == 'http://example.org/foo/bar/?'
|
||||
|
||||
# add with errors in recurrence_days list
|
||||
params = {
|
||||
'start_datetime': '2021-11-15 15:38',
|
||||
'places': 10,
|
||||
'recurrence_days': 'oups',
|
||||
}
|
||||
resp = app.post(api_url, params=params, status=400)
|
||||
assert resp.json['err']
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['recurrence_days']['0'][0] == 'A valid integer is required.'
|
||||
params = {
|
||||
'start_datetime': '2021-11-15 15:38',
|
||||
'places': 10,
|
||||
'recurrence_days': '7',
|
||||
}
|
||||
resp = app.post(api_url, params=params, status=400)
|
||||
assert resp.json['err']
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['recurrence_days']['0'][0] == 'Ensure this value is less than or equal to 6.'
|
||||
|
||||
# add a recurrent event
|
||||
params = {
|
||||
'start_datetime': '2021-11-15 15:38',
|
||||
'places': 12,
|
||||
'recurrence_days': '0,3,5',
|
||||
'recurrence_week_interval': '2',
|
||||
'description': 'A recurrent event',
|
||||
}
|
||||
assert Event.objects.filter(agenda=agenda).count() == 2
|
||||
resp = app.post(api_url, params=params)
|
||||
assert Event.objects.filter(agenda=agenda).count() == 3
|
||||
assert not resp.json['err']
|
||||
assert resp.json['data']['id'] == 'foo-bar-event-1'
|
||||
assert {'api', 'disabled', 'places'}.isdisjoint(resp.json['data'].keys())
|
||||
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.issubset(
|
||||
resp.json['data'].keys()
|
||||
)
|
||||
event = Event.objects.filter(agenda=agenda).get(slug='foo-bar-event-1')
|
||||
assert event.description == 'A recurrent event'
|
||||
assert event.recurrence_days == [0, 3, 5]
|
||||
assert event.recurrence_week_interval == 2
|
||||
assert event.recurrence_end_date is None
|
||||
|
||||
# add a recurrent event with end recurrence date creates 9 recurrences
|
||||
params = {
|
||||
'start_datetime': '2021-11-15 15:38',
|
||||
'places': 13,
|
||||
'recurrence_days': '0,3,5', # Monday, Tuesday, Saturday
|
||||
'recurrence_week_interval': '2',
|
||||
'recurrence_end_date': '2021-12-27',
|
||||
'description': 'A recurrent event having recurrences',
|
||||
}
|
||||
resp = app.post(api_url, params=params)
|
||||
assert not resp.json['err']
|
||||
assert resp.json['data']['id'] == 'foo-bar-event-2'
|
||||
assert {'api', 'disabled', 'places'}.isdisjoint(resp.json['data'].keys())
|
||||
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.issubset(
|
||||
resp.json['data'].keys()
|
||||
)
|
||||
event = Event.objects.filter(agenda=agenda).get(slug='foo-bar-event-2')
|
||||
assert Event.objects.filter(agenda=agenda).count() == 13
|
||||
assert event.description == 'A recurrent event having recurrences'
|
||||
assert event.recurrence_days == [0, 3, 5]
|
||||
assert event.recurrence_week_interval == 2
|
||||
assert event.recurrence_end_date == datetime.date(2021, 12, 27)
|
||||
assert sorted(
|
||||
str(x.start_datetime.date()) for x in Event.objects.all() if 'foo-bar-event-2--' in x.slug
|
||||
) == [
|
||||
'2021-11-15',
|
||||
'2021-11-18',
|
||||
'2021-11-20',
|
||||
'2021-11-29',
|
||||
'2021-12-02',
|
||||
'2021-12-04',
|
||||
'2021-12-13',
|
||||
'2021-12-16',
|
||||
'2021-12-18',
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue