diff --git a/aleksis/apps/chronos/managers.py b/aleksis/apps/chronos/managers.py
new file mode 100644
index 0000000000000000000000000000000000000000..dfeb9383695341574dd9c197c988226327c01601
--- /dev/null
+++ b/aleksis/apps/chronos/managers.py
@@ -0,0 +1,457 @@
+class LessonPeriodManager(models.Manager):
+    """ Manager adding specific methods to lesson periods. """
+
+    def get_queryset(self):
+        """ Ensures all related lesson data is loaded as well. """
+
+        return (
+            super()
+            .get_queryset()
+            .select_related("lesson", "lesson__subject", "period", "room")
+            .prefetch_related("lesson__groups", "lesson__teachers", "substitutions")
+        )
+
+
+class LessonSubstitutionManager(models.Manager):
+    """ Manager adding specific methods to lesson substitutions. """
+
+    def get_queryset(self):
+        """ Ensures all related lesson data is loaded as well. """
+
+        return (
+            super()
+            .get_queryset()
+            .select_related(
+                "lesson_period",
+                "lesson_period__lesson",
+                "subject",
+                "lesson_period__period",
+                "room",
+            )
+            .prefetch_related("lesson_period__lesson__groups", "teachers")
+        )
+
+
+class LessonDataQuerySet(models.QuerySet):
+    """ Overrides default QuerySet to add specific methods for lesson data. """
+
+    # Overridden in the subclasses. Swaps the paths to the base lesson period
+    # and to any substitutions depending on whether the query is run on a
+    # lesson period or a substitution
+    _period_path = None
+    _subst_path = None
+
+    def within_dates(self, start: date, end: date):
+        """ Filter for all lessons within a date range. """
+
+        return self.filter(
+            **{
+                self._period_path + "lesson__date_start__lte": start,
+                self._period_path + "lesson__date_end__gte": end,
+            }
+        )
+
+    def in_week(self, wanted_week: CalendarWeek):
+        """ Filter for all lessons within a calendar week. """
+
+        return self.within_dates(
+            wanted_week[0] + timedelta(days=1) * (F(self._period_path + "period__weekday") - 1),
+            wanted_week[0] + timedelta(days=1) * (F(self._period_path + "period__weekday") - 1),
+        ).annotate_week(wanted_week)
+
+    def on_day(self, day: date):
+        """ Filter for all lessons on a certain day. """
+
+        week, weekday = week_weekday_from_date(day)
+
+        return (
+            self.within_dates(day, day)
+            .filter(**{self._period_path + "period__weekday": weekday})
+            .annotate_week(week)
+        )
+
+    def at_time(self, when: Optional[datetime] = None):
+        """ Filter for the lessons taking place at a certain point in time. """
+
+        now = when or datetime.now()
+        week, weekday = week_weekday_from_date(now.date())
+
+        return self.filter(
+            **{
+                self._period_path + "lesson__date_start__lte": now.date(),
+                self._period_path + "lesson__date_end__gte": now.date(),
+                self._period_path + "period__weekday": now.weekday(),
+                self._period_path + "period__time_start__lte": now.time(),
+                self._period_path + "period__time_end__gte": now.time(),
+            }
+        ).annotate_week(week)
+
+    def filter_participant(self, person: Union[Person, int]):
+        """ Filter for all lessons a participant (student) attends. """
+
+        return self.filter(
+            Q(**{self._period_path + "lesson__groups__members": person})
+            | Q(**{self._period_path + "lesson__groups__parent_groups__members": person})
+        )
+
+    def filter_group(self, group: Union[Group, int]):
+        """ Filter for all lessons a group (class) regularly attends. """
+
+        if isinstance(group, int):
+            group = Group.objects.get(pk=group)
+
+        if group.parent_groups.all():
+            # Prevent to show lessons multiple times
+            return self.filter(Q(**{self._period_path + "lesson__groups": group}))
+        else:
+            return self.filter(
+                Q(**{self._period_path + "lesson__groups": group})
+                | Q(**{self._period_path + "lesson__groups__parent_groups": group})
+            )
+
+    def filter_teacher(self, teacher: Union[Person, int]):
+        """ Filter for all lessons given by a certain teacher. """
+
+        qs1 = self.filter(**{self._period_path + "lesson__teachers": teacher})
+        qs2 = self.filter(**{self._subst_path + "teachers": teacher, self._subst_path + "week": F("_week"), })
+
+        return qs1.union(qs2)
+
+    def filter_room(self, room: Union[Room, int]):
+        """ Filter for all lessons taking part in a certain room. """
+
+        qs1 = self.filter(**{self._period_path + "room": room})
+        qs2 = self.filter(**{self._subst_path + "room": room, self._subst_path + "week": F("_week"),})
+
+        return qs1.union(qs2)
+
+    def annotate_week(self, week: Union[CalendarWeek, int]):
+        """ Annotate all lessons in the QuerySet with the number of the provided calendar week. """
+
+        if isinstance(week, CalendarWeek):
+            week_num = week.week
+        else:
+            week_num = week
+
+        return self.annotate(_week=models.Value(week_num, models.IntegerField()))
+
+    def group_by_periods(self, is_person: bool = False) -> dict:
+        per_period = {}
+        for obj in self:
+            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
+
+
+class LessonPeriodQuerySet(LessonDataQuerySet):
+    _period_path = ""
+    _subst_path = "substitutions__"
+
+    def next(self, reference: LessonPeriod, offset: Optional[int] = 1) -> LessonPeriod:
+        """ Get another lesson in an ordered set of lessons.
+
+        By default, it returns the next lesson in the set. By passing the offset argument,
+        the n-th next lesson can be selected. By passing a negative number, the n-th
+        previous lesson can be selected.
+        """
+
+        index = list(self.values_list("id", flat=True)).index(reference.id)
+
+        next_index = index + offset
+        if next_index > self.count() - 1:
+            next_index %= self.count()
+            week = reference._week + 1
+        else:
+            week = reference._week
+
+        return self.annotate_week(week).all()[next_index]
+
+    def filter_from_query(self, query_data: QueryDict) -> models.QuerySet:
+        """ Apply all filters from a GET or POST query.
+
+        This method expects a QueryDict, like the GET or POST attribute of a Request
+        object, that contains one or more of the keys group, teacher or room.
+
+        All three fields are filtered, in order.
+        """
+
+        if query_data.get("group", None):
+            return self.filter_group(int(query_data["group"]))
+        if query_data.get("teacher", None):
+            return self.filter_teacher(int(query_data["teacher"]))
+        if query_data.get("room", None):
+            return self.filter_room(int(query_data["room"]))
+
+    def filter_from_type(self, type_: TimetableType, pk: int) -> Optional[models.QuerySet]:
+        if type_ == TimetableType.GROUP:
+            return self.filter_group(pk)
+        elif type_ == TimetableType.TEACHER:
+            return self.filter_teacher(pk)
+        elif type_ == TimetableType.ROOM:
+            return self.filter_room(pk)
+        else:
+            return None
+
+    def filter_from_person(self, person: Person) -> Optional[models.QuerySet]:
+        type_ = person.timetable_type
+
+        if type_ == TimetableType.TEACHER:
+            # Teacher
+
+            return self.filter_teacher(person)
+
+        elif type_ == TimetableType.GROUP:
+            # Student
+
+            return self.filter(lesson__groups__members=person)
+
+        else:
+            # If no student or teacher
+            return None
+
+    def daily_lessons_for_person(self, person: Person, wanted_day: date) -> Optional[models.QuerySet]:
+        if person.timetable_type is None:
+            return None
+
+        lesson_periods = self.on_day(wanted_day).filter_from_person(person)
+
+        return lesson_periods
+
+    def per_period_one_day(self) -> OrderedDict:
+        """ Group selected lessons per period for one day """
+        per_period = {}
+        for lesson_period in self:
+            if lesson_period.period.period in per_period:
+                per_period[lesson_period.period.period].append(lesson_period)
+            else:
+                per_period[lesson_period.period.period] = [lesson_period]
+        return OrderedDict(sorted(per_period.items()))
+
+
+class LessonSubstitutionQuerySet(LessonDataQuerySet):
+    _period_path = "lesson_period__"
+    _subst_path = ""
+
+    def affected_lessons(self):
+        """ Return all lessons which are affected by selected substitutions """
+
+        return Lesson.objects.filter(lesson_periods__substitutions__in=self)
+
+    def affected_teachers(self):
+        """ Return all teachers which are affected by selected substitutions (as substituted or substituting) """
+
+        return Person.objects.filter(
+            Q(lessons_as_teacher__in=self.affected_lessons())
+            | Q(lesson_substitutions__in=self)
+        ).annotate(lessons_count=Count("lessons_as_teacher"))
+
+    def affected_groups(self):
+        """ Return all groups which are affected by selected substitutions """
+
+        return Group.objects.filter(lessons__in=self.affected_lessons()).annotate(
+            lessons_count=Count("lessons")
+        )
+
+
+class DateRangeQuerySet(models.QuerySet):
+    def within_dates(self, start: date, end: date):
+        """ Filter for all events within a date range. """
+
+        return self.filter(date_start__lte=end, date_end__gte=start)
+
+    def in_week(self, wanted_week: CalendarWeek):
+        """ Filter for all events within a calendar week. """
+
+        return self.within_dates(wanted_week[0], wanted_week[6])
+
+    def on_day(self, day: date):
+        """ Filter for all events on a certain day. """
+
+        return self.within_dates(day, day)
+
+    def at_time(self, when: Optional[datetime] = None):
+        """ Filter for the events taking place at a certain point in time. """
+
+        now = when or datetime.now()
+
+        return self.on_day(now.date()).filter(
+            period_from__time_start__lte=now.time(),
+            period_to__time_end__gte=now.time()
+        )
+
+
+class AbsenceQuerySet(DateRangeQuerySet):
+    def absent_teachers(self):
+        return Person.objects.filter(absences__in=self).annotate(absences_count=Count("absences"))
+
+    def absent_groups(self):
+        return Group.objects.filter(absences__in=self).annotate(absences_count=Count("absences"))
+
+    def absent_rooms(self):
+        return Person.objects.filter(absences__in=self).annotate(absences_count=Count("absences"))
+
+
+class HolidayQuerySet(DateRangeQuerySet):
+    pass
+
+class SupervisionQuerySet(models.QuerySet):
+    def annotate_week(self, week: Union[CalendarWeek, int]):
+        """ Annotate all lessons in the QuerySet with the number of the provided calendar week. """
+
+        if isinstance(week, CalendarWeek):
+            week_num = week.week
+        else:
+            week_num = week
+
+        return self.annotate(_week=models.Value(week_num, models.IntegerField()))
+
+    def filter_by_weekday(self, weekday: int):
+        self.filter(
+            Q(break_item__before_period__weekday=weekday)
+            | Q(break_item__after_period__weekday=weekday)
+        )
+
+    def filter_by_teacher(self, teacher: Union[Person, int]):
+        """ Filter for all supervisions given by a certain teacher. """
+
+        if self.count() > 0:
+            if hasattr(self[0], "_week"):
+                week = CalendarWeek(week=self[0]._week)
+            else:
+                week = CalendarWeek.current_week()
+
+            dates = [week[w] for w in range(0, 7)]
+
+            return self.filter(Q(substitutions__teacher=teacher, substitutions__date__in=dates) | Q(teacher=teacher))
+
+        return self
+
+
+class TimetableQuerySet(models.QuerySet):
+    """ Common filters
+
+     Models need following fields:
+     - groups
+     - teachers
+     - rooms (_multiple_rooms=True)/room (_multiple_rooms=False)
+     """
+
+    _multiple_rooms = True
+
+    def filter_participant(self, person: Union[Person, int]):
+        """ Filter for all objects a participant (student) attends. """
+
+        return self.filter(Q(groups_members=person))
+
+    def filter_group(self, group: Union[Group, int]):
+        """ Filter for all objects a group (class) attends. """
+
+        if isinstance(group, int):
+            group = Group.objects.get(pk=group)
+
+        if group.parent_groups.all():
+            # Prevent to show lessons multiple times
+            return self.filter(groups=group)
+        else:
+            return self.filter(Q(groups=group) | Q(groups__parent_groups=group))
+
+    def filter_teacher(self, teacher: Union[Person, int]):
+        """ Filter for all lessons given by a certain teacher. """
+
+        return self.filter(teachers=teacher)
+
+    def filter_room(self, room: Union[Room, int]):
+        """ Filter for all objects taking part in a certain room. """
+
+        if self._multiple_rooms:
+            return self.filter(rooms=room)
+        else:
+            return self.filter(room=room)
+
+    def filter_from_type(self, type_: TimetableType, pk: int) -> Optional[models.QuerySet]:
+        if type_ == TimetableType.GROUP:
+            return self.filter_group(pk)
+        elif type_ == TimetableType.TEACHER:
+            return self.filter_teacher(pk)
+        elif type_ == TimetableType.ROOM:
+            return self.filter_room(pk)
+        else:
+            return None
+
+    def filter_from_person(self, person: Person) -> Optional[models.QuerySet]:
+        type_ = person.timetable_type
+
+        if type_ == TimetableType.TEACHER:
+            # Teacher
+
+            return self.filter_teacher(person)
+
+        elif type_ == TimetableType.GROUP:
+            # Student
+
+            return self.filter_participant(person)
+
+        else:
+            # If no student or teacher
+            return None
+
+
+class EventQuerySet(DateRangeQuerySet, TimetableQuerySet):
+    def annotate_day(self, day: date):
+        """ Annotate all events in the QuerySet with the provided date. """
+
+        return self.annotate(_date=models.Value(day, models.DateField()))
+
+
+class ExtraLessonQuerySet(TimetableQuerySet):
+    _multiple_rooms = False
+
+    def within_dates(self, start: date, end: date):
+        week_start = CalendarWeek.from_date(start)
+        week_end = CalendarWeek.from_date(end)
+
+        return self.filter(
+            week__gte=week_start.week,
+            week__lte=week_end.week,
+            period__weekday__gte=start.weekday(),
+            period__weekday__lte=end.weekday(),
+        )
+
+    def on_day(self, day:date):
+        return self.within_dates(day, day)
+
+
+class GroupPropertiesMixin:
+    @property
+    def group_names(self, sep: Optional[str] = ", ") -> str:
+        return sep.join([group.short_name for group in self.groups.all()])
+
+    @property
+    def groups_to_show(self) -> models.QuerySet:
+        groups = self.groups.all()
+        if groups.count() == 1 and groups[0].parent_groups.all() and get_site_preferences()["chronos__use_parent_groups"]:
+            return groups[0].parent_groups.all()
+        else:
+            return groups
+
+    @property
+    def groups_to_show_names(self, sep: Optional[str] = ", ") -> str:
+        return sep.join([group.short_name for group in self.groups_to_show])
+
+
+class TeacherPropertiesMixin:
+    @property
+    def teacher_names(self, sep: Optional[str] = ", ") -> str:
+        return sep.join([teacher.full_name for teacher in self.teachers.all()])
diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index 572e2bbdb484edd28368494364a674635cf15150..f3872c633f5faff676e1a7cbba009da871fa76c0 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -41,273 +41,6 @@ class TimetableType(Enum):
         return cls.__members__.get(s.upper())
 
 
