From 71be8be39f49045d5dde82a95dcf5d44a77e892f Mon Sep 17 00:00:00 2001
From: Konrad Mohrfeldt <konrad.mohrfeldt@farbdev.org>
Date: Wed, 31 Jul 2024 13:18:29 +0200
Subject: [PATCH] fix: generate stable ids for program entries
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Up until now the id of the first and last program entry was based on the
filtered range. Instead, we want these to be based on the first timeslot
that was scheduled before/after the specified range. This way we ensure
that the ids of these entries are stable as long as the schedule doesn’t
change.
---
 program/services.py | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/program/services.py b/program/services.py
index d1a17fe2..77e57bb3 100644
--- a/program/services.py
+++ b/program/services.py
@@ -765,12 +765,25 @@ def generate_program_entries(
     # show. We can only create these program entries if a fallback show has been specified.
     fallback_show = get_fallback_show(raise_exceptions=True)
 
-    entry_start = start
+    # Shift the range start/end to the closest scheduled timeslots around the specified range.
+    # This ensures that we generate stable ids for entries.
+    # We first check if the timeslots in our queryset might already start/end before/after the
+    # specified range, because we potentially include them according to the filter above.
+    first_ts_before_start = timeslots.first()
+    if not first_ts_before_start or first_ts_before_start.start > start:
+        first_ts_before_start = queryset.filter(end__lte=start).last()
+    first_ts_after_end = timeslots.last()
+    if not first_ts_after_end or first_ts_after_end.end < end:
+        first_ts_after_end = queryset.filter(start__gte=end).first()
+    range_start = first_ts_before_start.end if first_ts_before_start else start
+    range_end = first_ts_after_end.start if first_ts_after_end else end
+
+    entry_start = range_start
     timeslot: TimeSlot
     for timeslot in timeslots:
         if timeslot.start > entry_start:
             yield create_entry(entry_start, timeslot.start, fallback_show)
         yield create_timeslot_entry(timeslot)
         entry_start = timeslot.end
-    if entry_start < end:
-        yield create_entry(entry_start, end, fallback_show)
+    if entry_start < range_end:
+        yield create_entry(entry_start, range_end, fallback_show)
-- 
GitLab