api: add a meta dict on datetimes endpoints (#50278)
This commit is contained in:
parent
1ec10c4e46
commit
446ca922be
|
@ -749,8 +749,8 @@ class SharedTimePeriod(object):
|
|||
|
||||
When it crosses end_datetime it stops.
|
||||
|
||||
Generated start_datetime MUST be in the local timezone as the API
|
||||
needs it to generate stable ids.
|
||||
Generated start_datetime MUST be in the local timezone, and the local
|
||||
timezone must not change, as the API needs it to generate stable ids.
|
||||
"""
|
||||
meeting_duration = datetime.timedelta(minutes=meeting_duration)
|
||||
duration = datetime.timedelta(minutes=base_duration)
|
||||
|
|
|
@ -398,6 +398,24 @@ def get_event_detail(request, event, agenda=None):
|
|||
}
|
||||
|
||||
|
||||
def get_events_meta_detail(events):
|
||||
bookable_datetimes_number_total = 0
|
||||
bookable_datetimes_number_available = 0
|
||||
bookable_datetimes_first = None
|
||||
for event in events:
|
||||
bookable_datetimes_number_total += 1
|
||||
if not bool(event.full):
|
||||
bookable_datetimes_number_available += 1
|
||||
if not bookable_datetimes_first:
|
||||
bookable_datetimes_first = format_response_datetime(event.start_datetime)
|
||||
return {
|
||||
'no_bookable_datetimes': bool(bookable_datetimes_number_available == 0),
|
||||
'bookable_datetimes_number_total': bookable_datetimes_number_total,
|
||||
'bookable_datetimes_number_available': bookable_datetimes_number_available,
|
||||
'bookable_datetimes_first': bookable_datetimes_first,
|
||||
}
|
||||
|
||||
|
||||
def get_resources_from_request(request, agenda):
|
||||
if agenda.kind != 'meetings' or 'resources' not in request.GET:
|
||||
return []
|
||||
|
@ -502,7 +520,10 @@ class Datetimes(APIView):
|
|||
)
|
||||
)
|
||||
|
||||
response = {'data': [get_event_detail(request, x, agenda=agenda) for x in entries]}
|
||||
response = {
|
||||
'data': [get_event_detail(request, x, agenda=agenda) for x in entries],
|
||||
'meta': get_events_meta_detail(entries),
|
||||
}
|
||||
return Response(response)
|
||||
|
||||
|
||||
|
@ -603,21 +624,23 @@ class MeetingDatetimes(APIView):
|
|||
if resources:
|
||||
fillslot_url += '?resources=%s' % ','.join(r.slug for r in resources)
|
||||
|
||||
def make_id(start_datetime, meeting_type):
|
||||
"""Make virtual id for a slot, combining meeting_type.id and
|
||||
iso-format of date and time.
|
||||
!!! The datetime must always be in the local timezone and the local
|
||||
timezone must not change if we want the id to be stable.
|
||||
It MUST be a garanty of SharedTimePeriod.get_time_slots(),
|
||||
!!!
|
||||
"""
|
||||
return '%s:%s' % (
|
||||
meeting_type.slug,
|
||||
start_datetime.strftime('%Y-%m-%d-%H%M'),
|
||||
)
|
||||
bookable_datetimes_number_total = 0
|
||||
bookable_datetimes_number_available = 0
|
||||
bookable_datetimes_first = None
|
||||
data = []
|
||||
for slot in generator_of_unique_slots:
|
||||
bookable_datetimes_number_total += 1
|
||||
if not bool(slot.full):
|
||||
bookable_datetimes_number_available += 1
|
||||
if not bookable_datetimes_first:
|
||||
bookable_datetimes_first = format_response_datetime(slot.start_datetime)
|
||||
|
||||
response = {
|
||||
'data': [
|
||||
# Make virtual id for a slot, combining meeting_type.id and
|
||||
# iso-format of date and time.
|
||||
# (SharedTimePeriod.get_time_slots() generate datetime in fixed local timezone,
|
||||
# in order to make slot_id stable.)
|
||||
slot_id = '%s:%s' % (meeting_type.slug, slot.start_datetime.strftime('%Y-%m-%d-%H%M'))
|
||||
data.append(
|
||||
{
|
||||
'id': slot_id,
|
||||
'datetime': format_response_datetime(slot.start_datetime),
|
||||
|
@ -625,10 +648,16 @@ class MeetingDatetimes(APIView):
|
|||
'disabled': bool(slot.full),
|
||||
'api': {'fillslot_url': fillslot_url.replace(fake_event_identifier, slot_id)},
|
||||
}
|
||||
for slot in generator_of_unique_slots
|
||||
# we do not have the := operator, so we do that
|
||||
for slot_id in [make_id(slot.start_datetime, meeting_type)]
|
||||
]
|
||||
)
|
||||
|
||||
response = {
|
||||
'data': data,
|
||||
'meta': {
|
||||
'no_bookable_datetimes': bool(bookable_datetimes_number_available == 0),
|
||||
'bookable_datetimes_number_total': bookable_datetimes_number_total,
|
||||
'bookable_datetimes_number_available': bookable_datetimes_number_available,
|
||||
'bookable_datetimes_first': bookable_datetimes_first,
|
||||
},
|
||||
}
|
||||
return Response(response)
|
||||
|
||||
|
|
|
@ -4981,3 +4981,228 @@ def test_meetings_and_virtual_datetimes_date_filter(app):
|
|||
virtual_agenda.save()
|
||||
resp = app.get(virtual_api_url, params=params)
|
||||
assert len(resp.json['data']) == 16
|
||||
|
||||
|
||||
def test_datetimes_api_meta(app, freezer):
|
||||
# 2017-05-20 -> saturday
|
||||
freezer.move_to(make_aware(datetime.datetime(year=2017, month=5, day=20, hour=1, minute=12)))
|
||||
|
||||
agenda = Agenda.objects.create(label=u'Foo bar')
|
||||
first_date = localtime(now()).replace(hour=17, minute=0, second=0, microsecond=0)
|
||||
first_date += datetime.timedelta(days=1)
|
||||
for i in range(3):
|
||||
event = Event(start_datetime=first_date + datetime.timedelta(days=i), places=20, agenda=agenda)
|
||||
event.save()
|
||||
# a date in the past
|
||||
event = Event(start_datetime=first_date - datetime.timedelta(days=10), places=10, agenda=agenda)
|
||||
event.save()
|
||||
|
||||
events = Event.objects.filter(agenda_id=agenda.id).exclude(start_datetime__lt=now())
|
||||
assert len(events) == 3
|
||||
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 3,
|
||||
'bookable_datetimes_first': '2017-05-21 17:00:00',
|
||||
}
|
||||
|
||||
def simulate_booking(event, nb_places):
|
||||
for i in range(nb_places):
|
||||
Booking(event=event).save()
|
||||
|
||||
simulate_booking(events[0], 10)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 3,
|
||||
'bookable_datetimes_first': '2017-05-21 17:00:00',
|
||||
}
|
||||
|
||||
simulate_booking(events[0], 10)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 2,
|
||||
'bookable_datetimes_first': '2017-05-22 17:00:00',
|
||||
}
|
||||
|
||||
simulate_booking(events[1], 20)
|
||||
simulate_booking(events[2], 20)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': True,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 0,
|
||||
'bookable_datetimes_first': None,
|
||||
}
|
||||
|
||||
|
||||
def test_datetimes_api_meetings_agenda_meta(app, freezer):
|
||||
# 2017-05-20 -> saturday
|
||||
freezer.move_to(make_aware(datetime.datetime(year=2017, month=5, day=20, hour=1, minute=12)))
|
||||
meetings_agenda = Agenda.objects.create(
|
||||
label=u'Foo bar Meeting', kind='meetings', maximal_booking_delay=3
|
||||
)
|
||||
meeting_type = MeetingType(agenda=meetings_agenda, label='Blah', duration=30)
|
||||
meeting_type.save()
|
||||
|
||||
desk1 = Desk.objects.create(agenda=meetings_agenda, label='Desk 1')
|
||||
desk2 = Desk.objects.create(agenda=meetings_agenda, label='Desk 2')
|
||||
test_1st_weekday = (localtime(now()).weekday() + 2) % 7
|
||||
for desk in desk1, desk2:
|
||||
TimePeriod(
|
||||
weekday=test_1st_weekday,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=desk,
|
||||
).save()
|
||||
|
||||
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (meeting_type.agenda.slug, meeting_type.slug)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][2]['disabled'] is False
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 4,
|
||||
'bookable_datetimes_first': '2017-05-22 10:00:00',
|
||||
}
|
||||
|
||||
def simulate_booking(slot, desk):
|
||||
dt = datetime.datetime.strptime(slot['id'].split(':')[1], '%Y-%m-%d-%H%M')
|
||||
ev = Event(
|
||||
agenda=meetings_agenda,
|
||||
meeting_type=meeting_type,
|
||||
places=1,
|
||||
full=False,
|
||||
start_datetime=make_aware(dt),
|
||||
desk=desk,
|
||||
)
|
||||
ev.save()
|
||||
booking = Booking(event=ev)
|
||||
booking.save()
|
||||
|
||||
simulate_booking(resp.json['data'][0], desk1)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 4,
|
||||
'bookable_datetimes_first': '2017-05-22 10:00:00',
|
||||
}
|
||||
|
||||
simulate_booking(resp.json['data'][0], desk2)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 3,
|
||||
'bookable_datetimes_first': '2017-05-22 10:30:00',
|
||||
}
|
||||
|
||||
for idx in range(1, 4):
|
||||
simulate_booking(resp.json['data'][idx], desk1)
|
||||
simulate_booking(resp.json['data'][idx], desk2)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': True,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 0,
|
||||
'bookable_datetimes_first': None,
|
||||
}
|
||||
|
||||
|
||||
def test_datetimes_api_virtual_meetings_agenda_meta(app, freezer):
|
||||
# 2017-05-20 -> saturday
|
||||
freezer.move_to(make_aware(datetime.datetime(year=2017, month=5, day=20, hour=1, minute=12)))
|
||||
meetings_agenda1 = Agenda.objects.create(label=u'Foo Meeting', kind='meetings', maximal_booking_delay=3)
|
||||
meetings_agenda2 = Agenda.objects.create(label=u'Bar Meeting', kind='meetings', maximal_booking_delay=3)
|
||||
virtual_agenda = Agenda.objects.create(label=u'Agenda Virtual', kind='virtual', maximal_booking_delay=3)
|
||||
VirtualMember.objects.create(virtual_agenda=virtual_agenda, real_agenda=meetings_agenda1)
|
||||
VirtualMember.objects.create(virtual_agenda=virtual_agenda, real_agenda=meetings_agenda2)
|
||||
desk1 = Desk.objects.create(agenda=meetings_agenda1, label='Desk 1')
|
||||
desk2 = Desk.objects.create(agenda=meetings_agenda2, label='Desk 2')
|
||||
|
||||
test_1st_weekday = (localtime(now()).weekday() + 2) % 7
|
||||
for agenda, desk in zip((meetings_agenda1, meetings_agenda2), (desk1, desk2)):
|
||||
meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah', duration=30)
|
||||
TimePeriod(
|
||||
weekday=test_1st_weekday,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=desk,
|
||||
).save()
|
||||
|
||||
virt_meeting_type = virtual_agenda.iter_meetingtypes()[0]
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (virtual_agenda.slug, virt_meeting_type.slug)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][2]['disabled'] is False
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 4,
|
||||
'bookable_datetimes_first': '2017-05-22 10:00:00',
|
||||
}
|
||||
|
||||
def simulate_booking(slot, agenda, desk):
|
||||
dt = datetime.datetime.strptime(slot['id'].split(':')[1], '%Y-%m-%d-%H%M')
|
||||
ev = Event(
|
||||
agenda=agenda,
|
||||
meeting_type=meeting_type,
|
||||
places=1,
|
||||
full=False,
|
||||
start_datetime=make_aware(dt),
|
||||
desk=desk,
|
||||
)
|
||||
ev.save()
|
||||
booking = Booking(event=ev)
|
||||
booking.save()
|
||||
|
||||
simulate_booking(resp.json['data'][0], meetings_agenda1, desk1)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 4,
|
||||
'bookable_datetimes_first': '2017-05-22 10:00:00',
|
||||
}
|
||||
|
||||
simulate_booking(resp.json['data'][0], meetings_agenda2, desk2)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 3,
|
||||
'bookable_datetimes_first': '2017-05-22 10:30:00',
|
||||
}
|
||||
|
||||
for idx in range(1, 4):
|
||||
simulate_booking(resp.json['data'][idx], meetings_agenda1, desk1)
|
||||
simulate_booking(resp.json['data'][idx], meetings_agenda2, desk2)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': True,
|
||||
'bookable_datetimes_number_total': 4,
|
||||
'bookable_datetimes_number_available': 0,
|
||||
'bookable_datetimes_first': None,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue