Skip to content
Snippets Groups Projects
build.py 11.3 KiB
Newer Older
from collections import OrderedDict
Jonathan Weth's avatar
Jonathan Weth committed
from typing import Union, List, Tuple

from calendarweek import CalendarWeek
from django.db.models import QuerySet
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")


def group_by_periods(objs: QuerySet, is_person: bool =False) -> dict:
    per_period = {}
    for obj in objs:
        period = obj.period.period
        weekday = obj.period.weekday

        if period not in per_period:
            per_period[period] = [] if is_person else {}

        if not is_person and weekday not in per_period[period]:
            per_period[period][weekday] = []

        if is_person:
            per_period[period].append(obj)
        else:
            per_period[period][weekday].append(obj)
    return per_period

def build_timetable(
    type_: str, obj: Union[int, Person], date_ref: Union[CalendarWeek, date]
):
    if not isinstance(obj, int):
        pk = obj.pk
    else:
        pk = obj

    is_person = False
    if type_ == "person":
        is_person = True
        type_ = obj.timetable_type

Jonathan Weth's avatar
Jonathan Weth committed
    # Get matching holidays
    if is_person:
        holiday = Holiday.on_day(date_ref)
    else:
        holidays_per_weekday = Holiday.in_week(date_ref)

    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
        )
    lesson_periods_per_period = group_by_periods(lesson_periods, is_person=is_person)
    # Get events
    if is_person:
        extra_lessons = ExtraLesson.objects.on_day(date_ref).filter_from_person(obj)
    else:
        extra_lessons = ExtraLesson.objects.filter(week=date_ref.week).filter_from_type(type_, obj)
    # Sort lesson periods in a dict
    extra_lessons_per_period = group_by_periods(extra_lessons, is_person=is_person)
    # 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
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,
            }

            if not is_person:
                cols = []
                for weekday in range(
                    TimePeriod.weekday_min, TimePeriod.weekday_max + 1
                ):
                    col = []

                    # Add lesson periods
Jonathan Weth's avatar
Jonathan Weth 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
                    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]

Jonathan Weth's avatar
Jonathan Weth 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:
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"
    )

    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)

    # Get extra lessons
    extra_lessons = ExtraLesson.objects.on_day(wanted_day)

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

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

    rows.sort(key=sorter)

Jonathan Weth's avatar
Jonathan Weth committed


def build_weekdays(
    base: List[Tuple[int, str]], wanted_week: CalendarWeek
) -> List[dict]:
    holidays_per_weekday = Holiday.in_week(wanted_week)

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

        weekday = {
            "key": key,
            "name": name,
            "date": wanted_week[key],
            "holiday": holidays_per_weekday[key]
            if key in holidays_per_weekday
            else None,
        }
        weekdays.append(weekday)

    return weekdays