-class LessonPeriodManager(models.Manager):
-    """ Manager adding specific methods to lesson periods. """
-
-    def get_queryset(self):
-        """ Ensures all related lesson data is loaded as well. """
-
-        return (
-            super()
-            .get_queryset()
-            .select_related("lesson", "lesson__subject", "period", "room")
-            .prefetch_related("lesson__groups", "lesson__teachers", "substitutions")
-        )
-
-
-class LessonSubstitutionManager(models.Manager):
-    """ Manager adding specific methods to lesson substitutions. """
-
-    def get_queryset(self):
-        """ Ensures all related lesson data is loaded as well. """
-
-        return (
-            super()
-            .get_queryset()
-            .select_related(
-                "lesson_period",
-                "lesson_period__lesson",
-                "subject",
-                "lesson_period__period",
-                "room",
-            )
-            .prefetch_related("lesson_period__lesson__groups", "teachers")
-        )
-
-
-class LessonDataQuerySet(models.QuerySet):
-    """ Overrides default QuerySet to add specific methods for lesson data. """
-
-    # Overridden in the subclasses. Swaps the paths to the base lesson period
-    # and to any substitutions depending on whether the query is run on a
-    # lesson period or a substitution
-    _period_path = None
-    _subst_path = None
-
-    def within_dates(self, start: date, end: date):
-        """ Filter for all lessons within a date range. """
-
-        return self.filter(
-            **{
-                self._period_path + "lesson__date_start__lte": start,
-                self._period_path + "lesson__date_end__gte": end,
-            }
-        )
-
-    def in_week(self, wanted_week: CalendarWeek):
-        """ Filter for all lessons within a calendar week. """
-
-        return self.within_dates(
-            wanted_week[0] + timedelta(days=1) * (F(self._period_path + "period__weekday") - 1),
-            wanted_week[0] + timedelta(days=1) * (F(self._period_path + "period__weekday") - 1),
-        ).annotate_week(wanted_week)
-
-    def on_day(self, day: date):
-        """ Filter for all lessons on a certain day. """
-
-        week, weekday = week_weekday_from_date(day)
-
-        return (
-            self.within_dates(day, day)
-            .filter(**{self._period_path + "period__weekday": weekday})
-            .annotate_week(week)
-        )
-
-    def at_time(self, when: Optional[datetime] = None):
-        """ Filter for the lessons taking place at a certain point in time. """
-
-        now = when or datetime.now()
-        week, weekday = week_weekday_from_date(now.date())
-
-        return self.filter(
-            **{
-                self._period_path + "lesson__date_start__lte": now.date(),
-                self._period_path + "lesson__date_end__gte": now.date(),
-                self._period_path + "period__weekday": now.weekday(),
-                self._period_path + "period__time_start__lte": now.time(),
-                self._period_path + "period__time_end__gte": now.time(),
-            }
-        ).annotate_week(week)
-
-    def filter_participant(self, person: Union[Person, int]):
-        """ Filter for all lessons a participant (student) attends. """
-
-        return self.filter(
-            Q(**{self._period_path + "lesson__groups__members": person})
-            | Q(**{self._period_path + "lesson__groups__parent_groups__members": person})
-        )
-
-    def filter_group(self, group: Union[Group, int]):
-        """ Filter for all lessons a group (class) regularly attends. """
-
-        if isinstance(group, int):
-            group = Group.objects.get(pk=group)
-
-        if group.parent_groups.all():
-            # Prevent to show lessons multiple times
-            return self.filter(Q(**{self._period_path + "lesson__groups": group}))
-        else:
-            return self.filter(
-                Q(**{self._period_path + "lesson__groups": group})
-                | Q(**{self._period_path + "lesson__groups__parent_groups": group})
-            )
-
-    def filter_teacher(self, teacher: Union[Person, int]):
-        """ Filter for all lessons given by a certain teacher. """
-
-        qs1 = self.filter(**{self._period_path + "lesson__teachers": teacher})
-        qs2 = self.filter(**{self._subst_path + "teachers": teacher, self._subst_path + "week": F("_week"), })
-
-        return qs1.union(qs2)
-
-    def filter_room(self, room: Union[Room, int]):
-        """ Filter for all lessons taking part in a certain room. """
-
-        qs1 = self.filter(**{self._period_path + "room": room})
-        qs2 = self.filter(**{self._subst_path + "room": room, self._subst_path + "week": F("_week"),})
-
-        return qs1.union(qs2)
-
-    def annotate_week(self, week: Union[CalendarWeek, int]):
-        """ Annotate all lessons in the QuerySet with the number of the provided calendar week. """
-
-        if isinstance(week, CalendarWeek):
-            week_num = week.week
-        else:
-            week_num = week
-
-        return self.annotate(_week=models.Value(week_num, models.IntegerField()))
-
-    def group_by_periods(self, is_person: bool = False) -> dict:
-        per_period = {}
-        for obj in self:
-            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
-
-
-class LessonPeriodQuerySet(LessonDataQuerySet):
-    _period_path = ""
-    _subst_path = "substitutions__"
-
-    def next(self, reference: LessonPeriod, offset: Optional[int] = 1) -> LessonPeriod:
-        """ Get another lesson in an ordered set of lessons.
-
-        By default, it returns the next lesson in the set. By passing the offset argument,
-        the n-th next lesson can be selected. By passing a negative number, the n-th
-        previous lesson can be selected.
-        """
-
-        index = list(self.values_list("id", flat=True)).index(reference.id)
-
-        next_index = index + offset
-        if next_index > self.count() - 1:
-            next_index %= self.count()
-            week = reference._week + 1
-        else:
-            week = reference._week
-
-        return self.annotate_week(week).all()[next_index]
-
-    def filter_from_query(self, query_data: QueryDict) -> models.QuerySet:
-        """ Apply all filters from a GET or POST query.
-
-        This method expects a QueryDict, like the GET or POST attribute of a Request
-        object, that contains one or more of the keys group, teacher or room.
-
-        All three fields are filtered, in order.
-        """
-
-        if query_data.get("group", None):
-            return self.filter_group(int(query_data["group"]))
-        if query_data.get("teacher", None):
-            return self.filter_teacher(int(query_data["teacher"]))
-        if query_data.get("room", None):
-            return self.filter_room(int(query_data["room"]))
-
-    def filter_from_type(self, type_: TimetableType, pk: int) -> Optional[models.QuerySet]:
-        if type_ == TimetableType.GROUP:
-            return self.filter_group(pk)
-        elif type_ == TimetableType.TEACHER:
-            return self.filter_teacher(pk)
-        elif type_ == TimetableType.ROOM:
-            return self.filter_room(pk)
-        else:
-            return None
-
-    def filter_from_person(self, person: Person) -> Optional[models.QuerySet]:
-        type_ = person.timetable_type
-
-        if type_ == TimetableType.TEACHER:
-            # Teacher
-
-            return self.filter_teacher(person)
-
-        elif type_ == TimetableType.GROUP:
-            # Student
-
-            return self.filter(lesson__groups__members=person)
-
-        else:
-            # If no student or teacher
-            return None
-
-    def daily_lessons_for_person(self, person: Person, wanted_day: date) -> Optional[models.QuerySet]:
-        if person.timetable_type is None:
-            return None
-
-        lesson_periods = self.on_day(wanted_day).filter_from_person(person)
-
-        return lesson_periods
-
-    def per_period_one_day(self) -> OrderedDict:
-        """ Group selected lessons per period for one day """
-        per_period = {}
-        for lesson_period in self:
-            if lesson_period.period.period in per_period:
-                per_period[lesson_period.period.period].append(lesson_period)
-            else:
-                per_period[lesson_period.period.period] = [lesson_period]
-        return OrderedDict(sorted(per_period.items()))
-
-
-class LessonSubstitutionQuerySet(LessonDataQuerySet):
-    _period_path = "lesson_period__"
-    _subst_path = ""
-
-    def affected_lessons(self):
-        """ Return all lessons which are affected by selected substitutions """
-
-        return Lesson.objects.filter(lesson_periods__substitutions__in=self)
-
-    def affected_teachers(self):
-        """ Return all teachers which are affected by selected substitutions (as substituted or substituting) """
-
-        return Person.objects.filter(
-            Q(lessons_as_teacher__in=self.affected_lessons())
-            | Q(lesson_substitutions__in=self)
-        ).annotate(lessons_count=Count("lessons_as_teacher"))
-
-    def affected_groups(self):
-        """ Return all groups which are affected by selected substitutions """
-
-        return Group.objects.filter(lessons__in=self.affected_lessons()).annotate(
-            lessons_count=Count("lessons")
-        )
-
-
 class TimePeriod(ExtensibleModel):
     WEEKDAY_CHOICES = list(enumerate(i18n_day_names_lazy()))
     WEEKDAY_CHOICES_SHORT = list(enumerate(i18n_day_abbrs_lazy()))
