from collections import OrderedDict from datetime import date from typing import Union, List from calendarweek import CalendarWeek from django.apps import apps from aleksis.core.models import Person LessonPeriod = apps.get_model("chronos", "LessonPeriod") TimePeriod = apps.get_model("chronos", "TimePeriod") Break = apps.get_model("chronos", "Break") Supervision = apps.get_model("chronos", "Supervision") LessonSubstitution = apps.get_model("chronos", "LessonSubstitution") SupervisionSubstitution = apps.get_model("chronos", "SupervisionSubstitution") Event = apps.get_model("chronos", "Event") def build_timetable( type_: str, obj: Union[int, Person], date_ref: Union[CalendarWeek, date] ): needed_breaks = [] if not isinstance(obj, int): pk = obj.pk else: pk = obj is_person = False if type_ == "person": is_person = True type_ = obj.timetable_type # Get matching lesson periods if is_person: lesson_periods = LessonPeriod.objects.daily_lessons_for_person(obj, date_ref) else: lesson_periods = LessonPeriod.objects.in_week(date_ref).filter_from_type( type_, obj ) # Sort lesson periods in a dict lesson_periods_per_period = {} for lesson_period in lesson_periods: period = lesson_period.period.period weekday = lesson_period.period.weekday if period not in lesson_periods_per_period: lesson_periods_per_period[period] = [] if is_person else {} if not is_person and weekday not in lesson_periods_per_period[period]: lesson_periods_per_period[period][weekday] = [] if is_person: lesson_periods_per_period[period].append(lesson_period) else: lesson_periods_per_period[period][weekday].append(lesson_period) # Get events if is_person: events = Event.objects.on_day(date_ref).filter_from_person(obj) else: events = Event.objects.in_week(date_ref).filter_from_type(type_, obj) # Sort events in a dict events_per_period = {} for event in events: if not is_person and event.date_start < date_ref[TimePeriod.weekday_min]: # If start date not in current week, set weekday and period to min weekday_from = TimePeriod.weekday_min period_from_first_weekday = TimePeriod.period_min else: weekday_from = event.date_start.weekday() period_from_first_weekday = event.period_from.period if not is_person and event.date_end > date_ref[TimePeriod.weekday_max]: # If end date not in current week, set weekday and period to max weekday_to = TimePeriod.weekday_max period_to_last_weekday = TimePeriod.period_max else: weekday_to = event.date_end.weekday() period_to_last_weekday = event.period_to.period for weekday in range(weekday_from, weekday_to + 1): if is_person and weekday != date_ref.weekday(): # If daily timetable for person, skip other weekdays continue if weekday == weekday_from: # If start day, use start period period_from = period_from_first_weekday else: # If not start day, use min period period_from = TimePeriod.period_min if weekday == weekday_to: # If end day, use end period period_to = period_to_last_weekday else: # If not end day, use max period period_to = TimePeriod.period_max for period in range(period_from, period_to +1): if period not in events_per_period: events_per_period[period] = [] if is_person else {} if not is_person and weekday not in events_per_period[period]: events_per_period[period][weekday] = [] if is_person: events_per_period[period].append(event) else: events_per_period[period][weekday].append(event) if type_ == "teacher": # Get matching supervisions if is_person: week = CalendarWeek.from_date(date_ref) else: week = date_ref supervisions = Supervision.objects.all().annotate_week(week).filter_by_teacher(obj) if is_person: supervisions.filter_by_weekday(date_ref.weekday()) supervisions_per_period_after = {} for supervision in supervisions: weekday = supervision.break_item.weekday period_after_break = supervision.break_item.before_period_number print(supervision, weekday, period_after_break) if period_after_break not in needed_breaks: needed_breaks.append(period_after_break) if ( not is_person and period_after_break not in supervisions_per_period_after ): supervisions_per_period_after[period_after_break] = {} if is_person: supervisions_per_period_after[period_after_break] = supervision else: supervisions_per_period_after[period_after_break][weekday] = supervision # Get ordered breaks breaks = OrderedDict(sorted(Break.get_breaks_dict().items())) rows = [] for period, break_ in breaks.items(): # period is period after break # Break if type_ == "teacher" and period in needed_breaks: row = { "type": "break", "after_period": break_.after_period_number, "before_period": break_.before_period_number, "time_start": break_.time_start, "time_end": break_.time_end, } if not is_person: cols = [] for weekday in range( TimePeriod.weekday_min, TimePeriod.weekday_max + 1 ): col = None if period in supervisions_per_period_after: if weekday in supervisions_per_period_after[period]: col = supervisions_per_period_after[period][weekday] cols.append(col) row["cols"] = cols else: col = None if period in supervisions_per_period_after: col = supervisions_per_period_after[period] row["col"] = col rows.append(row) # Period if period <= TimePeriod.period_max: row = { "type": "period", "period": period, "time_start": break_.before_period.time_start, "time_end": break_.before_period.time_end, } if not is_person: cols = [] for weekday in range( TimePeriod.weekday_min, TimePeriod.weekday_max + 1 ): col = [] # Add lesson periods if period in lesson_periods_per_period: if weekday in lesson_periods_per_period[period]: col += lesson_periods_per_period[period][weekday] # Add events if period in events_per_period: if weekday in events_per_period[period]: col += events_per_period[period][weekday] cols.append(col) row["cols"] = cols else: col = [] # Add lesson periods if period in lesson_periods_per_period: col += lesson_periods_per_period[period] # Add events if period in events_per_period: col += events_per_period[period] row["col"] = col rows.append(row) return rows def build_substitutions_list(wanted_day: date) -> List[dict]: rows = [] subs = LessonSubstitution.objects.on_day(wanted_day).order_by( "lesson_period__lesson__groups", "lesson_period__period" ) for sub in subs: if not sub.cancelled_for_teachers: sort_a = sub.lesson_period.lesson.group_names else: sort_a = "Z.{}".format(sub.lesson_period.lesson.teacher_names) row = { "type": "substitution", "sort_a": sort_a, "sort_b": "{}".format(sub.lesson_period.period.period), "el": sub, } rows.append(row) # Get supervision substitutions super_subs = SupervisionSubstitution.objects.filter(date=wanted_day) for super_sub in super_subs: row = { "type": "supervision_substitution", "sort_a": "Z.{}".format(super_sub.teacher), "sort_b": "{}".format(super_sub.supervision.break_item.after_period_number), "el": super_sub } rows.append(row) # Sort all items def sorter(row: dict): return row["sort_a"] + row["sort_b"] rows.sort(key=sorter) return rows