agendas: change max_booking_datetime implementation (#76303)

When a minimal_booking_time is defined max_booking_datetime's time is always midnight.
The switch max_booking_datetime += 1 occurs at minimal_booking_time.

When a minimal_booking_time is None, booking_datetime's time is the current time.
This commit is contained in:
Emmanuel Cazenave 2023-04-06 16:24:21 +02:00
parent 3855cfccb6
commit c866e7e4a4
4 changed files with 302 additions and 29 deletions

View File

@ -659,12 +659,14 @@ class Agenda(models.Model):
# reference is now, in local timezone
t = localtime(now())
# add delay
t += datetime.timedelta(days=self.maximal_booking_delay)
maximal_booking_delay = self.maximal_booking_delay
if self.minimal_booking_time is None or t.time() < self.minimal_booking_time:
maximal_booking_delay -= 1
t += datetime.timedelta(days=maximal_booking_delay)
# replace time if needed
if self.minimal_booking_time:
t = datetime.datetime.combine(t.date(), self.minimal_booking_time, tzinfo=t.tzinfo)
t = t.replace(hour=0, minute=0, second=0, microsecond=0)
# t could not exist, recompute it as an existing datetime by converting to UTC then to localtime
return localtime(t.astimezone(utc))

View File

@ -1399,3 +1399,159 @@ def test_events_datetimes_min_booking_datetime_with_minimal_booking_time(app):
resp = app.get(api_url)
assert resp.json['data'][0]['datetime'] == '2023-04-03 09:00:00'
assert resp.json['data'][1]['datetime'] == '2023-04-03 11:00:00'
def test_events_datetimes_max_booking_datetime_with_minimal_booking_time(app, freezer):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=3
)
Event.objects.create(
slug='event-slug-0',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-1',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-2',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-3',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-4',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-5',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=11)),
places=5,
agenda=agenda,
)
# last slots visible are the one on J + maximal_booking_delay (3) -1
freezer.move_to('2023-04-03T00:00:00+02:00')
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# # move to noon, no changes
freezer.move_to('2023-04-03T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# set a minimal minimal_booking_time earlier than current time, no changes
agenda.minimal_booking_time = datetime.time(10, 0, 0)
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# set a minimal minimal_booking_time later than current time, slots of 2023-04-05 disappear
agenda.minimal_booking_time = datetime.time(14, 0, 0)
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-04 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-04 11:00:00'
# move to a time superior to minimal_booking_time (14:00), slots of 2023-04-05 re-appear
freezer.move_to('2023-04-03T15:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# move to the day after, prior to minimal_booking_time (14:00), no changes
freezer.move_to('2023-04-04T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# move to the day after, after minimal_booking_time (14:00), new slots available
freezer.move_to('2023-04-04T15:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-06 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-06 11:00:00'
def test_events_datetimes_max_booking_datetime_with_minimal_booking_time_to_none(app, freezer):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=3
)
Event.objects.create(
slug='event-slug-0',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-1',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=4, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-2',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-3',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=5, hour=11)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-4',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=9)),
places=5,
agenda=agenda,
)
Event.objects.create(
slug='event-slug-5',
start_datetime=make_aware(datetime.datetime(year=2023, month=4, day=6, hour=11)),
places=5,
agenda=agenda,
)
# last slots visible are the one on J + maximal_booking_delay (3) -1
freezer.move_to('2023-04-03T00:00:00+02:00')
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# set a minimal minimal_booking_time to None, 2023-04-05 disappear
# because current time is 00:00 and slots starts later
agenda.minimal_booking_time = None
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-04 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-04 11:00:00'
# move a few hours later, juste after the first slot time of a day
# a new slot becomes available
freezer.move_to('2023-04-03T09:01:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-1]['datetime'] == '2023-04-05 09:00:00'
# at 12:00, every slots are available
freezer.move_to('2023-04-03T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'

View File

@ -2692,3 +2692,118 @@ def test_datetimes_api_meetings_min_booking_datetime_with_minimal_booking_time(a
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][0]['datetime'] == '2023-04-03 09:00:00'
def test_datetimes_api_meetings_max_booking_datetime_with_minimal_booking_time(app, freezer):
agenda = Agenda.objects.create(
label='Agenda', kind='meetings', minimal_booking_delay=0, maximal_booking_delay=3
)
desk = Desk.objects.create(agenda=agenda, slug='desk')
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo', duration=30)
for weekday in [0, 1, 2, 3, 4, 5]:
TimePeriod.objects.create(
weekday=weekday,
start_time=datetime.time(9, 0),
end_time=datetime.time(10, 00),
desk=desk,
)
# last slots visible are the one on J + maximal_booking_delay (3) -1
freezer.move_to('2023-04-03T00:00:00+02:00') # 2023-04-03 is a monday
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meeting_type.slug)
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 09:30:00'
# move to noon, no changes
freezer.move_to('2023-04-03T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 09:30:00'
# set a minimal minimal_booking_time earlier than current time, no changes
agenda.minimal_booking_time = datetime.time(10, 0, 0)
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 09:30:00'
# set a minimal minimal_booking_time later than current time, slots of 2023-04-05 disappear
agenda.minimal_booking_time = datetime.time(14, 0, 0)
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-04 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-04 09:30:00'
# move to a time superior to minimal_booking_time (14:00), slots of 2023-04-05 re-appear
freezer.move_to('2023-04-03T15:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 09:30:00'
# move to the day after, prior to minimal_booking_time (14:00), no changes
freezer.move_to('2023-04-04T12:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 09:30:00'
# move to the day after, after minimal_booking_time (14:00), new slots available
freezer.move_to('2023-04-04T15:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-06 09:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-06 09:30:00'
def test_datetimes_api_meetings_max_booking_datetime_with_minimal_booking_time_to_none(app, freezer):
agenda = Agenda.objects.create(
label='Agenda', kind='meetings', minimal_booking_delay=0, maximal_booking_delay=3
)
desk = Desk.objects.create(agenda=agenda, slug='desk')
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo', duration=30)
for weekday in [0, 1, 2, 3, 4, 5]:
TimePeriod.objects.create(
weekday=weekday,
start_time=datetime.time(11, 00),
end_time=datetime.time(14, 00),
desk=desk,
)
# last slots visible are the one on J + maximal_booking_delay (3) -1
freezer.move_to('2023-04-03T00:00:00+02:00') # 2023-04-03 is a monday
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meeting_type.slug)
resp = app.get(api_url)
assert resp.json['data'][-6]['datetime'] == '2023-04-05 11:00:00'
assert resp.json['data'][-5]['datetime'] == '2023-04-05 11:30:00'
assert resp.json['data'][-4]['datetime'] == '2023-04-05 12:00:00'
assert resp.json['data'][-3]['datetime'] == '2023-04-05 12:30:00'
assert resp.json['data'][-2]['datetime'] == '2023-04-05 13:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 13:30:00'
# set a minimal minimal_booking_time to None, 2023-04-05 disappear
# because current time is 00:00 and slots starts later
agenda.minimal_booking_time = None
agenda.save()
resp = app.get(api_url)
assert resp.json['data'][-1]['datetime'] == '2023-04-04 13:30:00'
# move a few hours later, juste after the first slot time of a day
# a new slot becomes available
freezer.move_to('2023-04-03T11:01:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:00:00'
# move juste after the second slot time, one more slot availalbe
freezer.move_to('2023-04-03T11:31:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-2]['datetime'] == '2023-04-05 11:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 11:30:00'
# at 15:00, every slots are available
freezer.move_to('2023-04-03T15:00:00+02:00')
resp = app.get(api_url)
assert resp.json['data'][-6]['datetime'] == '2023-04-05 11:00:00'
assert resp.json['data'][-5]['datetime'] == '2023-04-05 11:30:00'
assert resp.json['data'][-4]['datetime'] == '2023-04-05 12:00:00'
assert resp.json['data'][-3]['datetime'] == '2023-04-05 12:30:00'
assert resp.json['data'][-2]['datetime'] == '2023-04-05 13:00:00'
assert resp.json['data'][-1]['datetime'] == '2023-04-05 13:30:00'

View File

@ -368,22 +368,22 @@ def test_agenda_minimal_booking_delay_minimal_booking_time_at_8(freezer, current
@pytest.mark.parametrize(
'current_time,max_booking_datetime',
[
('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 13, 8)),
('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 22, 7)),
('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 12, 8)),
('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 21, 7)),
# summer DST change on sunday 28th
('2021-03-25T01:30:00+01:00', datetime.datetime(2021, 3, 29, 1, 30)),
('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 29, 2, 30)),
('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 29, 3, 30)),
('2021-03-28T01:30:00+01:00', datetime.datetime(2021, 4, 1, 1, 30)),
('2021-03-28T03:30:00+02:00', datetime.datetime(2021, 4, 1, 3, 30)),
('2021-03-25T01:30:00+01:00', datetime.datetime(2021, 3, 28, 1, 30)),
('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 28, 3, 30)),
('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 28, 3, 30)),
('2021-03-28T01:30:00+01:00', datetime.datetime(2021, 3, 31, 1, 30)),
('2021-03-28T03:30:00+02:00', datetime.datetime(2021, 3, 31, 3, 30)),
# winter DST change on sunday 31th
('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 2, 1, 30)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 2, 30)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 2, 30)),
('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 4, 1, 30)),
('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 4, 2, 30)),
('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 4, 2, 30)),
('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 4, 3, 30)),
('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 1, 1, 30)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 1, 2, 30)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 1, 2, 30)),
('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 3, 1, 30)),
('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 3, 2, 30)),
('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 3, 2, 30)),
('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 3, 3, 30)),
],
ids=delay_parameter_to_label,
)
@ -396,19 +396,19 @@ def test_agenda_maximal_booking_delay_no_minimal_booking_time(freezer, current_t
@pytest.mark.parametrize(
'current_time,max_booking_datetime',
[
('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 13, 8)),
('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 22, 8)),
('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 13, 0)),
('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 21, 0)),
# summer DST change on sunday
('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 29, 8)),
('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 29, 8)),
('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 28, 0)),
('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 28, 0)),
# winter DST change on sunday
('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 2, 8)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 8)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 8)),
('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 4, 8)),
('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 4, 8)),
('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 4, 8)),
('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 4, 8)),
('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 1, 0)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 1, 0)),
('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 1, 0)),
('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 3, 0)),
('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 3, 0)),
('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 3, 0)),
('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 3, 0)),
],
ids=delay_parameter_to_label,
)