api: use serializer for event datetimes api (#56083)
This commit is contained in:
parent
95e2618863
commit
8f127f3606
|
@ -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 Booking
|
||||
from chrono.agendas.models import AbsenceReason, Booking
|
||||
|
||||
|
||||
class StringOrListField(serializers.ListField):
|
||||
|
@ -91,3 +91,30 @@ class StatisticsFiltersSerializer(serializers.Serializer):
|
|||
category = serializers.SlugField(required=False, allow_blank=False, max_length=256)
|
||||
agenda = serializers.SlugField(required=False, allow_blank=False, max_length=256)
|
||||
group_by = serializers.SlugField(required=False, allow_blank=False, max_length=256)
|
||||
|
||||
|
||||
class DateRangeSerializer(serializers.Serializer):
|
||||
datetime_formats = ['%Y-%m-%d', '%Y-%m-%d %H:%M', 'iso-8601']
|
||||
|
||||
date_start = serializers.DateTimeField(required=False, input_formats=datetime_formats)
|
||||
date_end = serializers.DateTimeField(required=False, input_formats=datetime_formats)
|
||||
|
||||
|
||||
class DatetimesSerializer(DateRangeSerializer):
|
||||
min_places = serializers.IntegerField(min_value=1, default=1)
|
||||
user_external_id = serializers.CharField(required=False, max_length=250, allow_blank=True)
|
||||
exclude_user_external_id = serializers.CharField(required=False, max_length=250, allow_blank=True)
|
||||
events = serializers.CharField(required=False, max_length=32, allow_blank=True)
|
||||
hide_disabled = serializers.BooleanField(default=False)
|
||||
|
||||
def validate(self, attrs):
|
||||
super().validate(attrs)
|
||||
if (
|
||||
'user_external_id' in attrs
|
||||
and 'exclude_user_external_id' in attrs
|
||||
and attrs['user_external_id'] != attrs['exclude_user_external_id']
|
||||
):
|
||||
raise ValidationError(
|
||||
{'user_external_id': _('user_external_id and exclude_user_external_id have different values')}
|
||||
)
|
||||
return attrs
|
||||
|
|
|
@ -27,11 +27,10 @@ from django.http import Http404, HttpResponse
|
|||
from django.shortcuts import get_object_or_404
|
||||
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist
|
||||
from django.urls import reverse
|
||||
from django.utils.dateparse import parse_date, parse_datetime
|
||||
from django.utils.dates import WEEKDAYS
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import is_naive, localtime, make_aware, now
|
||||
from django.utils.timezone import localtime, make_aware, now
|
||||
from django.utils.translation import gettext_noop
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django_filters import rest_framework as filters
|
||||
|
@ -41,7 +40,6 @@ from rest_framework.generics import ListAPIView
|
|||
from rest_framework.views import APIView
|
||||
|
||||
from chrono.agendas.models import (
|
||||
AbsenceReason,
|
||||
Agenda,
|
||||
Booking,
|
||||
BookingColor,
|
||||
|
@ -611,42 +609,17 @@ def get_resources_from_request(request, agenda):
|
|||
return resources
|
||||
|
||||
|
||||
def parse_date_or_datetime(value):
|
||||
try:
|
||||
result = parse_datetime(value)
|
||||
if result:
|
||||
if is_naive(result):
|
||||
return make_aware(result)
|
||||
return result
|
||||
|
||||
result = parse_date(value)
|
||||
if result:
|
||||
return make_aware(datetime.datetime.combine(result, datetime.time(0, 0)))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_start_and_end_datetime_from_request(request):
|
||||
start_datetime, end_datetime = request.GET.get('date_start'), request.GET.get('date_end')
|
||||
if start_datetime:
|
||||
start_datetime = parse_date_or_datetime(start_datetime)
|
||||
if not start_datetime:
|
||||
raise APIError(
|
||||
_('date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'),
|
||||
err_class='date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM',
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
if end_datetime:
|
||||
end_datetime = parse_date_or_datetime(end_datetime)
|
||||
if not end_datetime:
|
||||
raise APIError(
|
||||
_('date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'),
|
||||
err_class='date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM',
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
return start_datetime, end_datetime
|
||||
serializer = serializers.DateRangeSerializer(data=request.query_params)
|
||||
if not serializer.is_valid():
|
||||
raise APIError(
|
||||
_('invalid payload'),
|
||||
err_class='invalid payload',
|
||||
errors=serializer.errors,
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
return serializer.validated_data.get('date_start'), serializer.validated_data.get('date_end')
|
||||
|
||||
|
||||
def make_booking(event, payload, extra_data, primary_booking=None, in_waiting_list=False, color=None):
|
||||
|
@ -771,6 +744,7 @@ agenda_detail = AgendaDetail.as_view()
|
|||
|
||||
class Datetimes(APIView):
|
||||
permission_classes = ()
|
||||
serializer_class = serializers.DatetimesSerializer
|
||||
|
||||
def get(self, request, agenda_identifier=None, format=None):
|
||||
try:
|
||||
|
@ -784,30 +758,18 @@ class Datetimes(APIView):
|
|||
if agenda.kind != 'events':
|
||||
raise Http404('agenda found, but it was not an events agenda')
|
||||
|
||||
try:
|
||||
min_places = int(request.GET.get('min_places', 1))
|
||||
except ValueError:
|
||||
serializer = self.serializer_class(data=request.query_params)
|
||||
if not serializer.is_valid():
|
||||
raise APIError(
|
||||
_('min_places must be a number'),
|
||||
err_class='min_places must be a number',
|
||||
_('invalid payload'),
|
||||
err_class='invalid payload',
|
||||
errors=serializer.errors,
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
payload = serializer.validated_data
|
||||
|
||||
start_datetime, end_datetime = get_start_and_end_datetime_from_request(request)
|
||||
booked_user_external_id = request.GET.get('user_external_id') or None
|
||||
excluded_user_external_id = request.GET.get('exclude_user_external_id') or None
|
||||
if (
|
||||
booked_user_external_id
|
||||
and excluded_user_external_id
|
||||
and booked_user_external_id != excluded_user_external_id
|
||||
):
|
||||
raise APIError(
|
||||
_('user_external_id and exclude_user_external_id have different values'),
|
||||
err_class='user_external_id and exclude_user_external_id have different values',
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
show_events_raw = request.GET.get('events')
|
||||
user_external_id = payload.get('user_external_id') or payload.get('exclude_user_external_id')
|
||||
show_events_raw = payload.get('events')
|
||||
show_events = show_events_raw or 'future'
|
||||
show_past = show_events in ['all', 'past']
|
||||
show_future = show_events in ['all', 'future']
|
||||
|
@ -816,20 +778,20 @@ class Datetimes(APIView):
|
|||
if show_past:
|
||||
entries += agenda.get_past_events(
|
||||
annotate_queryset=True,
|
||||
min_start=start_datetime,
|
||||
max_start=end_datetime,
|
||||
user_external_id=booked_user_external_id or excluded_user_external_id,
|
||||
min_start=payload.get('date_start'),
|
||||
max_start=payload.get('date_end'),
|
||||
user_external_id=user_external_id,
|
||||
)
|
||||
if show_future:
|
||||
entries += agenda.get_open_events(
|
||||
annotate_queryset=True,
|
||||
min_start=start_datetime,
|
||||
max_start=end_datetime,
|
||||
user_external_id=booked_user_external_id or excluded_user_external_id,
|
||||
min_start=payload.get('date_start'),
|
||||
max_start=payload.get('date_end'),
|
||||
user_external_id=user_external_id,
|
||||
)
|
||||
|
||||
if request.GET.get('hide_disabled'):
|
||||
entries = [e for e in entries if not is_event_disabled(e, min_places)]
|
||||
if payload['hide_disabled']:
|
||||
entries = [e for e in entries if not is_event_disabled(e, payload['min_places'])]
|
||||
|
||||
response = {
|
||||
'data': [
|
||||
|
@ -837,14 +799,14 @@ class Datetimes(APIView):
|
|||
request,
|
||||
x,
|
||||
agenda=agenda,
|
||||
min_places=min_places,
|
||||
booked_user_external_id=booked_user_external_id,
|
||||
min_places=payload['min_places'],
|
||||
booked_user_external_id=payload.get('user_external_id'),
|
||||
show_events=show_events_raw,
|
||||
)
|
||||
for x in entries
|
||||
],
|
||||
'meta': get_events_meta_detail(
|
||||
request, entries, agenda=agenda, min_places=min_places, show_events=show_events_raw
|
||||
request, entries, agenda=agenda, min_places=payload['min_places'], show_events=show_events_raw
|
||||
),
|
||||
}
|
||||
return Response(response)
|
||||
|
|
|
@ -171,10 +171,10 @@ def test_datetime_api_min_places(app):
|
|||
resp = app.get('/api/agenda/%s/datetimes/?min_places=5' % agenda.slug)
|
||||
assert resp.json['data'][0]['disabled']
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=wrong' % agenda.slug, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=' % agenda.slug)
|
||||
assert not resp.json['data'][0]['disabled']
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=' % agenda.slug, status=400)
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=wrong' % agenda.slug, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
|
||||
|
||||
|
@ -315,7 +315,10 @@ def test_datetimes_api_user_external_id(app):
|
|||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'user_external_id and exclude_user_external_id have different values'
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['user_external_id'] == [
|
||||
'user_external_id and exclude_user_external_id have different values'
|
||||
]
|
||||
|
||||
|
||||
def test_datetimes_api_hide_disabled(app):
|
||||
|
@ -396,15 +399,19 @@ def test_agenda_api_date_range(app):
|
|||
params = {'date_start': value}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_start'] == [
|
||||
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
|
||||
]
|
||||
|
||||
for value in ['foo', '2017-05-42']:
|
||||
params = {'date_end': value}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_end'] == [
|
||||
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
|
||||
]
|
||||
|
||||
params = {'date_start': base_datetime.date().isoformat()}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
|
@ -447,8 +454,10 @@ def test_agenda_api_date_range(app):
|
|||
params = {'date_start': '2017-05-21 foo'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_start'] == [
|
||||
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
|
||||
]
|
||||
|
||||
for start in ['2017-05-30 09:00', '2017-05-30 09:00:00']:
|
||||
params = {'date_start': start}
|
||||
|
@ -467,8 +476,10 @@ def test_agenda_api_date_range(app):
|
|||
params = {'date_end': '2017-06-01 foo'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_end'] == [
|
||||
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
|
||||
]
|
||||
|
||||
for end in ['2017-05-30 11:01', '2017-05-30 11:00:01']:
|
||||
params = {'date_end': end}
|
||||
|
|
|
@ -1917,15 +1917,19 @@ def test_meetings_and_virtual_datetimes_date_filter(app):
|
|||
params = {'date_start': value}
|
||||
resp = app.get(foo_api_url, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_start'] == [
|
||||
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
|
||||
]
|
||||
|
||||
for value in ['foo', '2017-05-42']:
|
||||
params = {'date_end': value}
|
||||
resp = app.get(foo_api_url, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_end'] == [
|
||||
'Datetime has wrong format. Use one of these formats instead: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'
|
||||
]
|
||||
|
||||
# exclude weekday1 through date_start, 4 slots each day * 5 days
|
||||
params = {'date_start': (localtime(now()) + datetime.timedelta(days=2)).date().isoformat()}
|
||||
|
|
Loading…
Reference in New Issue