Skip to content
Snippets Groups Projects

Advanced data in timetable views

Merged Jonathan Weth requested to merge feature/advanced-data-timetable-views into master
Files
2
+ 305
0
from collections import OrderedDict
from datetime import date
from typing import Union, List, Tuple
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")
Holiday = apps.get_model("chronos", "Holiday")
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 holidays
if is_person:
holiday = Holiday.on_day(date_ref)
else:
holidays_per_weekday = Holiday.in_week(date_ref)
# 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
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)
row["cols"] = cols
else:
col = None
if period in supervisions_per_period_after and not holiday:
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
and weekday not in holidays_per_weekday
):
if weekday in lesson_periods_per_period[period]:
col += lesson_periods_per_period[period][weekday]
# Add events
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]
cols.append(col)
row["cols"] = cols
else:
col = []
# Add lesson periods
if period in lesson_periods_per_period and not holiday:
col += lesson_periods_per_period[period]
# Add events
if period in events_per_period and not holiday:
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
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
Loading