from datetime import datetime, timedelta from typing import Union from urllib.parse import urljoin from django.conf import settings from django.urls import reverse from django.utils import timezone from django.utils.formats import date_format from django.utils.translation import gettext_lazy as _ from django.utils.translation import ngettext import zoneinfo from aleksis.core.models import Notification, Person from aleksis.core.util.core_helpers import get_site_preferences from ..models import Event, ExtraLesson, LessonSubstitution, SupervisionSubstitution def send_notifications_for_object( instance: Union[ExtraLesson, LessonSubstitution, Event, SupervisionSubstitution], ): """Send notifications for a change object.""" recipients = [] if isinstance(instance, LessonSubstitution): recipients += instance.lesson_period.lesson.teachers.all() recipients += instance.teachers.all() recipients += Person.objects.filter( member_of__in=instance.lesson_period.lesson.groups.all() ) elif isinstance(instance, (Event, ExtraLesson)): recipients += instance.teachers.all() recipients += Person.objects.filter(member_of__in=instance.groups.all()) elif isinstance(instance, SupervisionSubstitution): recipients.append(instance.teacher) recipients.append(instance.supervision.teacher) description = "" if isinstance(instance, LessonSubstitution): # Date, lesson, subject subject = instance.lesson_period.lesson.subject day = instance.date period = instance.lesson_period.period if instance.cancelled: description += ( _( "The {subject} lesson in the {period}. period on {day} has been cancelled." ).format(subject=subject.name, period=period.period, day=date_format(day)) + " " ) else: description += ( _( "The {subject} lesson in the {period}. period " "on {day} has some current changes." ).format(subject=subject.name, period=period.period, day=date_format(day)) + " " ) if instance.teachers.all(): description += ( ngettext( "The teacher {old} is substituted by {new}.", "The teachers {old} are substituted by {new}.", instance.teachers.count(), ).format( old=instance.lesson_period.lesson.teacher_names, new=instance.teacher_names, ) + " " ) if instance.subject: description += ( _("The subject is changed to {subject}.").format(subject=instance.subject.name) + " " ) if instance.room: description += ( _("The lesson is moved from {old} to {new}.").format( old=instance.lesson_period.room.name, new=instance.room.name, ) + " " ) if instance.comment: description += ( _("There is an additional comment: {comment}.").format(comment=instance.comment) + " " ) elif isinstance(instance, Event): if instance.date_start != instance.date_end: description += ( _( "There is an event that starts on {date_start}, {period_from}. period " "and ends on {date_end}, {period_to}. period:" ).format( date_start=date_format(instance.date_start), date_end=date_format(instance.date_end), period_from=instance.period_from.period, period_to=instance.period_to.period, ) + "\n" ) else: description += ( _( "There is an event on {date} from the " "{period_from}. period to the {period_to}. period:" ).format( date=date_format(instance.date_start), period_from=instance.period_from.period, period_to=instance.period_to.period, ) + "\n" ) if instance.groups.all(): description += _("Groups: {groups}").format(groups=instance.group_names) + "\n" if instance.teachers.all(): description += _("Teachers: {teachers}").format(teachers=instance.teacher_names) + "\n" if instance.rooms.all(): description += ( _("Rooms: {rooms}").format( rooms=", ".join([room.name for room in instance.rooms.all()]) ) + "\n" ) elif isinstance(instance, ExtraLesson): description += ( _("There is an extra lesson on {date} in the {period}. period:").format( date=date_format(instance.date), period=instance.period.period, ) + "\n" ) if instance.groups.all(): description += _("Groups: {groups}").format(groups=instance.group_names) + "\n" if instance.room: description += _("Subject: {subject}").format(subject=instance.subject.name) + "\n" if instance.teachers.all(): description += _("Teachers: {teachers}").format(teachers=instance.teacher_names) + "\n" if instance.room: description += _("Room: {room}").format(room=instance.room.name) + "\n" if instance.comment: description += _("Comment: {comment}.").format(comment=instance.comment) + "\n" elif isinstance(instance, SupervisionSubstitution): description += _( "The supervision of {old} on {date} between the {period_from}. period " "and the {period_to}. period in the area {area} is substituted by {new}." ).format( old=instance.supervision.teacher.full_name, date=date_format(instance.date), period_from=instance.supervision.break_item.after_period_number, period_to=instance.supervision.break_item.before_period_number, area=instance.supervision.area.name, new=instance.teacher.full_name, ) day = instance.date if hasattr(instance, "date") else instance.date_start url = urljoin( settings.BASE_URL, reverse( "my_timetable_by_date", args=[day.year, day.month, day.day], ), ) dt_start, dt_end = instance.time_range dt_start = dt_start.replace(tzinfo=zoneinfo.ZoneInfo(settings.TIME_ZONE)) dt_end = dt_end.replace(tzinfo=zoneinfo.ZoneInfo(settings.TIME_ZONE)) send_time = get_site_preferences()["chronos__time_for_sending_notifications"] number_of_days = get_site_preferences()["chronos__days_in_advance_notifications"] start_range = timezone.now().replace(hour=send_time.hour, minute=send_time.minute) if timezone.now().time() > send_time: start_range = start_range - timedelta(days=1) end_range = start_range + timedelta(days=number_of_days) if dt_start < start_range and dt_end < end_range: # Skip this, because the change is in the past return if dt_start <= end_range and dt_end >= start_range: # Send immediately send_at = timezone.now() else: # Schedule for later send_at = datetime.combine( dt_start.date() - timedelta(days=number_of_days), send_time ).replace(tzinfo=zoneinfo.ZoneInfo(settings.TIME_ZONE)) for recipient in recipients: if recipient.preferences["chronos__send_notifications"]: n = Notification( recipient=recipient, sender=_("Timetable"), title=_("There are current changes to your timetable."), description=description, link=url, send_at=send_at, ) n.save()