@@ -447,28 +180,6 @@ class Room(ExtensibleModel):
         verbose_name_plural = _("Rooms")
 
 
-class GroupPropertiesMixin:
-    @property
-    def group_names(self, sep: Optional[str] = ", ") -> str:
-        return sep.join([group.short_name for group in self.groups.all()])
-
-    @property
-    def groups_to_show(self) -> models.QuerySet:
-        groups = self.groups.all()
-        if groups.count() == 1 and groups[0].parent_groups.all() and get_site_preferences()["chronos__use_parent_groups"]:
-            return groups[0].parent_groups.all()
-        else:
-            return groups
-
-    @property
-    def groups_to_show_names(self, sep: Optional[str] = ", ") -> str:
-        return sep.join([group.short_name for group in self.groups_to_show])
-
-
-class TeacherPropertiesMixin:
-    @property
-    def teacher_names(self, sep: Optional[str] = ", ") -> str:
-        return sep.join([teacher.full_name for teacher in self.teachers.all()])
 
 
 class Lesson(ExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin):
@@ -656,33 +367,6 @@ class TimetableWidget(DashboardWidget):
         verbose_name_plural = _("Timetable widgets")
 
 
-class DateRangeQuerySet(models.QuerySet):
-    def within_dates(self, start: date, end: date):
-        """ Filter for all events within a date range. """
-
-        return self.filter(date_start__lte=end, date_end__gte=start)
-
-    def in_week(self, wanted_week: CalendarWeek):
-        """ Filter for all events within a calendar week. """
-
-        return self.within_dates(wanted_week[0], wanted_week[6])
-
-    def on_day(self, day: date):
-        """ Filter for all events on a certain day. """
-
-        return self.within_dates(day, day)
-
-    def at_time(self, when: Optional[datetime] = None):
-        """ Filter for the events taking place at a certain point in time. """
-
-        now = when or datetime.now()
-
-        return self.on_day(now.date()).filter(
-            period_from__time_start__lte=now.time(),
-            period_to__time_end__gte=now.time()
-        )
-
-
 class AbsenceReason(ExtensibleModel):
     short_name = models.CharField(verbose_name=_("Short name"), max_length=255)
     name = models.CharField(verbose_name=_("Name"), blank=True, null=True, max_length=255)
