diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index 4d61b16c611de2dcee4a0f63e58cb52a2833d90e..f8e5df901f6e599aebd0d64fb7073b875c7b7228 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 d26653216f30acefac397ead20f97193546b0f0a..b3dc8362091693b313bda2d5f9493d8afa7e7701 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 62cdb0847a9707156975d681e603bae9e6429935..bade1561151dbe2bbd78d9d8215eee2e7dd43b70 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 67e7626f65bbb4bfde2280e4aab64af236eb03f5..8e70ceae4b383dfcd72eb3aad80afd12d2f08175 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