Skip to content
Snippets Groups Projects
build.py 14.4 KiB
Newer Older
from collections import OrderedDict
Tom Teichler's avatar
Tom Teichler committed
from typing import List, Tuple, Union
Tom Teichler's avatar
Tom Teichler committed
from calendarweek import CalendarWeek

from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.models import Room
from aleksis.core.models import Group, 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")
Jonathan Weth's avatar
Jonathan Weth committed
Holiday = apps.get_model("chronos", "Holiday")
ExtraLesson = apps.get_model("chronos", "ExtraLesson")


Jonathan Weth's avatar
Jonathan Weth committed
    type_: Union[TimetableType, str],
    obj: Union[Group, Room, Person],
Jonathan Weth's avatar
Jonathan Weth committed
    date_ref: Union[CalendarWeek, date],
    with_holidays: bool = True,
    is_person = False
    if type_ == "person":
        is_person = True
        type_ = obj.timetable_type

    is_week = False
    if type(date_ref) == CalendarWeek:
        is_week = True

Jonathan Weth's avatar
Jonathan Weth committed
    # Get matching holidays
        holidays_per_weekday = Holiday.in_week(date_ref) if with_holidays else {}
        holiday = Holiday.on_day(date_ref) if with_holidays else None
Jonathan Weth's avatar
Jonathan Weth committed

    lesson_periods = LessonPeriod.objects
    lesson_periods = (
        lesson_periods.select_related(None)
        .select_related("lesson", "lesson__subject", "period", "room")
        .only(
            "lesson",
            "period",
            "room",
            "lesson__subject",
            "period__weekday",
            "period__period",
            "lesson__subject__short_name",
            "lesson__subject__name",
            "lesson__subject__colour_fg",
            "lesson__subject__colour_bg",
            "room__short_name",
            "room__name",
        )
    )

    if is_week:
        lesson_periods = lesson_periods.in_week(date_ref)
    else:
        lesson_periods = lesson_periods.on_day(date_ref)

        lesson_periods = lesson_periods.filter_from_person(obj)
        lesson_periods = lesson_periods.filter_from_type(type_, obj)
    lesson_periods_per_period = lesson_periods.group_by_periods(is_week=is_week)
    # Get events
    extra_lessons = ExtraLesson.objects
    if is_week:
        extra_lessons = extra_lessons.filter(week=date_ref.week, year=date_ref.year)
    else:
        extra_lessons = extra_lessons.on_day(date_ref)
    if is_person:
        extra_lessons = extra_lessons.filter_from_person(obj)
        extra_lessons = extra_lessons.filter_from_type(type_, obj)
    extra_lessons = extra_lessons.only(
        "week",
        "year",
        "period",
        "subject",
        "room",
        "comment",
        "period__weekday",
        "period__period",
        "subject__short_name",
        "subject__name",
        "subject__colour_fg",
        "subject__colour_bg",
        "room__short_name",
        "room__name",
    )

    # Sort lesson periods in a dict
    extra_lessons_per_period = extra_lessons.group_by_periods(is_week=is_week)
    events = Event.objects
    if is_week:
        events = events.in_week(date_ref)
    else:
        events = events.on_day(date_ref)

        events = events.filter_from_person(obj)
        events = events.filter_from_type(type_, obj)
    events = events.only(
        "id",
        "title",
        "date_start",
        "date_end",
        "period_from",
        "period_to",
        "period_from__weekday",
        "period_from__period",
        "period_to__weekday",
        "period_to__period",
    )
    # Sort events in a dict
    events_per_period = {}
    for event in events:
        if is_week 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 is_week 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 not is_week 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

Tom Teichler's avatar
Tom Teichler committed
            for period in range(period_from, period_to + 1):
                if period not in events_per_period:
                    events_per_period[period] = {} if is_week else []
                if is_week and weekday not in events_per_period[period]:
                    events_per_period[period][weekday] = []

                    events_per_period[period].append(event)
                else:
                    events_per_period[period][weekday].append(event)

    if type_ == TimetableType.TEACHER:
        # Get matching supervisions
            week = CalendarWeek.from_date(date_ref)
        else:
            week = date_ref
Jonathan Weth's avatar
Jonathan Weth committed
        supervisions = (
            Supervision.objects.in_week(week)
            .all()
            .annotate_week(week)
            .filter_by_teacher(obj)
            .only(
                "area",
                "break_item",
                "teacher",
                "area",
                "area__short_name",
                "area__name",
                "area__colour_fg",
                "area__colour_bg",
                "break_item__short_name",
                "break_item__name",
                "break_item__after_period__period",
                "break_item__after_period__weekday",
                "break_item__before_period__period",
                "break_item__before_period__weekday",
                "teacher__short_name",
                "teacher__first_name",
                "teacher__last_name",
            )
Jonathan Weth's avatar
Jonathan Weth committed
        )
            supervisions = 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

            if period_after_break not in needed_breaks:
                needed_breaks.append(period_after_break)

            if is_week and period_after_break not in supervisions_per_period_after:
                supervisions_per_period_after[period_after_break] = {}

                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_ == TimetableType.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,
            }