@@ -698,17 +382,6 @@ class AbsenceReason(ExtensibleModel):
         verbose_name_plural = _("Absence reasons")
 
 
-class AbsenceQuerySet(DateRangeQuerySet):
-    def absent_teachers(self):
-        return Person.objects.filter(absences__in=self).annotate(absences_count=Count("absences"))
-
-    def absent_groups(self):
-        return Group.objects.filter(absences__in=self).annotate(absences_count=Count("absences"))
-
-    def absent_rooms(self):
-        return Person.objects.filter(absences__in=self).annotate(absences_count=Count("absences"))
-
-
 class Absence(ExtensibleModel):
     objects = models.Manager.from_queryset(AbsenceQuerySet)()
 
@@ -758,10 +431,6 @@ class Exam(ExtensibleModel):
         verbose_name_plural = _("Exams")
 
 
-class HolidayQuerySet(DateRangeQuerySet):
-    pass
-
-
 class Holiday(ExtensibleModel):
     objects = models.Manager.from_queryset(HolidayQuerySet)()
 
@@ -876,39 +545,6 @@ class Break(ExtensibleModel):
         verbose_name_plural = _("Breaks")
 
 
-class SupervisionQuerySet(models.QuerySet):
-    def annotate_week(self, week: Union[CalendarWeek, int]):
-        """ Annotate all lessons in the QuerySet with the number of the provided calendar week. """
-
-        if isinstance(week, CalendarWeek):
-            week_num = week.week
-        else:
-            week_num = week
-
-        return self.annotate(_week=models.Value(week_num, models.IntegerField()))
-
-    def filter_by_weekday(self, weekday: int):
-        self.filter(
-            Q(break_item__before_period__weekday=weekday)
-            | Q(break_item__after_period__weekday=weekday)
-        )
-
-    def filter_by_teacher(self, teacher: Union[Person, int]):
-        """ Filter for all supervisions given by a certain teacher. """
-
-        if self.count() > 0:
-            if hasattr(self[0], "_week"):
-                week = CalendarWeek(week=self[0]._week)
-            else:
-                week = CalendarWeek.current_week()
-
-            dates = [week[w] for w in range(0, 7)]
-
-            return self.filter(Q(substitutions__teacher=teacher, substitutions__date__in=dates) | Q(teacher=teacher))
-
-        return self
-
-
 class Supervision(ExtensibleModel):
     objects = models.Manager.from_queryset(SupervisionQuerySet)()
 
