Skip to content

Commit 431857f

Browse files
committed
Revert breaking optimizations to FRT calculation
Reverts commits: - e0c0d11 - 3f5c078 - a5c7034 - 91c43d5 Something in here breaks FRT in test server. Revert changes until fix is found so that we can merge changes to prod.
1 parent 4b885d4 commit 431857f

4 files changed

Lines changed: 127 additions & 193 deletions

File tree

opening_hours/utils/time_span_element_utils.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@ def merge_overlapping_time_span_elements(time_span_elements: list[TimeSpanElemen
1515

1616
merged_time_span_elements: list[TimeSpanElement] = []
1717
for current_time_span in time_span_elements:
18-
# If the `merged_time_span_elements` list is empty, simply append the current time span
18+
# If the selected_list is empty, simply append the current time span
1919
if not merged_time_span_elements:
2020
merged_time_span_elements.append(current_time_span)
2121
continue
2222

23-
# If the `merged_time_span_elements` contains a time span that overlaps with the current time span, combine them
23+
# If the selected_list contains a time span that overlaps with the current time span, combine them.
2424
# The only time span that can overlap with the current time span is the last time span in the list.
25-
last_time_span = merged_time_span_elements[-1]
26-
2725
# ┌────────────────────────┐
2826
# │ LEGEND │
2927
# │ █ = Reservation │
@@ -32,7 +30,7 @@ def merge_overlapping_time_span_elements(time_span_elements: list[TimeSpanElemen
3230
# ├────────────────────────┼──────────────────────────────────────────┐
3331
# │ ████ -> ███████ │ Overlapping │
3432
# │ █████ -> │ The last time span is extended │
35-
# │ │ │
33+
# ├────────────────────────┼──────────────────────────────────────────┤
3634
# │ ████ -> ███████ │ Current time span ends at the same time │
3735
# │ ███ -> │ as the last last time span ends. │
3836
# │ │ The last time span is extended │
@@ -46,25 +44,35 @@ def merge_overlapping_time_span_elements(time_span_elements: list[TimeSpanElemen
4644
# │ ▄▄▄██▄▄ -> │ │
4745
# ├────────────────────────┼──────────────────────────────────────────┤
4846
# │ ███████ -> ███████ │ Time span is fully inside the last one │
49-
# │ ███ -> │ Later can be skipped
50-
# │ │
51-
# │ █████ -> ▄█████▄ │ Buffers extend outside earlier time span
52-
# │ ▄▄███▄▄ -> │ The buffered start/end times are kept
47+
# │ ███ -> │
48+
# │ │ The buffered start/end times are kept
49+
# │ █████ -> ▄█████▄ │
50+
# │ ▄▄███▄▄ -> │
5351
# └────────────────────────┴──────────────────────────────────────────┘
54-
# Last time span overlaps with the current time span, so they can be merged.
52+
last_time_span = merged_time_span_elements[-1]
53+
54+
# Last time span overlaps with the current time span, combine them.
5555
if last_time_span.end_datetime >= current_time_span.start_datetime:
56-
# Adjust the before buffer of the last time span
57-
if current_time_span.buffer_time_before:
58-
min_buffered_dt = min(last_time_span.buffered_start_datetime, current_time_span.buffered_start_datetime)
59-
last_time_span.buffer_time_before = last_time_span.start_datetime - min_buffered_dt
56+
# This time span is fully inside the last one.
57+
if last_time_span.end_datetime > current_time_span.end_datetime:
58+
# Even with after buffers, this time span is fully inside the last one, so it can be skipped.
59+
if last_time_span.buffered_end_datetime > current_time_span.buffered_end_datetime:
60+
continue
61+
62+
# Current time span's buffer ends after the last's buffer ends, so we can extend the last's buffer.
63+
buffer_delta = last_time_span.buffered_end_datetime - current_time_span.buffered_end_datetime
64+
last_time_span.buffer_time_after += buffer_delta
65+
continue
6066

61-
# Adjust the after buffer of the last time span
62-
# If the current time span ends after the last time span ends, extend the last time span's end datetime.
67+
# Extend the last time span's before buffer.
68+
min_buffered_dt = min(last_time_span.buffered_start_datetime, current_time_span.buffered_start_datetime)
69+
last_time_span.buffer_time_before = last_time_span.start_datetime - min_buffered_dt
70+
71+
# Extend the last time span and it's after buffer.
72+
# The buffered end datetime should stay the same.
6373
max_buffered_dt = max(last_time_span.buffered_end_datetime, current_time_span.buffered_end_datetime)
64-
last_time_span.end_datetime = max(last_time_span.end_datetime, current_time_span.end_datetime)
74+
last_time_span.end_datetime = current_time_span.end_datetime
6575
last_time_span.buffer_time_after = max_buffered_dt - last_time_span.end_datetime
66-
67-
# The current time span is discarded after the above adjustments.
6876
continue
6977

7078
# No overlapping time spans, check for overlapping buffers and add the current time span to the merged list.

reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservable_time_span_helper.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,14 @@ def calculate_first_reservable_time(self) -> ReservableTimeOutput:
4343
if not normalised_time_spans:
4444
return ReservableTimeOutput(is_closed=True, first_reservable_time=None)
4545

46-
# For minimal performance gains this should only be run once; the first time we know the ReservationUnit is open
47-
if self.parent.is_reservation_unit_closed:
48-
# At this point we know that the ReservationUnit is OPEN.
49-
# Now that we know if the ReservationUnit is OPEN, Validate `reservation_unit.max_reservation_duration`.
50-
if self.parent.is_reservation_unit_max_duration_invalid:
51-
return ReservableTimeOutput(is_closed=False, first_reservable_time=None)
46+
# At this point we know that the ReservationUnit is OPEN.
47+
# Now that we know if the ReservationUnit is OPEN, Validate `reservation_unit.max_reservation_duration`.
48+
if self.parent.is_reservation_unit_max_duration_invalid:
49+
return ReservableTimeOutput(is_closed=False, first_reservable_time=None)
5250

53-
normalised_time_spans = self._soft_normalise_time_span(normalised_time_spans)
54-
if not normalised_time_spans:
55-
return ReservableTimeOutput(is_closed=False, first_reservable_time=None)
51+
normalised_time_spans = self._soft_normalise_time_span(normalised_time_spans)
52+
if not normalised_time_spans:
53+
return ReservableTimeOutput(is_closed=False, first_reservable_time=None)
5654

5755
# At this point we have removed all the closed time spans from the reservable time span.
5856
# Finally, try to find the first reservable time span from the left over reservable time spans.
@@ -65,12 +63,14 @@ def calculate_first_reservable_time(self) -> ReservableTimeOutput:
6563

6664
def _hard_normalise_time_span(self, current_time_span: TimeSpanElement) -> list[TimeSpanElement]:
6765
"""Remove Hard-Closed time spans from a TimeSpanElement."""
68-
combined_hard_closed_time_spans: list[TimeSpanElement] = (
69-
self.parent.hard_closed_time_spans
70-
+ current_time_span.generate_closed_time_spans_outside_filter(
71-
filter_time_start=self.parent.parent.filter_time_start,
72-
filter_time_end=self.parent.parent.filter_time_end,
73-
)
66+
combined_hard_closed_time_spans: list[TimeSpanElement] = []
67+
68+
# Add hard closed time spans from the ReservationUnit
69+
combined_hard_closed_time_spans += self.parent.hard_closed_time_spans
70+
# Add hard closed time spans from the ReservableTimeSpan
71+
combined_hard_closed_time_spans += current_time_span.generate_closed_time_spans_outside_filter(
72+
filter_time_start=self.parent.parent.filter_time_start,
73+
filter_time_end=self.parent.parent.filter_time_end,
7474
)
7575

7676
return override_reservable_with_closed_time_spans(

reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservation_unit_helper.py

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,22 @@ class ReservationUnitFirstReservableTimeHelper:
5454

5555
is_reservation_unit_max_duration_invalid: bool
5656

57-
is_reservation_unit_closed: bool
58-
5957
def __init__(self, parent: FirstReservableTimeHelper, reservation_unit: ReservationUnit) -> None:
6058
self.parent = parent
6159
self.reservation_unit = reservation_unit
6260

63-
self.hard_closed_time_spans = self._merge_time_spans(
64-
self._get_hard_closed_time_spans() + parent.shared_hard_closed_time_spans
65-
)
61+
self.hard_closed_time_spans = self._get_hard_closed_time_spans()
62+
self.hard_closed_time_spans += parent.shared_hard_closed_time_spans
63+
self.hard_closed_time_spans = self._merge_time_spans(self.hard_closed_time_spans)
6664

67-
self.reservation_closed_time_spans = self._merge_time_spans(self._get_reservation_closed_time_spans())
65+
self.reservation_closed_time_spans = self._get_reservation_closed_time_spans()
66+
self.reservation_closed_time_spans = self._merge_time_spans(self.reservation_closed_time_spans)
6867
self.blocking_reservation_closed_time_spans = self._get_blocking_reservation_closed_time_spans()
6968

70-
self.soft_closed_time_spans = self._merge_time_spans(
71-
self._get_soft_closed_time_spans()
72-
+ self.reservation_closed_time_spans
73-
+ self.blocking_reservation_closed_time_spans
74-
)
69+
self.soft_closed_time_spans = self._get_soft_closed_time_spans()
70+
self.soft_closed_time_spans += self.reservation_closed_time_spans
71+
self.soft_closed_time_spans += self.blocking_reservation_closed_time_spans
72+
self.soft_closed_time_spans = self._merge_time_spans(self.soft_closed_time_spans)
7573

7674
start_interval_minutes = ReservationStartInterval(reservation_unit.reservation_start_interval).as_number
7775

@@ -97,35 +95,22 @@ def _merge_time_spans(time_spans) -> list[TimeSpanElement]:
9795
return merge_overlapping_time_span_elements(sorted(time_spans, key=lambda time_span: time_span.start_datetime))
9896

9997
def calculate_first_reservable_time(self) -> ReservableTimeOutput:
100-
self.is_reservation_unit_closed = True
98+
is_closed = True
10199

102100
# Go through each ReservableTimeSpan individually one-by-one until a suitable time span is found.
103101
for reservable_time_span in self.reservation_unit.origin_hauki_resource.reservable_time_spans.all():
104102
helper = ReservableTimeSpanFirstReservableTimeHelper(parent=self, reservable_time_span=reservable_time_span)
105103
output = helper.calculate_first_reservable_time()
106104

105+
# The ReservationUnit is not closed. Save the value in case we don't find a first reservable time.
106+
if output.is_closed is False:
107+
is_closed = False
108+
107109
# If we have found a first reservable time, we can return early
108110
if output.first_reservable_time is not None:
109111
return output
110112

111-
# The ReservationUnit is not closed. Save the value in case we don't find a first reservable time.
112-
if not output.is_closed and self.is_reservation_unit_closed:
113-
self.is_reservation_unit_closed = False
114-
115-
# Now that we know that the ReservationUnit is not closed,
116-
# we can exit early if the maximum duration is invalid.
117-
if self.is_reservation_unit_max_duration_invalid:
118-
return output
119-
120-
# We don't have a first reservable time, but we know that the ReservationUnit is not closed.
121-
# Merge the soft closed time spans with the hard closed time spans, so that all future time spans are
122-
# faster to find as we hopefully have less overlapping closed time spans that need to be looped through.
123-
self.hard_closed_time_spans = self._merge_time_spans(
124-
self.hard_closed_time_spans + self.soft_closed_time_spans
125-
)
126-
self.soft_closed_time_spans = []
127-
128-
return ReservableTimeOutput(is_closed=self.is_reservation_unit_closed, first_reservable_time=None)
113+
return ReservableTimeOutput(is_closed=is_closed, first_reservable_time=None)
129114

130115
def _get_hard_closed_time_spans(self) -> list[TimeSpanElement]:
131116
"""

0 commit comments

Comments
 (0)