Skip to content
Snippets Groups Projects
Verified Commit 5a9e7d73 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Refactor generate_list_of_all_register_objects to multiple smaller functions

parent 8db232ff
No related branches found
No related tags found
1 merge request!152Overview of all register objects
from datetime import date
from operator import itemgetter from operator import itemgetter
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, Iterable, List, Optional, Sequence, Union
from django.db.models.expressions import Exists, OuterRef from django.db.models.expressions import Exists, OuterRef
from django.db.models.query import Prefetch, QuerySet from django.db.models.query import Prefetch, QuerySet
...@@ -12,15 +13,8 @@ from calendarweek import CalendarWeek ...@@ -12,15 +13,8 @@ from calendarweek import CalendarWeek
from aleksis.apps.alsijil.forms import FilterRegisterObjectForm from aleksis.apps.alsijil.forms import FilterRegisterObjectForm
from aleksis.apps.alsijil.models import LessonDocumentation from aleksis.apps.alsijil.models import LessonDocumentation
from aleksis.apps.chronos.models import ( from aleksis.apps.chronos.models import Event, ExtraLesson, Holiday, LessonPeriod
Event,
ExtraLesson,
Holiday,
LessonPeriod,
LessonSubstitution,
)
from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
from aleksis.core.models import SchoolTerm
def get_register_object_by_pk( def get_register_object_by_pk(
...@@ -112,72 +106,260 @@ def register_objects_sorter(register_object: Union[LessonPeriod, Event, ExtraLes ...@@ -112,72 +106,260 @@ def register_objects_sorter(register_object: Union[LessonPeriod, Event, ExtraLes
return 0 return 0
def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[dict]: def _filter_register_objects_by_dict(
# Get data for filtering filter_dict: Dict[str, Any],
register_objects: QuerySet[Union[LessonPeriod, Event, ExtraLesson]],
label_: str,
) -> QuerySet[Union[LessonPeriod, Event, ExtraLesson]]:
if label_ == LessonPeriod.label_:
register_objects = register_objects.filter(
lesson__validity__school_term=filter_dict.get("school_term")
)
else:
register_objects = register_objects.filter(school_term=filter_dict.get("school_term"))
register_objects = register_objects.distinct()
if (
filter_dict.get("date_start")
and filter_dict.get("date_end")
and label_ != LessonPeriod.label_
):
register_objects = register_objects.within_dates(
filter_dict.get("date_start"), filter_dict.get("date_end")
)
if filter_dict.get("person"):
if label_ == LessonPeriod.label_:
register_objects = register_objects.filter(
Q(lesson__teachers=filter_dict.get("person"))
| Q(substitutions__teachers=filter_dict.get("person"))
)
else:
register_objects = register_objects.filter_teacher(filter_dict.get("person"))
if filter_dict.get("group"):
register_objects = register_objects.filter_group(filter_dict.get("group"))
if filter_dict.get("groups"):
register_objects = register_objects.filter_groups(filter_dict.get("groups"))
if filter_dict.get("subject"):
if label_ == LessonPeriod.label_:
register_objects = register_objects.filter(
Q(lesson__subject=filter_dict.get("subject"))
| Q(substitutions__subject=filter_dict.get("subject"))
)
elif label_ == Event.label_:
# As events have no subject, we exclude them at all
register_objects = register_objects.none()
else:
register_objects = register_objects.filter(subject=filter_dict.get("subject"))
return register_objects
def _generate_dicts_for_lesson_periods(
filter_dict: Dict[str, Any],
lesson_periods: QuerySet[LessonPeriod],
documentations: Optional[Iterable[LessonDocumentation]] = None,
holiday_days: Optional[Sequence[date]] = None,
) -> List[Dict[str, Any]]:
"""Generate a list of dicts for use with ``RegisterObjectTable``."""
if not holiday_days:
holiday_days = []
date_start = lesson_periods.first().lesson.validity.date_start
date_end = lesson_periods.last().lesson.validity.date_end
if (
filter_dict["filter_date"]
and filter_dict.get("date_start") > date_start
and filter_dict.get("date_start") < date_end
):
date_start = filter_dict.get("date_start")
if (
filter_dict["filter_date"]
and filter_dict.get("date_end") < date_end
and filter_dict.get("date_end") > date_start
):
date_end = filter_dict.get("date_end")
weeks = CalendarWeek.weeks_within(date_start, date_end)
register_objects = []
for lesson_period in lesson_periods:
for week in weeks:
day = week[lesson_period.period.weekday]
# Skip all lesson periods in holidays
if day in holiday_days:
continue
# Ensure that the lesson period is in filter range and validity range
if (
lesson_period.lesson.validity.date_start
<= day
<= lesson_period.lesson.validity.date_end
) and (
not filter_dict.get("filter_date")
or (filter_dict.get("date_start") <= day <= filter_dict.get("date_end"))
):
sub = lesson_period.get_substitution()
# Skip lesson period if the person isn't a teacher
# or substitution teacher of this lesson period
if filter_dict.get("person") and (
filter_dict.get("person") not in lesson_period.lesson.teachers.all() and not sub
):
continue
teachers = (
sub.teacher_names
if sub and sub.teachers.all()
else lesson_period.lesson.teacher_names
)
if (
filter_dict.get("subject")
and filter_dict.get("subject") != lesson_period.get_subject()
):
continue
# Filter matching documentations and annotate if they exist
filtered_documentations = list(
filter(
lambda d: d.week == week.week
and d.year == week.year
and d.lesson_period_id == lesson_period.pk,
documentations
if documentations is not None
else lesson_period.documentations.all(),
)
)
has_documentation = bool(filtered_documentations)
if filter_dict.get(
"has_documentation"
) is not None and has_documentation != filter_dict.get("has_documentation"):
continue
# Build table entry
entry = {
"pk": f"lesson_period_{lesson_period.pk}_{week.year}_{week.week}",
"week": week,
"has_documentation": has_documentation,
"substitution": sub,
"register_object": lesson_period,
"date": date_format(day),
"date_sort": day,
"period": f"{lesson_period.period.period}.",
"period_sort": lesson_period.period.period,
"groups": lesson_period.lesson.group_names,
"teachers": teachers,
"subject": lesson_period.get_subject().name,
}
if has_documentation:
doc = filtered_documentations[0]
entry["topic"] = doc.topic
entry["homework"] = doc.homework
entry["group_note"] = doc.group_note
register_objects.append(entry)
return register_objects
def _generate_dicts_for_events_and_extra_lessons(
filter_dict: Dict[str, Any],
register_objects_start: Sequence[Union[Event, ExtraLesson]],
documentations: Optional[Iterable[LessonDocumentation]] = None,
) -> List[Dict[str, Any]]:
"""Generate a list of dicts for use with ``RegisterObjectTable``."""
register_objects = []
for register_object in register_objects_start:
filtered_documentations = list(
filter(
lambda d: getattr(d, f"{register_object.label_}_id") == register_object.pk,
documentations
if documentations is not None
else register_object.documentations.all(),
)
)
has_documentation = bool(filtered_documentations)
if filter_dict.get(
"has_documentation"
) is not None and has_documentation != filter_dict.get("has_documentation"):
continue
if isinstance(register_object, ExtraLesson):
day = date_format(register_object.day)
day_sort = register_object.day
period = f"{register_object.period.period}."
period_sort = register_object.period.period
else:
day = (
f"{date_format(register_object.date_start)}"
f"{date_format(register_object.date_end)}"
)
day_sort = register_object.date_start
period = f"{register_object.period_from.period}.–{register_object.period_to.period}."
period_sort = register_object.period_from.period
# Build table entry
entry = {
"pk": f"{register_object.label_}_{register_object.pk}",
"has_documentation": has_documentation,
"register_object": register_object,
"date": day,
"date_sort": day_sort,
"period": period,
"period_sort": period_sort,
"groups": register_object.group_names,
"teachers": register_object.teacher_names,
"subject": register_object.subject.name
if isinstance(register_object, ExtraLesson)
else _("Event"),
}
if has_documentation:
doc = filtered_documentations[0]
entry["topic"] = doc.topic
entry["homework"] = doc.homework
entry["group_note"] = doc.group_note
register_objects.append(entry)
return register_objects
def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Generate a list of all register objects.
This list can be filtered using ``filter_dict``. The following keys are supported:
- ``school_term`` (defaults to the current school term)
- ``date_start`` and ``date_end`` (defaults to the last thirty days)
- ``groups`` and/or ``groups``
- ``person``
- ``subject``
"""
# Always force a value for school term, start and end date so that queries won't get too big
initial_filter_data = FilterRegisterObjectForm.get_initial() initial_filter_data = FilterRegisterObjectForm.get_initial()
# Always force a selected school term so that queries won't get to big filter_dict["school_term"] = filter_dict.get("school_term", initial_filter_data["school_term"])
filter_school_term = filter_dict.get("school_term", SchoolTerm.current) filter_dict["date_start"] = filter_dict.get("date_start", initial_filter_data["date_start"])
filter_person = filter_dict.get("person") filter_dict["date_end"] = filter_dict.get("date_end", initial_filter_data["date_end"])
should_have_documentation = filter_dict.get("has_documentation") filter_dict["filter_date"] = bool(filter_dict.get("date_start")) and bool(
filter_group = filter_dict.get("group") filter_dict.get("date_end")
filter_groups = filter_dict.get("groups") )
filter_subject = filter_dict.get("subject")
filter_date_start = filter_dict.get("date_start", initial_filter_data.get("date_start"))
filter_date_end = filter_dict.get("date_end", initial_filter_data.get("date_end"))
filter_date = filter_date_start and filter_date_end
# Get all holidays in the selected school term to sort all data in holidays out # Get all holidays in the selected school term to sort all data in holidays out
holidays = Holiday.objects.within_dates( holidays = Holiday.objects.within_dates(
filter_school_term.date_start, filter_school_term.date_end filter_dict["school_term"].date_start, filter_dict["school_term"].date_end
) )
event_q = Q() holiday_days = holidays.get_all_days()
extra_lesson_q = Q()
holiday_days = [] lesson_periods = _filter_register_objects_by_dict(
for holiday in holidays: filter_dict,
event_q = event_q | Q(date_start__lte=holiday.date_end, date_end__gte=holiday.date_start) LessonPeriod.objects.order_by("lesson__validity__date_start"),
extra_lesson_q = extra_lesson_q | Q(day__gte=holiday.date_start, day__lte=holiday.date_end) LessonPeriod.label_,
holiday_days += list(holiday.get_days())
lesson_periods = (
LessonPeriod.objects.select_related("lesson")
.prefetch_related("lesson__teachers", "lesson__groups")
.filter(lesson__validity__school_term=filter_school_term)
.distinct()
.order_by("lesson__validity__school_term__date_start")
) )
events = Event.objects.filter(school_term=filter_school_term).exclude(event_q).distinct() events = _filter_register_objects_by_dict(
extra_lessons = ( filter_dict, Event.objects.exclude_holidays(holidays), Event.label_
ExtraLesson.objects.annotate_day() )
.filter(school_term=filter_school_term) extra_lessons = _filter_register_objects_by_dict(
.exclude(extra_lesson_q) filter_dict, ExtraLesson.objects.exclude_holidays(holidays), ExtraLesson.label_
.distinct()
) )
# Do filtering by date, by person, by group and by subject (if activated)
if filter_date:
events = events.within_dates(filter_date_start, filter_date_end)
extra_lessons = extra_lessons.filter(day__gte=filter_date_start, day__lte=filter_date_end)
if filter_person:
lesson_periods = lesson_periods.filter(
Q(lesson__teachers=filter_person) | Q(substitutions__teachers=filter_person)
)
events = events.filter_teacher(filter_person)
extra_lessons = extra_lessons.filter_teacher(filter_person)
if filter_group:
lesson_periods = lesson_periods.filter_group(filter_group)
events = events.filter_group(filter_group)
extra_lessons = extra_lessons.filter_group(filter_group)
if filter_groups:
lesson_periods = lesson_periods.filter_groups(filter_groups)
events = events.filter_groups(filter_groups)
extra_lessons = extra_lessons.filter_groups(filter_groups)
if filter_subject:
lesson_periods = lesson_periods.filter(
Q(lesson__subject=filter_subject) | Q(substitutions__subject=filter_subject)
)
# As events have no subject, we exclude them at all
events = []
extra_lessons = extra_lessons.filter(subject=filter_subject)
# Prefetch documentations for all register objects and substitutions for all lesson periods # Prefetch documentations for all register objects and substitutions for all lesson periods
# in order to prevent extra queries # in order to prevent extra queries
...@@ -186,151 +368,14 @@ def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[d ...@@ -186,151 +368,14 @@ def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[d
| Q(extra_lesson__in=extra_lessons) | Q(extra_lesson__in=extra_lessons)
| Q(lesson_period__in=lesson_periods) | Q(lesson_period__in=lesson_periods)
) )
substitutions = LessonSubstitution.objects.filter(lesson_period__in=lesson_periods)
if filter_person:
substitutions = substitutions.filter(teachers=filter_person)
if lesson_periods: if lesson_periods:
# Get date range for which lesson periods should be added register_objects = _generate_dicts_for_lesson_periods(
date_start = lesson_periods.first().lesson.validity.school_term.date_start filter_dict, lesson_periods, documentations, holiday_days
date_end = lesson_periods.last().lesson.validity.school_term.date_end )
if filter_date and filter_date_start > date_start and filter_date_start < date_end: register_objects += _generate_dicts_for_events_and_extra_lessons(
date_start = filter_date_start filter_dict, list(events) + list(extra_lessons), documentations
if filter_date and filter_date_end < date_end and filter_date_start > date_start: )
date_end = filter_date_end
print(date_start, date_end)
weeks = CalendarWeek.weeks_within(date_start, date_end)
register_objects = []
for lesson_period in lesson_periods:
for week in weeks:
day = week[lesson_period.period.weekday]
# Skip all lesson periods in holidays
if day in holiday_days:
continue
# Ensure that the lesson period is in filter range and validity range
if (
lesson_period.lesson.validity.date_start
<= day
<= lesson_period.lesson.validity.date_end
) and (not filter_date or (filter_date_start <= day <= filter_date_end)):
filtered_substitutions = list(
filter(lambda s: s.lesson_period_id == lesson_period.id, substitutions)
)
# Skip lesson period if the person isn't a teacher
# or substitution teacher of this lesson period
if filter_person and (
filter_person not in lesson_period.lesson.teachers.all()
and not filtered_substitutions
):
continue
# Annotate substitution to lesson period
sub = filtered_substitutions[0] if filtered_substitutions else None
subject = sub.subject if sub and sub.subject else lesson_period.lesson.subject
teachers = (
sub.teacher_names
if sub and sub.teachers.all()
else lesson_period.lesson.teacher_names
)
if filter_subject and filter_subject != subject:
continue
# Filter matching documentations and annotate if they exist
filtered_documentations = list(
filter(
lambda d: d.week == week.week
and d.year == week.year
and d.lesson_period_id == lesson_period.pk,
documentations,
)
)
has_documentation = bool(filtered_documentations)
if (
should_have_documentation is not None
and has_documentation != should_have_documentation
):
continue
# Build table entry
entry = {
"pk": f"lesson_period_{lesson_period.pk}_{week.year}_{week.week}",
"week": week,
"has_documentation": has_documentation,
"substitution": sub,
"register_object": lesson_period,
"date": date_format(day),
"date_sort": day,
"period": f"{lesson_period.period.period}.",
"period_sort": lesson_period.period.period,
"groups": lesson_period.lesson.group_names,
"teachers": teachers,
"subject": subject.name,
}
if has_documentation:
doc = filtered_documentations[0]
entry["topic"] = doc.topic
entry["homework"] = doc.homework
entry["group_note"] = doc.group_note
register_objects.append(entry)
for register_object in list(extra_lessons) + list(events):
filtered_documentations = list(
filter(
lambda d: getattr(d, f"{register_object.label_}_id") == register_object.pk,
documentations,
)
)
has_documentation = bool(filtered_documentations)
if (
should_have_documentation is not None
and has_documentation != should_have_documentation
):
continue
if isinstance(register_object, ExtraLesson):
day = date_format(register_object.day)
day_sort = register_object.day
period = f"{register_object.period.period}."
period_sort = register_object.period.period
else:
day = (
f"{date_format(register_object.date_start)}"
f"{date_format(register_object.date_end)}"
)
day_sort = register_object.date_start
period = (
f"{register_object.period_from.period}.–{register_object.period_to.period}."
)
period_sort = register_object.period_from.period
# Build table entry
entry = {
"pk": f"{register_object.label_}_{register_object.pk}",
"has_documentation": has_documentation,
"register_object": register_object,
"date": day,
"date_sort": day_sort,
"period": period,
"period_sort": period_sort,
"groups": register_object.group_names,
"teachers": register_object.teacher_names,
"subject": register_object.subject.name
if isinstance(register_object, ExtraLesson)
else _("Event"),
}
if has_documentation:
doc = filtered_documentations[0]
entry["topic"] = doc.topic
entry["homework"] = doc.homework
entry["group_note"] = doc.group_note
register_objects.append(entry)
# Sort table entries by date and period and configure table # Sort table entries by date and period and configure table
register_objects = sorted(register_objects, key=itemgetter("date_sort", "period_sort")) register_objects = sorted(register_objects, key=itemgetter("date_sort", "period_sort"))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment