agenda: factorize code to compute min/max_booking_time (#52227)

This commit is contained in:
Benjamin Dauvergne 2021-03-19 10:38:18 +01:00
parent b7f1a2c5ec
commit 918d2c2339
3 changed files with 65 additions and 23 deletions

View File

@ -505,6 +505,42 @@ class Agenda(models.Model):
for weektime_interval in IntervalSet.simple(*time_period_interval) - closed_hours_by_days:
yield SharedTimePeriod.from_weektime_interval(weektime_interval, desks=desks)
@property
def max_booking_datetime(self):
if self.maximal_booking_delay is None:
return None
# compute middle of today with localtime
# 28 Mar 2021 12:00 +01:00
t = localtime(now()).replace(hour=12, minute=0)
# advance of self.maximal_booking_delay - 1 days
# 28 Mar 2021 12:00 +01:00 == 28 Mars 2021 13:00 +02:00 as DST happend on 28 Mar 2021.
t += datetime.timedelta(days=self.maximal_booking_delay)
# move to midnight of the day before, DST happen between 2h/3h so it
# always exists because +/- timedelta does not move the timezone, only
# localtime() does it.
# 27 Mar 2021 12:00 +01:00 == 28 Mars 2021 01:00 +02:00
t = t.replace(hour=0, minute=0, second=0, microsecond=0)
return localtime(t)
@property
def min_booking_datetime(self):
if self.minimal_booking_delay is None:
return None
# compute middle of today with localtime
# 28 Mar 2021 12:00 +01:00
t = localtime(now()).replace(hour=12, minute=0)
# advance of self.maximal_booking_delay - 1 days
# 28 Mar 2021 12:00 +01:00 == 28 Mars 2021 13:00 +02:00 as DST happend on 28 Mar 2021.
t += datetime.timedelta(days=self.minimal_booking_delay)
# move to midnight of the day before, DST happen between 2h/3h so it
# always exists because +/- timedelta does not move the timezone, only
# localtime() does it.
# 27 Mar 2021 12:00 +01:00 == 28 Mars 2021 01:00 +02:00
t = t.replace(hour=0, minute=0, second=0, microsecond=0)
return localtime(t)
def get_open_events(
self,
prefetched_queryset=False,
@ -533,10 +569,7 @@ class Agenda(models.Model):
entries = entries.filter(Q(full=False) | Q(primary_event__isnull=False))
if self.minimal_booking_delay:
min_booking_time = localtime(now() + datetime.timedelta(days=self.minimal_booking_delay)).replace(
hour=0, minute=0
)
min_start = max(min_booking_time, min_start) if min_start else min_booking_time
min_start = max(self.min_booking_datetime, min_start) if min_start else self.min_booking_datetime
if min_start:
if prefetched_queryset:
@ -545,10 +578,7 @@ class Agenda(models.Model):
entries = entries.filter(start_datetime__gte=min_start)
if self.maximal_booking_delay:
max_booking_time = localtime(now() + datetime.timedelta(days=self.maximal_booking_delay)).replace(
hour=0, minute=0
)
max_start = min(max_booking_time, max_start) if max_start else max_booking_time
max_start = min(self.max_booking_datetime, max_start) if max_start else self.max_booking_datetime
if max_start:
if prefetched_queryset:
@ -1111,16 +1141,12 @@ class Event(models.Model):
def in_bookable_period(self):
if self.publication_date and localtime(now()).date() < self.publication_date:
return False
today = localtime(now()).date()
event_day = localtime(self.start_datetime).date()
days_to_event = event_day - today
if self.agenda.maximal_booking_delay:
if days_to_event >= datetime.timedelta(days=self.agenda.maximal_booking_delay):
return False
if self.agenda.maximal_booking_delay and self.start_datetime > self.agenda.max_booking_datetime:
return False
if self.recurrence_rule is not None:
# bookable recurrences probably exist
return True
if days_to_event < datetime.timedelta(days=self.agenda.minimal_booking_delay):
if self.agenda.minimal_booking_delay and self.start_datetime < self.agenda.min_booking_datetime:
return False
if self.start_datetime < now():
# past the event date, we may want in the future to allow for some

View File

@ -50,22 +50,18 @@ def get_min_datetime(agenda, start_datetime=None):
if agenda.minimal_booking_delay is None:
return start_datetime
min_datetime = localtime(now()) + datetime.timedelta(days=agenda.minimal_booking_delay)
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)
return agenda.min_booking_datetime
return max(agenda.min_booking_datetime, start_datetime)
def get_max_datetime(agenda, end_datetime=None):
if agenda.maximal_booking_delay is None:
return end_datetime
max_datetime = localtime(now()) + datetime.timedelta(days=agenda.maximal_booking_delay)
max_datetime = max_datetime.replace(hour=0, minute=0, second=0, microsecond=0)
if end_datetime is None:
return max_datetime
return min(max_datetime, end_datetime)
return agenda.max_booking_datetime
return min(agenda.max_booking_datetime, end_datetime)
TimeSlot = collections.namedtuple('TimeSlot', ['start_datetime', 'end_datetime', 'full', 'desk'])

View File

@ -6045,3 +6045,23 @@ def test_recurring_events_api_various_times(app, user, mock_now):
new_event = Booking.objects.get(pk=resp.json['booking_id']).event
assert event.start_datetime == new_event.start_datetime
def test_datetimes_dst(app, freezer):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=10
)
event = Event.objects.create(
slug='event-slug',
start_datetime=make_aware(datetime.datetime(2021, 3, 28, 12, 30)),
places=5,
agenda=agenda,
)
freezer.move_to('Fri, 18 Mar 2021 23:59:39 +0100')
# maximal_booking_delay is 10days so last bookable event is on
# 2021-03-28T00:00, so event should not be bookable
assert not event.in_bookable_period()
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
assert len(resp.json['data']) == 0