from datetime import date, datetime, timedelta from typing import TYPE_CHECKING, Optional from django.db.models import Count, Q from django.http import HttpRequest, HttpResponseNotFound from django.shortcuts import get_object_or_404 from guardian.core import ObjectPermissionChecker from aleksis.core.models import Announcement, Group, Person, Room from aleksis.core.util.core_helpers import get_site_preferences from aleksis.core.util.predicates import check_global_permission from ..managers import TimetableType from ..models import ( Absence, LessonPeriod, LessonSubstitution, Supervision, SupervisionSubstitution, ) from .build import build_substitutions_list if TYPE_CHECKING: from django.contrib.auth import get_user_model User = get_user_model() # noqa def get_el_by_pk( request: HttpRequest, type_: str, pk: int, prefetch: bool = False, *args, **kwargs, ): if type_ == TimetableType.GROUP.value: return get_object_or_404( Group.objects.prefetch_related("owners", "parent_groups") if prefetch else Group, pk=pk, ) elif type_ == TimetableType.TEACHER.value: return get_object_or_404(Person, pk=pk) elif type_ == TimetableType.ROOM.value: return get_object_or_404(Room, pk=pk) else: return HttpResponseNotFound() def get_substitution_by_id(request: HttpRequest, id_: int, week: int): lesson_period = get_object_or_404(LessonPeriod, pk=id_) wanted_week = lesson_period.lesson.get_calendar_week(week) return LessonSubstitution.objects.filter( week=wanted_week.week, year=wanted_week.year, lesson_period=lesson_period ).first() def get_supervision_substitution_by_id(request: HttpRequest, id_: int, date: datetime.date): supervision = get_object_or_404(Supervision, pk=id_) return SupervisionSubstitution.objects.filter(date=date, supervision=supervision).first() def get_teachers(user: "User"): """Get the teachers whose timetables are allowed to be seen by current user.""" checker = ObjectPermissionChecker(user) teachers = ( Person.objects.annotate(lessons_count=Count("lesson_events_as_teacher")) .filter(lessons_count__gt=0) .order_by("short_name", "last_name") ) if not check_global_permission(user, "chronos.view_all_person_timetables"): checker.prefetch_perms(teachers) wanted_teachers = set() for teacher in teachers: if checker.has_perm("core.view_person_timetable", teacher): wanted_teachers.add(teacher.pk) teachers = teachers.filter(Q(pk=user.person.pk) | Q(pk__in=wanted_teachers)) teachers = teachers.distinct() return teachers def get_groups(user: "User"): """Get the groups whose timetables are allowed to be seen by current user.""" checker = ObjectPermissionChecker(user) groups = ( Group.objects.for_current_school_term_or_all() .annotate( lessons_count=Count("lesson_events"), child_lessons_count=Count("child_groups__lesson_events"), ) .filter(Q(lessons_count__gt=0) | Q(child_lessons_count__gt=0)) ) group_types = get_site_preferences()["chronos__group_types_timetables"] if group_types: groups = groups.filter(group_type__in=group_types) groups = groups.order_by("short_name", "name") if not check_global_permission(user, "chronos.view_all_group_timetables"): checker.prefetch_perms(groups) wanted_classes = set() for _class in groups: if checker.has_perm("core.view_group_timetable", _class): wanted_classes.add(_class.pk) groups = groups.filter( Q(pk__in=wanted_classes) | Q(members=user.person) | Q(owners=user.person) ) if user.person.primary_group: groups = groups.filter(Q(pk=user.person.primary_group.pk)) groups = groups.distinct() return groups def get_rooms(user: "User"): """Get the rooms whose timetables are allowed to be seen by current user.""" checker = ObjectPermissionChecker(user) rooms = ( Room.objects.annotate(lessons_count=Count("lesson_events")) .filter(lessons_count__gt=0) .order_by("short_name", "name") ) if not check_global_permission(user, "chronos.view_all_room_timetables"): checker.prefetch_perms(rooms) wanted_rooms = set() for room in rooms: if checker.has_perm("core.view_room_timetable", room): wanted_rooms.add(room.pk) rooms = rooms.filter(Q(pk__in=wanted_rooms)) rooms = rooms.distinct() return rooms def get_substitutions_context_data( wanted_day: date, number_of_days: Optional[int] = None, show_header_box: Optional[bool] = None, ): """Get context data for the substitutions table.""" context = {} day_number = ( number_of_days or get_site_preferences()["chronos__substitutions_print_number_of_days"] ) show_header_box = ( show_header_box if show_header_box is not None else get_site_preferences()["chronos__substitutions_show_header_box"] ) day_contexts = {} day = wanted_day for _i in range(day_number): day_contexts[day] = {"day": day} subs, affected_teachers, affected_groups = build_substitutions_list(day) day_contexts[day]["substitutions"] = subs 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) context["days"] = day_contexts return context