api: use date or datetimes for start/end param in datetimes (#51986)
This commit is contained in:
parent
bc2e447465
commit
07f2af930b
|
@ -24,10 +24,10 @@ from django.db.models import Prefetch, Q
|
|||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.utils.dateparse import parse_date
|
||||
from django.utils.dateparse import parse_date, parse_datetime
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import now, make_aware, localtime
|
||||
from django.utils.timezone import now, is_naive, make_aware, localtime
|
||||
from django.utils.translation import gettext_noop
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
@ -486,6 +486,44 @@ 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
|
||||
|
||||
|
||||
class Agendas(APIView):
|
||||
permission_classes = ()
|
||||
|
||||
|
@ -580,34 +618,13 @@ class Datetimes(APIView):
|
|||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
date_start, date_end = request.GET.get('date_start'), request.GET.get('date_end')
|
||||
if date_start:
|
||||
try:
|
||||
date_start = make_aware(
|
||||
datetime.datetime.combine(parse_date(date_start), datetime.time(0, 0))
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
raise APIError(
|
||||
_('date_start format must be YYYY-MM-DD'),
|
||||
err_class='date_start format must be YYYY-MM-DD',
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
if date_end:
|
||||
try:
|
||||
date_end = make_aware(datetime.datetime.combine(parse_date(date_end), datetime.time(0, 0)))
|
||||
except (TypeError, ValueError):
|
||||
raise APIError(
|
||||
_('date_end format must be YYYY-MM-DD'),
|
||||
err_class='date_end format must be YYYY-MM-DD',
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
start_datetime, end_datetime = get_start_and_end_datetime_from_request(request)
|
||||
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,
|
||||
min_start=start_datetime,
|
||||
max_start=end_datetime,
|
||||
excluded_user_external_id=user_external_id,
|
||||
)
|
||||
|
||||
|
@ -640,33 +657,7 @@ class MeetingDatetimes(APIView):
|
|||
now_datetime = now()
|
||||
|
||||
resources = get_resources_from_request(request, agenda)
|
||||
|
||||
start_datetime = None
|
||||
if 'date_start' in request.GET:
|
||||
try:
|
||||
start_datetime = make_aware(
|
||||
datetime.datetime.combine(parse_date(request.GET['date_start']), datetime.time(0, 0))
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
raise APIError(
|
||||
_('date_start format must be YYYY-MM-DD'),
|
||||
err_class='date_start format must be YYYY-MM-DD',
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
end_datetime = None
|
||||
if 'date_end' in request.GET:
|
||||
try:
|
||||
end_datetime = make_aware(
|
||||
datetime.datetime.combine(parse_date(request.GET['date_end']), datetime.time(0, 0))
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
raise APIError(
|
||||
_('date_end format must be YYYY-MM-DD'),
|
||||
err_class='date_end format must be YYYY-MM-DD',
|
||||
http_status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
start_datetime, end_datetime = get_start_and_end_datetime_from_request(request)
|
||||
user_external_id = request.GET.get('exclude_user_external_id') or None
|
||||
|
||||
# Generate an unique slot for each possible meeting [start_datetime,
|
||||
|
|
|
@ -4,7 +4,6 @@ import datetime
|
|||
import mock
|
||||
import urllib.parse as urlparse
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import connection
|
||||
|
@ -26,7 +25,6 @@ from chrono.agendas.models import (
|
|||
VirtualMember,
|
||||
BookingColor,
|
||||
)
|
||||
import chrono.api.views
|
||||
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
@ -3672,11 +3670,18 @@ def test_agenda_detail_api(app):
|
|||
app.get('/api/agenda/whatever/', status=404)
|
||||
|
||||
|
||||
def test_agenda_api_date_range(app, some_data):
|
||||
@pytest.mark.freeze_time('2017-05-20')
|
||||
def test_agenda_api_date_range(app):
|
||||
# test range limitation
|
||||
agenda2 = Agenda.objects.get(slug='foo-bar-2')
|
||||
base_date = agenda2.event_set.last().start_datetime.date()
|
||||
base_date = base_date + datetime.timedelta(days=1)
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
|
||||
first_datetime = localtime(now()).replace(hour=20, minute=0, second=0, microsecond=0)
|
||||
first_datetime += datetime.timedelta(days=1)
|
||||
for i in range(2):
|
||||
event = Event.objects.create(
|
||||
start_datetime=first_datetime + datetime.timedelta(days=i), places=20, agenda=agenda
|
||||
)
|
||||
base_datetime = agenda.event_set.last().start_datetime
|
||||
base_datetime = base_datetime + datetime.timedelta(days=1)
|
||||
|
||||
for idx in range(7, 10):
|
||||
if idx == 7:
|
||||
|
@ -3685,62 +3690,103 @@ def test_agenda_api_date_range(app, some_data):
|
|||
day_events = ['13:00', '14:00']
|
||||
else:
|
||||
day_events = ['8:00']
|
||||
day = base_date + datetime.timedelta(days=idx)
|
||||
day = base_datetime.date() + datetime.timedelta(days=idx)
|
||||
for event in day_events:
|
||||
event_dt = datetime.datetime.combine(day, datetime.datetime.strptime(event, '%H:%M').time())
|
||||
Event.objects.create(agenda=agenda2, start_datetime=make_aware(event_dt), places=2)
|
||||
Event.objects.create(agenda=agenda, start_datetime=make_aware(event_dt), places=2)
|
||||
|
||||
for value in ['foo', '2017-05-42']:
|
||||
params = {'date_start': value}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda2.slug, params=params, status=400)
|
||||
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'
|
||||
assert resp.json['err_desc'] == 'date_start format must be YYYY-MM-DD'
|
||||
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'
|
||||
|
||||
for value in ['foo', '2017-05-42']:
|
||||
params = {'date_end': value}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda2.slug, params=params, status=400)
|
||||
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'
|
||||
assert resp.json['err_desc'] == 'date_end format must be YYYY-MM-DD'
|
||||
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'
|
||||
|
||||
params = {'date_start': base_date.isoformat()}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda2.slug, params=params)
|
||||
params = {'date_start': base_datetime.date().isoformat()}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 6
|
||||
date_end = base_date + datetime.timedelta(days=7)
|
||||
params = {'date_end': date_end.isoformat()}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda2.slug, params=params)
|
||||
date_endtime = base_datetime + datetime.timedelta(days=7)
|
||||
params = {'date_end': date_endtime.date().isoformat()}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-21 20:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-22 20:00:00'
|
||||
|
||||
params = {
|
||||
'date_start': base_date + datetime.timedelta(days=8),
|
||||
'date_end': base_date + datetime.timedelta(days=10),
|
||||
'date_start': base_datetime.date() + datetime.timedelta(days=8),
|
||||
'date_end': base_datetime.date() + datetime.timedelta(days=10),
|
||||
}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda2.slug, params=params)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-31 13:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-06-01 08:00:00'
|
||||
|
||||
# with minimal booking delay changed
|
||||
agenda2.minimal_booking_delay = 3
|
||||
agenda2.save()
|
||||
agenda.minimal_booking_delay = 3
|
||||
agenda.save()
|
||||
params = {'date_start': '2017-05-21'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda2.slug, params=params)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 6
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-06-01 08:00:00'
|
||||
|
||||
# with maximal booking delay changed
|
||||
agenda2.maximal_booking_delay = 11
|
||||
agenda2.save()
|
||||
agenda.maximal_booking_delay = 11
|
||||
agenda.save()
|
||||
params = {'date_end': '2017-06-01'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda2.slug, params=params)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
# with time
|
||||
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'
|
||||
|
||||
for start in ['2017-05-30 09:00', '2017-05-30 09:00:00']:
|
||||
params = {'date_start': start}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
for start in ['2017-05-30 09:01', '2017-05-30 09:00:01', '2017-05-30 09:00:01+02:00']:
|
||||
params = {'date_start': start}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 10:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
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'
|
||||
|
||||
for end in ['2017-05-30 11:01', '2017-05-30 11:00:01']:
|
||||
params = {'date_end': end}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
for end in ['2017-05-30 11:00', '2017-05-30 11:00:00', '2017-05-30 11:00:00+02:00']:
|
||||
params = {'date_end': end}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 10:00:00'
|
||||
|
||||
|
||||
def test_agenda_meeting_api_multiple_desk(app, meetings_agenda, user):
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
@ -5648,15 +5694,15 @@ 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'
|
||||
assert resp.json['err_desc'] == 'date_start format must be YYYY-MM-DD'
|
||||
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'
|
||||
|
||||
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'
|
||||
assert resp.json['err_desc'] == 'date_end format must be YYYY-MM-DD'
|
||||
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'
|
||||
|
||||
# exclude weekday1 through date_start, 4 slots each day * 5 days
|
||||
params = {'date_start': (localtime(now()) + datetime.timedelta(days=2)).date().isoformat()}
|
||||
|
@ -5665,6 +5711,12 @@ def test_meetings_and_virtual_datetimes_date_filter(app):
|
|||
resp = app.get(virtual_api_url, params=params)
|
||||
assert len(resp.json['data']) == 20
|
||||
|
||||
params = {'date_start': (localtime(now()) + datetime.timedelta(days=2)).isoformat()}
|
||||
resp = app.get(foo_api_url, params=params)
|
||||
assert len(resp.json['data']) == 20
|
||||
resp = app.get(virtual_api_url, params=params)
|
||||
assert len(resp.json['data']) == 20
|
||||
|
||||
# minimal_booking_delay (which exclude weekday1 and wekkday2 ) takes precedence
|
||||
# 4 slots each day * 4 days
|
||||
agenda_foo.minimal_booking_delay = 3
|
||||
|
@ -5690,6 +5742,12 @@ def test_meetings_and_virtual_datetimes_date_filter(app):
|
|||
resp = app.get(virtual_api_url, params=params)
|
||||
assert len(resp.json['data']) == 20
|
||||
|
||||
params = {'date_end': (localtime(now()) + datetime.timedelta(days=6)).replace(hour=12).isoformat()}
|
||||
resp = app.get(foo_api_url, params=params)
|
||||
assert len(resp.json['data']) == 24
|
||||
resp = app.get(virtual_api_url, params=params)
|
||||
assert len(resp.json['data']) == 24
|
||||
|
||||
# maximal_booking_delay (which exclude weekday5 and weekday6 ) takes precedence
|
||||
# 4 slots each day * 4 days
|
||||
agenda_foo.maximal_booking_delay = 5
|
||||
|
|
Loading…
Reference in New Issue