From 31428640ab5e1690d034e52edd0a3bc577d9ef32 Mon Sep 17 00:00:00 2001 From: Hangzhi Yu <hangzhi@protonmail.com> Date: Sat, 3 Aug 2024 17:33:01 +0200 Subject: [PATCH] Implement calendar alarms for LessonEvent --- .../migrations/0018_add_lessoneventalarm.py | 26 ++++++++++ aleksis/apps/chronos/models.py | 42 +++++++++++++++- aleksis/apps/chronos/preferences.py | 50 ++++++++++++++++++- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 aleksis/apps/chronos/migrations/0018_add_lessoneventalarm.py diff --git a/aleksis/apps/chronos/migrations/0018_add_lessoneventalarm.py b/aleksis/apps/chronos/migrations/0018_add_lessoneventalarm.py new file mode 100644 index 00000000..b813aa84 --- /dev/null +++ b/aleksis/apps/chronos/migrations/0018_add_lessoneventalarm.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.7 on 2024-08-03 15:21 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('chronos', '0017_optional_slot_number'), + ('core', '0066_alter_freebusy_options_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='LessonEventAlarm', + fields=[ + ('calendaralarm_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.calendaralarm')), + ], + options={ + 'verbose_name': 'Lesson event alarm', + 'verbose_name_plural': 'Lesson event alarms', + }, + bases=('core.calendaralarm',), + ), + ] diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index 35f4e011..25a16143 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -68,7 +68,7 @@ from aleksis.core.mixins import ( GlobalPermissionModel, SchoolTermRelatedExtensibleModel, ) -from aleksis.core.models import CalendarEvent, Group, Person, Room, SchoolTerm +from aleksis.core.models import CalendarAlarm, CalendarEvent, Group, Person, Room, SchoolTerm from aleksis.core.util.core_helpers import get_site_preferences, has_person @@ -1588,11 +1588,51 @@ class LessonEvent(CalendarEvent): return objs.for_person(request.user.person) return objs + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + + # Save alarm in lesson event alarm model + if self.amends: + alarm, created = LessonEventAlarm.objects.get_or_create(event=self, defaults={"send_notifications": True}) + class Meta: verbose_name = _("Lesson Event") verbose_name_plural = _("Lesson Events") +class LessonEventAlarm(CalendarAlarm): + """Alarm model for lesson events.""" + + def value_description(self, request: HttpRequest | None = None) -> str: + return LessonEvent.value_title(self.event) + + def value_trigger(self, request: HttpRequest | None = None) -> Union[datetime, timedelta]: + # question: allow for generating multiple alarms for one event? + if "fixed_time_relative" in get_site_preferences()["chronos__alarm_trigger_mode"]: + return ( + self.event.datetime_start + - timedelta(days=get_site_preferences()["chronos__days_in_advance_alarms"]) + ).replace( + hour=get_site_preferences()["chronos__fixed_time_alarms"].hour, + minute=get_site_preferences()["chronos__fixed_time_alarms"].minute, + ) + elif "strictly_relative" in get_site_preferences()["chronos__alarm_trigger_mode"]: + return get_site_preferences()["chronos__time_in_advance_alarms"] + + def value_notification_sender(self, request: HttpRequest | None = None) -> str: + return _("Lesson notification") + + def value_notification_recipients(self, request: HttpRequest | None = None) -> [Person]: + return self.event.all_teachers + + def value_notification_description(self, request: HttpRequest | None = None) -> str: + return _("bliblablubb") + + class Meta: + verbose_name = _("Lesson event alarm") + verbose_name_plural = _("Lesson event alarms") + + class SupervisionEvent(LessonEvent): """Calendar feed for supervisions.""" diff --git a/aleksis/apps/chronos/preferences.py b/aleksis/apps/chronos/preferences.py index d2665321..ba435ec9 100644 --- a/aleksis/apps/chronos/preferences.py +++ b/aleksis/apps/chronos/preferences.py @@ -1,4 +1,4 @@ -from datetime import time +from datetime import time, timedelta from django.utils.translation import gettext_lazy as _ @@ -6,8 +6,10 @@ from colorfield.widgets import ColorWidget from dynamic_preferences.preferences import Section from dynamic_preferences.types import ( BooleanPreference, + DurationPreference, IntegerPreference, ModelMultipleChoicePreference, + MultipleChoicePreference, StringPreference, TimePreference, ) @@ -150,3 +152,49 @@ class SupervisionEventFeedColor(StringPreference): verbose_name = _("Supervision calendar feed color") widget = ColorWidget required = True + + +@site_preferences_registry.register +class AlarmTriggerMode(MultipleChoicePreference): + """Mode for computing the trigger property of alarms associated with lesson events.""" + + section = chronos + name = "alarm_trigger_mode" + default = ["fixed_time_relative", "strictly_relative"] + verbose_name = _("Trigger mode for lesson event alarms") + choices = ( + ( + "fixed_time_relative", + "Trigger alarm on a fixed time in a earlier day relative to the event's start", + ), + ("strictly_relative", "Trigger alarm on a time relative to the event's start"), + ) + required = True + + +# FIXME: hide the following preferences conditionally, depending on the trigger mode selected above +@site_preferences_registry.register +class DaysInAdvanceAlarms(IntegerPreference): + section = chronos + name = "days_in_advance_alarms" + default = 1 + verbose_name = _("How many days in advance should lesson event alarms be sent?") + required = True + + +@site_preferences_registry.register +class FixedTimeAlarms(TimePreference): + section = chronos + name = "fixed_time_alarms" + default = time(17, 00) + verbose_name = _("Time for sending lesson event alarms") + required = True + + +@site_preferences_registry.register +class TimeInAdvanceAlarms(DurationPreference): + section = chronos + name = "time_in_advance_alarms" + default = timedelta(hours=24) + verbose_name = _("How much in advance should lesson event alarms be sent?") + required = True -- GitLab