agendas: add date filter params on datetimes api (#48078)

This commit is contained in:
Emmanuel Cazenave 2020-11-02 13:55:48 +01:00
parent 7f826697dc
commit 6d0e8a57ad
2 changed files with 131 additions and 12 deletions

View File

@ -64,25 +64,34 @@ def format_response_datetime(dt):
return localtime(dt).strftime('%Y-%m-%d %H:%M:%S')
def get_min_datetime(agenda):
def get_min_datetime(agenda, start_datetime=None):
if agenda.minimal_booking_delay is None:
return None
return start_datetime
min_datetime = now() + datetime.timedelta(days=agenda.minimal_booking_delay)
return min_datetime.replace(hour=0, minute=0, second=0, microsecond=0)
min_datetime = min_datetime.replace(hour=0, minute=0, second=0, microsecond=0)
if start_datetime is None:
return min_datetime
return max(min_datetime, start_datetime)
def get_max_datetime(agenda):
def get_max_datetime(agenda, end_datetime=None):
if agenda.maximal_booking_delay is None:
return None
return end_datetime
max_datetime = now() + datetime.timedelta(days=agenda.maximal_booking_delay)
max_datetime = max_datetime.replace(hour=0, minute=0, second=0, microsecond=0)
return max_datetime
if end_datetime is None:
return max_datetime
return min(max_datetime, end_datetime)
TimeSlot = collections.namedtuple('TimeSlot', ['start_datetime', 'end_datetime', 'full', 'desk'])
def get_all_slots(base_agenda, meeting_type, resources=None, unique=False):
def get_all_slots(
base_agenda, meeting_type, resources=None, unique=False, start_datetime=None, end_datetime=None
):
'''Get all occupation state of all possible slots for the given agenda (of
its real agendas for a virtual agenda) and the given meeting_type.
@ -107,8 +116,8 @@ def get_all_slots(base_agenda, meeting_type, resources=None, unique=False):
# that the base_meeting_duration for the virtual agenda is always the same
# as the base meeting duration of each real agenda.
base_meeting_duration = base_agenda.get_base_meeting_duration()
base_min_datetime = get_min_datetime(base_agenda)
base_max_datetime = get_max_datetime(base_agenda)
base_min_datetime = get_min_datetime(base_agenda, start_datetime)
base_max_datetime = get_max_datetime(base_agenda, end_datetime)
meeting_duration = meeting_type.duration
meeting_duration_td = datetime.timedelta(minutes=meeting_duration)
@ -121,8 +130,8 @@ def get_all_slots(base_agenda, meeting_type, resources=None, unique=False):
agenda_ids_by_min_max_datetimes = collections.defaultdict(set)
agenda_id_min_max_datetime = {}
for agenda in agendas:
used_min_datetime = base_min_datetime or get_min_datetime(agenda)
used_max_datetime = base_max_datetime or get_max_datetime(agenda)
used_min_datetime = base_min_datetime or get_min_datetime(agenda, start_datetime)
used_max_datetime = base_max_datetime or get_max_datetime(agenda, end_datetime)
agenda_ids_by_min_max_datetimes[(used_min_datetime, used_max_datetime)].add(agenda.id)
agenda_id_min_max_datetime[agenda.id] = (used_min_datetime, used_max_datetime)
@ -519,6 +528,32 @@ class MeetingDatetimes(APIView):
except APIError as e:
return e.to_response()
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 as e:
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 as e:
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,
)
# Generate an unique slot for each possible meeting [start_datetime,
# end_datetime] range.
# First use get_all_slots() to get each possible meeting by desk and
@ -532,7 +567,16 @@ class MeetingDatetimes(APIView):
# The generator also remove slots starting before the current time.
def unique_slots():
last_slot = None
all_slots = list(get_all_slots(agenda, meeting_type, resources=resources, unique=True))
all_slots = list(
get_all_slots(
agenda,
meeting_type,
resources=resources,
unique=True,
start_datetime=start_datetime,
end_datetime=end_datetime,
)
)
for slot in sorted(all_slots, key=lambda slot: slot[:3]):
if slot.start_datetime < now_datetime:
continue

View File

@ -4504,3 +4504,78 @@ def test_datetimes_maximal_booking_delay(app, user):
resp = app.get(api_url)
data = resp.json['data']
assert len(data) == 4
def test_meetings_and_virtual_datetimes_date_filter(app):
agenda_foo = Agenda.objects.create(
label=u'Agenda Foo', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=7
)
meeting_type = MeetingType.objects.create(agenda=agenda_foo, label='Meeting Type', duration=30)
desk_foo = Desk.objects.create(agenda=agenda_foo, label='Desk 1')
weekday1 = (localtime(now())).weekday() + 1 % 7
weekday2 = (localtime(now())).weekday() + 2 % 7
weekday3 = (localtime(now())).weekday() + 3 % 7
weekday4 = (localtime(now())).weekday() + 4 % 7
weekday5 = (localtime(now())).weekday() + 5 % 7
weekday6 = (localtime(now())).weekday() + 6 % 7
for weekday in (weekday1, weekday2, weekday3, weekday4, weekday5, weekday6):
TimePeriod.objects.create(
weekday=weekday, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=desk_foo,
)
virtual_agenda = Agenda.objects.create(
label=u'Agenda Virtual', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=7
)
VirtualMember.objects.create(virtual_agenda=virtual_agenda, real_agenda=agenda_foo)
# 4 slots each day * 6 days
foo_api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (agenda_foo.slug, meeting_type.slug)
resp = app.get(foo_api_url)
assert len(resp.json['data']) == 24
virtual_api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (virtual_agenda.slug, meeting_type.slug)
resp = app.get(virtual_api_url)
assert len(resp.json['data']) == 24
# exclude weekday1 through date_start, 4 slots each day * 5 days
params = {'date_start': (localtime(now()) + datetime.timedelta(days=2)).date().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
agenda_foo.save()
resp = app.get(foo_api_url, params=params)
assert len(resp.json['data']) == 16
# also on virtual agenda
virtual_agenda.minimal_booking_delay = 3
virtual_agenda.save()
resp = app.get(virtual_api_url, params=params)
assert len(resp.json['data']) == 16
# reset
agenda_foo.minimal_booking_delay = 1
virtual_agenda.minimal_booking_delay = 1
agenda_foo.save()
virtual_agenda.save()
# exclude weekday6 through date_end, 4 slots each day * 5 days
params = {'date_end': (localtime(now()) + datetime.timedelta(days=6)).date().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
# maximal_booking_delay (which exclude weekday5 and weekday6 ) takes precedence
# 4 slots each day * 4 days
agenda_foo.maximal_booking_delay = 5
agenda_foo.save()
resp = app.get(foo_api_url, params=params)
assert len(resp.json['data']) == 16
# also on virtual agenda
virtual_agenda.maximal_booking_delay = 5
virtual_agenda.save()
resp = app.get(virtual_api_url, params=params)
assert len(resp.json['data']) == 16