@@ -961,82 +597,6 @@ class SupervisionSubstitution(ExtensibleModel):
         verbose_name_plural = _("Supervision substitutions")
 
 
-class TimetableQuerySet(models.QuerySet):
-    """ Common filters
-
-     Models need following fields:
-     - groups
-     - teachers
-     - rooms (_multiple_rooms=True)/room (_multiple_rooms=False)
-     """
-
-    _multiple_rooms = True
-
-    def filter_participant(self, person: Union[Person, int]):
-        """ Filter for all objects a participant (student) attends. """
-
-        return self.filter(Q(groups_members=person))
-
-    def filter_group(self, group: Union[Group, int]):
-        """ Filter for all objects a group (class) attends. """
-
-        if isinstance(group, int):
-            group = Group.objects.get(pk=group)
-
-        if group.parent_groups.all():
-            # Prevent to show lessons multiple times
-            return self.filter(groups=group)
-        else:
-            return self.filter(Q(groups=group) | Q(groups__parent_groups=group))
-
-    def filter_teacher(self, teacher: Union[Person, int]):
-        """ Filter for all lessons given by a certain teacher. """
-
-        return self.filter(teachers=teacher)
-
-    def filter_room(self, room: Union[Room, int]):
-        """ Filter for all objects taking part in a certain room. """
-
-        if self._multiple_rooms:
-            return self.filter(rooms=room)
-        else:
-            return self.filter(room=room)
-
-    def filter_from_type(self, type_: TimetableType, pk: int) -> Optional[models.QuerySet]:
-        if type_ == TimetableType.GROUP:
-            return self.filter_group(pk)
-        elif type_ == TimetableType.TEACHER:
-            return self.filter_teacher(pk)
-        elif type_ == TimetableType.ROOM:
-            return self.filter_room(pk)
-        else:
-            return None
-
-    def filter_from_person(self, person: Person) -> Optional[models.QuerySet]:
-        type_ = person.timetable_type
-
-        if type_ == TimetableType.TEACHER:
-            # Teacher
-
-            return self.filter_teacher(person)
-
-        elif type_ == TimetableType.GROUP:
-            # Student
-
-            return self.filter_participant(person)
-
-        else:
-            # If no student or teacher
-            return None
-
-
-class EventQuerySet(DateRangeQuerySet, TimetableQuerySet):
-    def annotate_day(self, day: date):
-        """ Annotate all events in the QuerySet with the provided date. """
-
-        return self.annotate(_date=models.Value(day, models.DateField()))
-
-
 class Event(ExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin):
     label_ = "event"
 
@@ -1083,24 +643,6 @@ class Event(ExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin):
         verbose_name_plural = _("Events")
 
 
-class ExtraLessonQuerySet(TimetableQuerySet):
-    _multiple_rooms = False
-
-    def within_dates(self, start: date, end: date):
-        week_start = CalendarWeek.from_date(start)
-        week_end = CalendarWeek.from_date(end)
-
-        return self.filter(
-            week__gte=week_start.week,
-            week__lte=week_end.week,
-            period__weekday__gte=start.weekday(),
-            period__weekday__lte=end.weekday(),
-        )
-
-    def on_day(self, day:date):
-        return self.within_dates(day, day)
-
-
 class ExtraLesson(ExtensibleModel, GroupPropertiesMixin):
     label_ = "extra_lesson"