From 8c6aa254ffc87bf77506016e571608fbb2ca66c7 Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Sun, 11 Aug 2024 19:05:32 +0200
Subject: [PATCH] Show only relevant days in substitutions print

---
 aleksis/apps/chronos/models.py               | 11 +++++--
 aleksis/apps/chronos/preferences.py          | 31 ++++++++++++++++++++
 aleksis/apps/chronos/util/build.py           |  1 -
 aleksis/apps/chronos/util/chronos_helpers.py | 26 ++++++++++++----
 4 files changed, 60 insertions(+), 9 deletions(-)

diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index 4d61b16c..f8e5df90 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -1209,12 +1209,19 @@ class AutomaticPlan(LiveDocument):
     @property
     def current_start_day(self) -> date:
         """Get first day which should be shown in the PDF."""
-        return timezone.now().date()
+        from aleksis.apps.chronos.util.chronos_helpers import get_next_relevant_day
+
+        return get_next_relevant_day(timezone.now())
 
     @property
     def current_end_day(self) -> date:
         """Get last day which should be shown in the PDF."""
-        return self.current_start_day + timedelta(days=self.number_of_days - 1)
+        from aleksis.apps.chronos.util.chronos_helpers import get_next_relevant_day
+
+        day = self.current_start_day
+        for _i in range(self.number_of_days - 1):
+            day = get_next_relevant_day(day)
+        return day
 
     def get_context_data(self) -> dict[str, Any]:
         """Get context data for generating the substitutions PDF."""
diff --git a/aleksis/apps/chronos/preferences.py b/aleksis/apps/chronos/preferences.py
index d2665321..b3dc8362 100644
--- a/aleksis/apps/chronos/preferences.py
+++ b/aleksis/apps/chronos/preferences.py
@@ -1,13 +1,16 @@
 from datetime import time
 
+from django.core.exceptions import ValidationError
 from django.utils.translation import gettext_lazy as _
 
+from calendarweek.django import i18n_day_name_choices_lazy
 from colorfield.widgets import ColorWidget
 from dynamic_preferences.preferences import Section
 from dynamic_preferences.types import (
     BooleanPreference,
     IntegerPreference,
     ModelMultipleChoicePreference,
+    MultipleChoicePreference,
     StringPreference,
     TimePreference,
 )
@@ -52,6 +55,34 @@ class ShortenGroupsLimit(IntegerPreference):
     )
 
 
+@site_preferences_registry.register
+class SubstitutionsRelevantDays(MultipleChoicePreference):
+    """Relevant days which have substitution plans."""
+
+    section = chronos
+    name = "substitutions_relevant_days"
+    default = [0, 1, 2, 3, 4]
+    verbose_name = _("Relevant days for substitution plans")
+    required = True
+    choices = i18n_day_name_choices_lazy()
+
+    def validate(self, value):
+        for v in value:
+            if int(v) not in self.get_choice_values():
+                raise ValidationError(f"{v} is not a valid choice")
+
+
+@site_preferences_registry.register
+class SubstitutionsDayChangeTime(TimePreference):
+    """Time when substitution plans should switch to the next day."""
+
+    section = chronos
+    name = "substitutions_day_change_time"
+    default = time(18, 0)
+    verbose_name = _("Time when substitution plans switch to the next day")
+    required = True
+
+
 @site_preferences_registry.register
 class SubstitutionsPrintNumberOfDays(IntegerPreference):
     section = chronos
diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py
index 62cdb084..bade1561 100644
--- a/aleksis/apps/chronos/util/build.py
+++ b/aleksis/apps/chronos/util/build.py
@@ -410,7 +410,6 @@ def build_substitutions_list(wanted_day: date) -> tuple[list[dict], set[Person],
 
     rows.sort(key=lambda row: row["sort_a"] + row["sort_b"])
 
-    print(rows)
     return rows, affected_teachers, affected_groups
 
 
diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py
index 67e7626f..8e70ceae 100644
--- a/aleksis/apps/chronos/util/chronos_helpers.py
+++ b/aleksis/apps/chronos/util/chronos_helpers.py
@@ -13,7 +13,6 @@ from aleksis.core.util.predicates import check_global_permission
 
 from ..managers import TimetableType
 from ..models import (
-    Absence,
     LessonPeriod,
     LessonSubstitution,
     Supervision,
@@ -173,7 +172,7 @@ def get_substitutions_context_data(
     )
     day_contexts = {}
 
-    day = wanted_day
+    day = get_next_relevant_day(wanted_day)
     for _i in range(day_number):
         day_contexts[day] = {"day": day}
 
@@ -183,16 +182,31 @@ def get_substitutions_context_data(
         day_contexts[day]["announcements"] = Announcement.objects.on_date(day)
 
         if show_header_box:
-            absences = Absence.objects.on_day(day)
-            day_contexts[day]["absent_teachers"] = absences.absent_teachers()
-            day_contexts[day]["absent_groups"] = absences.absent_groups()
             day_contexts[day]["affected_teachers"] = sorted(
                 affected_teachers, key=lambda t: t.short_name or t.full_name
             )
             day_contexts[day]["affected_groups"] = affected_groups
 
-        day = day + timedelta(days=1)
+        day = get_next_relevant_day(day + timedelta(days=1))
 
     context["days"] = day_contexts
 
     return context
+
+
+def get_next_relevant_day(current: datetime | date) -> date:
+    """Get next relevant day for substitution plans."""
+    relevant_days = get_site_preferences()["chronos__substitutions_relevant_days"]
+    change_time = get_site_preferences()["chronos__substitutions_day_change_time"]
+
+    if isinstance(current, datetime):
+        current_day = current.date()
+        if current.time() > change_time:
+            current_day += timedelta(days=1)
+    else:
+        current_day = current
+
+    while str(current_day.weekday()) not in relevant_days:
+        current_day += timedelta(days=1)
+
+    return current_day
-- 
GitLab