Tom Teichler's avatar
Tom Teichler committed
                for weekday in range(TimePeriod.weekday_min, TimePeriod.weekday_max + 1):
Jonathan Weth's avatar
Jonathan Weth committed
                    if (
                        period in supervisions_per_period_after
                        and weekday not in holidays_per_weekday
                    ):
                        if weekday in supervisions_per_period_after[period]:
                            col = supervisions_per_period_after[period][weekday]
                    cols.append(col)
Jonathan Weth's avatar
Jonathan Weth committed
                if period in supervisions_per_period_after and not holiday:
                    col = supervisions_per_period_after[period]
                row["col"] = col
        if period <= TimePeriod.period_max:
            row = {
                "type": "period",
                "period": period,
                "time_start": break_.before_period.time_start,
                "time_end": break_.before_period.time_end,
            }
Tom Teichler's avatar
Tom Teichler committed
                for weekday in range(TimePeriod.weekday_min, TimePeriod.weekday_max + 1):
Tom Teichler's avatar
Tom Teichler committed
                    if period in lesson_periods_per_period and weekday not in holidays_per_weekday:
                        if weekday in lesson_periods_per_period[period]:
                            col += lesson_periods_per_period[period][weekday]

                    # Add extra lessons
Tom Teichler's avatar
Tom Teichler committed
                    if period in extra_lessons_per_period and weekday not in holidays_per_weekday:
                        if weekday in extra_lessons_per_period[period]:
                            col += extra_lessons_per_period[period][weekday]

Tom Teichler's avatar
Tom Teichler committed
                    if period in events_per_period and weekday not in holidays_per_weekday:
                        if weekday in events_per_period[period]:
                            col += events_per_period[period][weekday]

                # Add lesson periods
Jonathan Weth's avatar
Jonathan Weth committed
                if period in lesson_periods_per_period and not holiday:
                # Add extra lessons
                if period in extra_lessons_per_period and not holiday:
                    col += extra_lessons_per_period[period]

Jonathan Weth's avatar
Jonathan Weth committed
                if period in events_per_period and not holiday:
                    col += events_per_period[period]



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"
    )

    start_period = None
    for i, sub in enumerate(subs):
            sort_a = sub.lesson_period.lesson.groups_to_show_names
            sort_a = f"Z.{sub.lesson_period.lesson.teacher_names}"
        # Get next substitution
        next_sub = subs[i + 1] if i + 1 < len(subs) else None

        # Check if next substitution is equal with this substitution
        if (
            next_sub
            and sub.comment == next_sub.comment
            and sub.cancelled == next_sub.cancelled
            and sub.subject == next_sub.subject
            and sub.room == next_sub.room
            and sub.lesson_period.lesson == next_sub.lesson_period.lesson
            and set(sub.teachers.all()) == set(next_sub.teachers.all())
        ):
            if not start_period:
                start_period = sub.lesson_period.period.period
            continue

            "sort_b": str(sub.lesson_period.period.period),
            "start_period": start_period if start_period else sub.lesson_period.period.period,
            "end_period": sub.lesson_period.period.period,
    # Get supervision substitutions
    super_subs = SupervisionSubstitution.objects.filter(date=wanted_day)

    for super_sub in super_subs:
        row = {
            "type": "supervision_substitution",
            "sort_a": f"Z.{super_sub.teacher}",
            "sort_b": str(super_sub.supervision.break_item.after_period_number),
Tom Teichler's avatar
Tom Teichler committed
            "el": super_sub,
    # Get extra lessons
    extra_lessons = ExtraLesson.objects.on_day(wanted_day)

    for extra_lesson in extra_lessons:
        row = {
            "type": "extra_lesson",
            "sort_a": str(extra_lesson.group_names),
            "sort_b": str(extra_lesson.period.period),
            "el": extra_lesson,
        }
        rows.append(row)

    # Get events
    events = Event.objects.on_day(wanted_day).annotate_day(wanted_day)

    for event in events:
        if event.groups.all():
            sort_a = event.group_names
        else:
            sort_a = f"Z.{event.teacher_names}"

        row = {
            "type": "event",
            "sort_a": sort_a,
            "sort_b": str(event.period_from_on_day),
            "el": event,
        }
        rows.append(row)

    # Sort all items
    def sorter(row: dict):
        return row["sort_a"] + row["sort_b"]

    rows.sort(key=sorter)

def build_weekdays(
    base: List[Tuple[int, str]], wanted_week: CalendarWeek, with_holidays: bool = True
) -> List[dict]:
    if with_holidays:
        holidays_per_weekday = Holiday.in_week(wanted_week)
Jonathan Weth's avatar
Jonathan Weth committed

    weekdays = []
    for key, name in base[TimePeriod.weekday_min : TimePeriod.weekday_max + 1]:

        weekday = {
            "key": key,
            "name": name,
            "date": wanted_week[key],
        }
        if with_holidays:
            weekday["holiday"] = holidays_per_weekday[key] if key in holidays_per_weekday else None
Jonathan Weth's avatar
Jonathan Weth committed
        weekdays.append(weekday)

    return weekdays