diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 6e535409dd609de46245d7f9210ec440be8bc59c..0bce4160fd70426d1078ac7b238262af44566b43 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,16 @@ All notable changes to this project will be documented in this file.
 The format is based on `Keep a Changelog`_,
 and this project adheres to `Semantic Versioning`_.
 
+Unreleased
+----------
+
+Upgrade notice
+~~~~~~~~~~~~~~
+
+If you're upgrading from 3.x, there is now a migration path to use.
+Therefore, please install ``AlekSIS-App-Lesrooster`` which now
+includes parts of the legacy Chronos and the migration path.
+
 `4.0.0.dev3`_ - 2024-07-10
 --------------------------
 
@@ -38,8 +48,7 @@ Starting from the class register core functionality, Alsijil is getting a entire
 of both its frontend and backend. The models formerly used for lesson documentation, notably
 `LessonDocumentation` and `PersonalNote` are replaced by new ones based on the calendar framework
 provided by `AlekSIS-Core` and the absense framework provided by `AlekSIS-App-Kolego`. The legacy
-views providing management functionality for those legacy models are not available anymore. Currently,
-there exists no migration path away from legacy data.
+views providing management functionality for those legacy models are not available anymore.
 
 Changed
 ~~~~~~~
diff --git a/aleksis/apps/alsijil/actions.py b/aleksis/apps/alsijil/actions.py
deleted file mode 100644
index 623a96b95ad28e319d16b4b2595f74352a03655d..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/actions.py
+++ /dev/null
@@ -1,92 +0,0 @@
-from typing import Callable, Sequence
-
-from django.contrib import messages
-from django.contrib.humanize.templatetags.humanize import apnumber
-from django.http import HttpRequest
-from django.template.loader import get_template
-from django.urls import reverse
-from django.utils.translation import gettext_lazy as _
-
-from aleksis.apps.alsijil.models import PersonalNote
-from aleksis.core.models import Notification
-
-
-def mark_as_excused(modeladmin, request, queryset):
-    queryset.filter(absent=True).update(excused=True, excuse_type=None)
-
-
-mark_as_excused.short_description = _("Mark as excused")
-
-
-def mark_as_unexcused(modeladmin, request, queryset):
-    queryset.filter(absent=True).update(excused=False, excuse_type=None)
-
-
-mark_as_unexcused.short_description = _("Mark as unexcused")
-
-
-def mark_as_excuse_type_generator(excuse_type) -> Callable:
-    def mark_as_excuse_type(modeladmin, request, queryset):
-        queryset.filter(absent=True).update(excused=True, excuse_type=excuse_type)
-
-    mark_as_excuse_type.short_description = _(f"Mark as {excuse_type.name}")
-    mark_as_excuse_type.__name__ = f"mark_as_excuse_type_{excuse_type.short_name}"
-
-    return mark_as_excuse_type
-
-
-def delete_personal_note(modeladmin, request, queryset):
-    notes = []
-    for personal_note in queryset:
-        personal_note.reset_values()
-        notes.append(personal_note)
-    PersonalNote.objects.bulk_update(
-        notes, fields=["absent", "excused", "tardiness", "excuse_type", "remarks"]
-    )
-
-
-delete_personal_note.short_description = _("Delete")
-
-
-def send_request_to_check_entry(modeladmin, request: HttpRequest, selected_items: Sequence[dict]):
-    """Send notifications to the teachers of the selected register objects.
-
-    Action for use with ``RegisterObjectTable`` and ``RegisterObjectActionForm``.
-    """
-    # Group class register entries by teachers so each teacher gets just one notification
-    grouped_by_teachers = {}
-    for entry in selected_items:
-        teachers = entry["register_object"].get_teachers().all()
-        for teacher in teachers:
-            grouped_by_teachers.setdefault(teacher, [])
-            grouped_by_teachers[teacher].append(entry)
-
-    template = get_template("alsijil/notifications/check.html")
-    for teacher, items in grouped_by_teachers.items():
-        msg = template.render({"items": items})
-
-        title = _("{} asks you to check some class register entries.").format(
-            request.user.person.addressing_name
-        )
-
-        n = Notification(
-            title=title,
-            description=msg,
-            sender=request.user.person.addressing_name,
-            recipient=teacher,
-            link=request.build_absolute_uri(reverse("overview_me")),
-        )
-        n.save()
-
-    count_teachers = len(grouped_by_teachers.keys())
-    count_items = len(selected_items)
-    messages.success(
-        request,
-        _(
-            "We have successfully sent notifications to "
-            "{count_teachers} persons for {count_items} lessons."
-        ).format(count_teachers=apnumber(count_teachers), count_items=apnumber(count_items)),
-    )
-
-
-send_request_to_check_entry.short_description = _("Ask teacher to check data")
diff --git a/aleksis/apps/alsijil/checks.py b/aleksis/apps/alsijil/checks.py
new file mode 100644
index 0000000000000000000000000000000000000000..a440f52da0888a3a97f752affd038c0fff583244
--- /dev/null
+++ b/aleksis/apps/alsijil/checks.py
@@ -0,0 +1,152 @@
+import logging
+from datetime import datetime, time
+from typing import TYPE_CHECKING
+
+from django.db.models.query_utils import Q
+from django.utils.translation import gettext as _
+
+from aleksis.apps.chronos.models import LessonEvent
+from aleksis.core.data_checks import DataCheck, IgnoreSolveOption, SolveOption
+
+if TYPE_CHECKING:
+    from aleksis.core.models import DataCheckResult
+
+
+class DeleteRelatedObjectSolveOption(SolveOption):
+    name = "delete"
+    verbose_name = _("Delete object")
+
+    @classmethod
+    def solve(cls, check_result: "DataCheckResult"):
+        check_result.related_object.delete()
+        check_result.delete()
+
+
+class SetGroupsWithCurrentGroupsSolveOption(SolveOption):
+    name = "set_groups_of_person"
+    verbose_name = _("Set current groups")
+
+    @classmethod
+    def solve(cls, check_result: "DataCheckResult"):
+        person = check_result.related_object.person
+        check_result.related_object.groups_of_person.set(person.member_of.all())
+        check_result.delete()
+
+
+class NoParticipationStatusesPersonalNotesInCancelledLessonsDataCheck(DataCheck):
+    name = "no_personal_notes_participation_statuses_in_cancelled_lessons"
+    verbose_name = _(
+        "Ensure that there are no participation statuses and personal notes in cancelled lessons"
+    )
+    problem_name = _("The participation status or personal note is related to a cancelled lesson.")
+    solve_options = {
+        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
+        IgnoreSolveOption.name: IgnoreSolveOption,
+    }
+
+    @classmethod
+    def check_data(cls):
+        from .models import NewPersonalNote, ParticipationStatus
+
+        participation_statuses = ParticipationStatus.objects.filter(
+            related_documentation__amends__in=LessonEvent.objects.filter(cancelled=True)
+        )
+        personal_notes = NewPersonalNote.objects.filter(
+            documentation__amends__in=LessonEvent.objects.filter(cancelled=True)
+        )
+
+        for status in participation_statuses:
+            logging.info(f"Check participation status {status}")
+            cls.register_result(status)
+
+        for note in personal_notes:
+            logging.info(f"Check personal note {note}")
+            cls.register_result(note)
+
+
+class NoGroupsOfPersonsSetInParticipationStatusesDataCheck(DataCheck):
+    name = "no_groups_of_persons_set_in_participation_statuses"
+    verbose_name = _("Ensure that 'groups_of_person' is set for every participation status")
+    problem_name = _("The participation status has no group in 'groups_of_person'.")
+    solve_options = {
+        SetGroupsWithCurrentGroupsSolveOption.name: SetGroupsWithCurrentGroupsSolveOption,
+        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
+        IgnoreSolveOption.name: IgnoreSolveOption,
+    }
+
+    @classmethod
+    def check_data(cls):
+        from .models import ParticipationStatus
+
+        participation_statuses = ParticipationStatus.objects.filter(groups_of_person__isnull=True)
+
+        for status in participation_statuses:
+            logging.info(f"Check participation status {status}")
+            cls.register_result(status)
+
+
+class DocumentationOnHolidaysDataCheck(DataCheck):
+    """Checks for documentation objects on holidays."""
+
+    name = "documentation_on_holidays"
+    verbose_name = _("Ensure that there are no documentations on holidays")
+    problem_name = _("The documentation is on holidays.")
+    solve_options = {
+        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
+        IgnoreSolveOption.name: IgnoreSolveOption,
+    }
+
+    @classmethod
+    def check_data(cls):
+        from aleksis.core.models import Holiday
+
+        from .models import Documentation
+
+        holidays = Holiday.objects.all()
+
+        q = Q(pk__in=[])
+        for holiday in holidays:
+            q = q | Q(
+                datetime_start__gte=datetime.combine(holiday.date_start, time.min),
+                datetime_end__lte=datetime.combine(holiday.date_end, time.max),
+            )
+        documentations = Documentation.objects.filter(q)
+
+        for doc in documentations:
+            logging.info(f"Documentation {doc} is on holidays")
+            cls.register_result(doc)
+
+
+class ParticipationStatusPersonalNoteOnHolidaysDataCheck(DataCheck):
+    """Checks for participation status and personal note objects on holidays."""
+
+    name = "participation_status_personal_note_on_holidays"
+    verbose_name = _(
+        "Ensure that there are no participation statuses or personal notes on holidays"
+    )
+    problem_name = _("The participation status or personal note is on holidays.")
+    solve_options = {
+        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
+        IgnoreSolveOption.name: IgnoreSolveOption,
+    }
+
+    @classmethod
+    def check_data(cls):
+        from aleksis.core.models import Holiday
+
+        from .models import ParticipationStatus
+
+        holidays = Holiday.objects.all()
+
+        q = Q(pk__in=[])
+        for holiday in holidays:
+            q = q | Q(
+                datetime_start__gte=datetime.combine(holiday.date_start, time.min),
+                datetime_end__lte=datetime.combine(holiday.date_end, time.max),
+            )
+
+        participation_statuses = ParticipationStatus.objects.filter(q)
+
+        for status in participation_statuses:
+            logging.info(f"Participation status {status} is on holidays")
+            cls.register_result(status)
diff --git a/aleksis/apps/alsijil/data_checks.py b/aleksis/apps/alsijil/data_checks.py
deleted file mode 100644
index 87e703a0cb4fb17cff6be360cbea52895a6fa775..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/data_checks.py
+++ /dev/null
@@ -1,181 +0,0 @@
-import logging
-from typing import TYPE_CHECKING
-
-from django.db.models import F
-from django.db.models.query_utils import Q
-from django.utils.translation import gettext as _
-
-from aleksis.core.data_checks import DataCheck, IgnoreSolveOption, SolveOption
-
-if TYPE_CHECKING:
-    from aleksis.core.models import DataCheckResult
-
-
-class DeleteRelatedObjectSolveOption(SolveOption):
-    name = "delete"
-    verbose_name = _("Delete object")
-
-    @classmethod
-    def solve(cls, check_result: "DataCheckResult"):
-        check_result.related_object.delete()
-        check_result.delete()
-
-
-class SetGroupsWithCurrentGroupsSolveOption(SolveOption):
-    name = "set_groups_of_person"
-    verbose_name = _("Set current groups")
-
-    @classmethod
-    def solve(cls, check_result: "DataCheckResult"):
-        person = check_result.related_object.person
-        check_result.related_object.groups_of_person.set(person.member_of.all())
-        check_result.delete()
-
-
-class ResetPersonalNoteSolveOption(SolveOption):
-    name = "reset_personal_note"
-    verbose_name = _("Reset personal note to defaults")
-
-    @classmethod
-    def solve(cls, check_result: "DataCheckResult"):
-        note = check_result.related_object
-        note.reset_values()
-        note.save()
-        check_result.delete()
-
-
-class NoPersonalNotesInCancelledLessonsDataCheck(DataCheck):
-    name = "no_personal_notes_in_cancelled_lessons"
-    verbose_name = _("Ensure that there are no personal notes in cancelled lessons")
-    problem_name = _("The personal note is related to a cancelled lesson.")
-    solve_options = {
-        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
-        IgnoreSolveOption.name: IgnoreSolveOption,
-    }
-
-    @classmethod
-    def check_data(cls):
-        from .models import PersonalNote
-
-        personal_notes = (
-            PersonalNote.objects.not_empty()
-            .filter(
-                lesson_period__substitutions__cancelled=True,
-                lesson_period__substitutions__week=F("week"),
-                lesson_period__substitutions__year=F("year"),
-            )
-            .prefetch_related("lesson_period", "lesson_period__substitutions")
-        )
-
-        for note in personal_notes:
-            logging.info(f"Check personal note {note}")
-            cls.register_result(note)
-
-
-class NoGroupsOfPersonsSetInPersonalNotesDataCheck(DataCheck):
-    name = "no_groups_of_persons_set_in_personal_notes"
-    verbose_name = _("Ensure that 'groups_of_person' is set for every personal note")
-    problem_name = _("The personal note has no group in 'groups_of_person'.")
-    solve_options = {
-        SetGroupsWithCurrentGroupsSolveOption.name: SetGroupsWithCurrentGroupsSolveOption,
-        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
-        IgnoreSolveOption.name: IgnoreSolveOption,
-    }
-
-    @classmethod
-    def check_data(cls):
-        from .models import PersonalNote
-
-        personal_notes = PersonalNote.objects.filter(groups_of_person__isnull=True)
-
-        for note in personal_notes:
-            logging.info(f"Check personal note {note}")
-            cls.register_result(note)
-
-
-class LessonDocumentationOnHolidaysDataCheck(DataCheck):
-    """Checks for lesson documentation objects on holidays.
-
-    This ignores empty lesson documentation as they are created by default.
-    """
-
-    name = "lesson_documentation_on_holidays"
-    verbose_name = _("Ensure that there are no filled out lesson documentations on holidays")
-    problem_name = _("The lesson documentation is on holidays.")
-    solve_options = {
-        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
-        IgnoreSolveOption.name: IgnoreSolveOption,
-    }
-
-    @classmethod
-    def check_data(cls):
-        from aleksis.apps.chronos.models import Holiday
-
-        from .models import LessonDocumentation
-
-        holidays = Holiday.objects.all()
-
-        documentations = LessonDocumentation.objects.not_empty().annotate_date_range()
-
-        q = Q(pk__in=[])
-        for holiday in holidays:
-            q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end)
-        documentations = documentations.filter(q)
-
-        for doc in documentations:
-            logging.info(f"Lesson documentation {doc} is on holidays")
-            cls.register_result(doc)
-
-
-class PersonalNoteOnHolidaysDataCheck(DataCheck):
-    """Checks for personal note objects on holidays.
-
-    This ignores empty personal notes as they are created by default.
-    """
-
-    name = "personal_note_on_holidays"
-    verbose_name = _("Ensure that there are no filled out personal notes on holidays")
-    problem_name = _("The personal note is on holidays.")
-    solve_options = {
-        DeleteRelatedObjectSolveOption.name: DeleteRelatedObjectSolveOption,
-        IgnoreSolveOption.name: IgnoreSolveOption,
-    }
-
-    @classmethod
-    def check_data(cls):
-        from aleksis.apps.chronos.models import Holiday
-
-        from .models import PersonalNote
-
-        holidays = Holiday.objects.all()
-
-        personal_notes = PersonalNote.objects.not_empty().annotate_date_range()
-
-        q = Q(pk__in=[])
-        for holiday in holidays:
-            q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end)
-        personal_notes = personal_notes.filter(q)
-
-        for note in personal_notes:
-            logging.info(f"Personal note {note} is on holidays")
-            cls.register_result(note)
-
-
-class ExcusesWithoutAbsences(DataCheck):
-    name = "excuses_without_absences"
-    verbose_name = _("Ensure that there are no excused personal notes without an absence")
-    problem_name = _("The personal note is marked as excused, but not as absent.")
-    solve_options = {
-        ResetPersonalNoteSolveOption.name: ResetPersonalNoteSolveOption,
-        IgnoreSolveOption.name: IgnoreSolveOption,
-    }
-
-    @classmethod
-    def check_data(cls):
-        from .models import PersonalNote
-
-        personal_notes = PersonalNote.objects.filter(excused=True, absent=False)
-
-        for note in personal_notes:
-            logging.info(f"Check personal note {note}")
-            cls.register_result(note)
diff --git a/aleksis/apps/alsijil/filters.py b/aleksis/apps/alsijil/filters.py
deleted file mode 100644
index 0de3f04876edf978403287ed1031be79ba88790c..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/filters.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from django.utils.translation import gettext as _
-
-from django_filters import CharFilter, DateFilter, FilterSet
-from material import Layout, Row
-
-from aleksis.core.models import SchoolTerm
-
-from .models import PersonalNote
-
-
-class PersonalNoteFilter(FilterSet):
-    day_start = DateFilter(lookup_expr="gte", label=_("After"))
-    day_end = DateFilter(lookup_expr="lte", label=_("Before"))
-    subject = CharFilter(lookup_expr="icontains", label=_("Subject"))
-
-    def __init__(self, data=None, *args, **kwargs):
-        if data is not None:
-            data = data.copy()
-
-            current_school_term = SchoolTerm.current
-            if not data.get("day_start") and current_school_term:
-                data["day_start"] = current_school_term.date_start
-
-            for name, f in self.base_filters.items():
-                initial = f.extra.get("initial")
-                if not data.get(name) and initial:
-                    data[name] = initial
-
-        super().__init__(data, *args, **kwargs)
-        self.form.fields["tardiness__lt"].label = _("Tardiness is lower than")
-        self.form.fields["tardiness__gt"].label = _("Tardiness is bigger than")
-        self.form.layout = Layout(
-            Row("subject"),
-            Row("day_start", "day_end"),
-            Row("absent", "excused", "excuse_type"),
-            Row("tardiness__gt", "tardiness__lt", "extra_marks"),
-        )
-
-    class Meta:
-        model = PersonalNote
-        fields = {
-            "excused": ["exact"],
-            "tardiness": ["lt", "gt"],
-            "absent": ["exact"],
-            "excuse_type": ["exact"],
-            "extra_marks": ["exact"],
-        }
diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py
index 35cdf3b70552de594a0c7ef8811c53220ddef071..45784137cb114b360709eedfbdab7f5a16b3c94c 100644
--- a/aleksis/apps/alsijil/forms.py
+++ b/aleksis/apps/alsijil/forms.py
@@ -1,268 +1,19 @@
-from datetime import datetime, timedelta
-from typing import Optional, Sequence
-
 from django import forms
-from django.core.exceptions import ValidationError
-from django.db.models import Count, Q
-from django.http import HttpRequest
-from django.utils import timezone
+from django.db.models import Q
 from django.utils.translation import gettext_lazy as _
 
-from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget, Select2Widget
-from guardian.shortcuts import get_objects_for_user
-from material import Fieldset, Layout, Row
+from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget
+from material import Layout, Row
 
-from aleksis.apps.chronos.managers import TimetableType
-from aleksis.apps.chronos.models import LessonPeriod, Subject, TimePeriod
-from aleksis.core.forms import ActionForm, ListActionForm
-from aleksis.core.models import Group, Person, SchoolTerm
+from aleksis.core.models import Group, Person
 from aleksis.core.util.core_helpers import get_site_preferences
-from aleksis.core.util.predicates import check_global_permission
 
-from .actions import (
-    delete_personal_note,
-    mark_as_excuse_type_generator,
-    mark_as_excused,
-    mark_as_unexcused,
-    send_request_to_check_entry,
-)
 from .models import (
-    ExcuseType,
-    ExtraMark,
     GroupRole,
     GroupRoleAssignment,
-    LessonDocumentation,
-    PersonalNote,
-)
-
-
-class LessonDocumentationForm(forms.ModelForm):
-    class Meta:
-        model = LessonDocumentation
-        fields = ["topic", "homework", "group_note"]
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-
-        self.fields["homework"].label = _("Homework for the next lesson")
-        if (
-            self.instance.lesson_period
-            and get_site_preferences()["alsijil__allow_carry_over_same_week"]
-        ):
-            self.fields["carry_over_week"] = forms.BooleanField(
-                label=_("Carry over data to all other lessons with the same subject in this week"),
-                initial=True,
-                required=False,
-            )
-
-    def save(self, **kwargs):
-        lesson_documentation = super().save(commit=True)
-        if (
-            get_site_preferences()["alsijil__allow_carry_over_same_week"]
-            and self.cleaned_data["carry_over_week"]
-            and (
-                lesson_documentation.topic
-                or lesson_documentation.homework
-                or lesson_documentation.group_note
-            )
-            and lesson_documentation.lesson_period
-        ):
-            lesson_documentation.carry_over_data(
-                LessonPeriod.objects.filter(lesson=lesson_documentation.lesson_period.lesson)
-            )
-
-
-class PersonalNoteForm(forms.ModelForm):
-    class Meta:
-        model = PersonalNote
-        fields = ["absent", "tardiness", "excused", "excuse_type", "extra_marks", "remarks"]
-
-    person_name = forms.CharField(disabled=True)
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.fields["person_name"].widget.attrs.update(
-            {"class": "alsijil-lesson-personal-note-name"}
-        )
-        self.fields["person_name"].widget = forms.HiddenInput()
-
-        if self.instance and getattr(self.instance, "person", None):
-            self.fields["person_name"].initial = str(self.instance.person)
-
-
-class SelectForm(forms.Form):
-    layout = Layout(Row("group", "teacher"))
-
-    group = forms.ModelChoiceField(
-        queryset=None,
-        label=_("Group"),
-        required=False,
-        widget=Select2Widget,
-    )
-    teacher = forms.ModelChoiceField(
-        queryset=None,
-        label=_("Teacher"),
-        required=False,
-        widget=Select2Widget,
-    )
-
-    def clean(self) -> dict:
-        data = super().clean()
-
-        if data.get("group") and not data.get("teacher"):
-            type_ = TimetableType.GROUP
-            instance = data["group"]
-        elif data.get("teacher") and not data.get("group"):
-            type_ = TimetableType.TEACHER
-            instance = data["teacher"]
-        elif not data.get("teacher") and not data.get("group"):
-            return data
-        else:
-            raise ValidationError(_("You can't select a group and a teacher both."))
-
-        data["type_"] = type_
-        data["instance"] = instance
-        return data
-
-    def __init__(self, request, *args, **kwargs):
-        self.request = request
-        super().__init__(*args, **kwargs)
-
-        person = self.request.user.person
-
-        group_qs = Group.get_groups_with_lessons()
-
-        # Filter selectable groups by permissions
-        if not check_global_permission(self.request.user, "alsijil.view_week"):
-            # 1) All groups the user is allowed to see the week view by object permissions
-            # 2) All groups the user is a member of an owner of
-            # 3) If the corresponding preference is turned on:
-            # All groups that have a parent group the user is an owner of
-            group_qs = (
-                group_qs.filter(
-                    pk__in=get_objects_for_user(
-                        self.request.user, "core.view_week_class_register_group", Group
-                    ).values_list("pk", flat=True)
-                )
-            ).union(
-                group_qs.filter(
-                    Q(members=person) | Q(owners=person) | Q(parent_groups__owners=person)
-                    if get_site_preferences()["alsijil__inherit_privileges_from_parent_group"]
-                    else Q(members=person) | Q(owners=person)
-                )
-            )
-
-        # Flatten query by filtering groups by pk
-        self.fields["group"].queryset = Group.objects.filter(
-            pk__in=list(group_qs.values_list("pk", flat=True))
-        )
-
-        teacher_qs = Person.objects.annotate(lessons_count=Count("lessons_as_teacher")).filter(
-            lessons_count__gt=0
-        )
-
-        # Filter selectable teachers by permissions
-        if not check_global_permission(self.request.user, "alsijil.view_week"):
-            # If the user hasn't got the global permission and the inherit privileges preference is
-            # turned off, the user is only allowed to see their own person. Otherwise, the user
-            # is allowed to see all persons that teach lessons that the given groups attend.
-            if get_site_preferences()["alsijil__inherit_privileges_from_parent_group"]:
-                teacher_pks = []
-                for group in group_qs:
-                    for lesson in group.lessons.all():
-                        for teacher in lesson.teachers.all():
-                            teacher_pks.append(teacher.pk)
-                teacher_qs = teacher_qs.filter(pk__in=teacher_pks)
-            else:
-                teacher_qs = teacher_qs.filter(pk=person.pk)
-
-        self.fields["teacher"].queryset = teacher_qs
-
-
-PersonalNoteFormSet = forms.modelformset_factory(
-    PersonalNote, form=PersonalNoteForm, max_num=0, extra=0
 )
 
 
-class RegisterAbsenceForm(forms.Form):
-    layout = Layout(
-        Fieldset("", "person"),
-        Fieldset("", Row("date_start", "date_end"), Row("from_period", "to_period")),
-        Fieldset("", Row("absent", "excused"), Row("excuse_type"), Row("remarks")),
-    )
-    person = forms.ModelChoiceField(label=_("Person"), queryset=None, widget=Select2Widget)
-    date_start = forms.DateField(label=_("Start date"), initial=datetime.today)
-    date_end = forms.DateField(label=_("End date"), initial=datetime.today)
-    from_period = forms.ChoiceField(label=_("Start period"))
-    to_period = forms.ChoiceField(label=_("End period"))
-    absent = forms.BooleanField(label=_("Absent"), initial=True, required=False)
-    excused = forms.BooleanField(label=_("Excused"), initial=True, required=False)
-    excuse_type = forms.ModelChoiceField(
-        label=_("Excuse type"),
-        queryset=ExcuseType.objects.all(),
-        widget=Select2Widget,
-        required=False,
-    )
-    remarks = forms.CharField(label=_("Remarks"), max_length=30, required=False)
-
-    def __init__(self, request, *args, **kwargs):
-        self.request = request
-        super().__init__(*args, **kwargs)
-        period_choices = TimePeriod.period_choices
-
-        if self.request.user.has_perm("alsijil.register_absence"):
-            self.fields["person"].queryset = Person.objects.all()
-        else:
-            persons_qs = Person.objects.filter(
-                Q(
-                    pk__in=get_objects_for_user(
-                        self.request.user, "core.register_absence_person", Person
-                    )
-                )
-                | Q(primary_group__owners=self.request.user.person)
-                | Q(
-                    member_of__in=get_objects_for_user(
-                        self.request.user, "core.register_absence_group", Group
-                    )
-                )
-            ).distinct()
-
-            self.fields["person"].queryset = persons_qs
-
-        self.fields["from_period"].choices = period_choices
-        self.fields["to_period"].choices = period_choices
-        self.fields["from_period"].initial = TimePeriod.period_min
-        self.fields["to_period"].initial = TimePeriod.period_max
-
-
-class ExtraMarkForm(forms.ModelForm):
-    layout = Layout("short_name", "name")
-
-    class Meta:
-        model = ExtraMark
-        fields = ["short_name", "name"]
-
-
-class ExcuseTypeForm(forms.ModelForm):
-    layout = Layout("short_name", "name", "count_as_absent")
-
-    class Meta:
-        model = ExcuseType
-        fields = ["short_name", "name", "count_as_absent"]
-
-
-class PersonOverviewForm(ActionForm):
-    def get_actions(self):
-        return (
-            [mark_as_excused, mark_as_unexcused]
-            + [
-                mark_as_excuse_type_generator(excuse_type)
-                for excuse_type in ExcuseType.objects.all()
-            ]
-            + [delete_personal_note]
-        )
-
-
 class GroupRoleForm(forms.ModelForm):
     layout = Layout("name", "icon", "colour")
 
@@ -357,74 +108,3 @@ class GroupRoleAssignmentEditForm(forms.ModelForm):
     class Meta:
         model = GroupRoleAssignment
         fields = ["date_start", "date_end"]
-
-
-class FilterRegisterObjectForm(forms.Form):
-    """Form for filtering register objects in ``RegisterObjectTable``."""
-
-    layout = Layout(
-        Row("school_term", "date_start", "date_end"), Row("has_documentation", "group", "subject")
-    )
-    school_term = forms.ModelChoiceField(queryset=None, label=_("School term"))
-    has_documentation = forms.NullBooleanField(label=_("Has lesson documentation"))
-    group = forms.ModelChoiceField(queryset=None, label=_("Group"), required=False)
-    subject = forms.ModelChoiceField(queryset=None, label=_("Subject"), required=False)
-    date_start = forms.DateField(label=_("Start date"))
-    date_end = forms.DateField(label=_("End date"))
-
-    @classmethod
-    def get_initial(cls, has_documentation: Optional[bool] = None):
-        date_end = timezone.now().date()
-        date_start = date_end - timedelta(days=30)
-        school_term = SchoolTerm.current
-
-        # If there is no current school year, use last known school year.
-        if not school_term:
-            school_term = SchoolTerm.objects.all().last()
-
-        return {
-            "school_term": school_term,
-            "date_start": date_start,
-            "date_end": date_end,
-            "has_documentation": has_documentation,
-        }
-
-    def __init__(
-        self,
-        request: HttpRequest,
-        *args,
-        for_person: bool = True,
-        default_documentation: Optional[bool] = None,
-        groups: Optional[Sequence[Group]] = None,
-        **kwargs,
-    ):
-        self.request = request
-        person = self.request.user.person
-
-        kwargs["initial"] = self.get_initial(has_documentation=default_documentation)
-        super().__init__(*args, **kwargs)
-
-        self.fields["school_term"].queryset = SchoolTerm.objects.all()
-
-        if not groups and for_person:
-            groups = Group.objects.filter(
-                Q(lessons__teachers=person)
-                | Q(lessons__lesson_periods__substitutions__teachers=person)
-                | Q(events__teachers=person)
-                | Q(extra_lessons__teachers=person)
-            ).distinct()
-        elif not for_person:
-            groups = Group.objects.all()
-        self.fields["group"].queryset = groups
-
-        # Filter subjects by selectable groups
-        subject_qs = Subject.objects.filter(
-            Q(lessons__groups__in=groups) | Q(extra_lessons__groups__in=groups)
-        ).distinct()
-        self.fields["subject"].queryset = subject_qs
-
-
-class RegisterObjectActionForm(ListActionForm):
-    """Action form for managing register objects for use with ``RegisterObjectTable``."""
-
-    actions = [send_request_to_check_entry]
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue
index 3263225dc890242ed038f8b5f58153278e5aea64..7a6b4c73f5e3dbe6b6dbfa86df69b96b4a8d74a2 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue
@@ -67,7 +67,7 @@
         v-if="form"
         @click="form = false"
         :loading="loading"
-        :disabled="!formValid"
+        :disabled="!formValid || !absenceReason"
       >
         {{ $t("actions.continue") }}
         <v-icon right>$next</v-icon>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
index 3248becfa4ddeaaaf998794f5398d5845f38d21f..2c1479f73a0de45c1c687fea455514e3ba5af93a 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
@@ -288,6 +288,25 @@ export default {
               <span v-t="'actions.back_to_overview'" />
             </v-tooltip>
             {{ item.person.fullName }}
+            <v-spacer />
+            <v-tooltip bottom>
+              <template #activator="{ on, attrs }">
+                <v-btn
+                  v-bind="attrs"
+                  v-on="on"
+                  icon
+                  :to="{
+                    name: 'core.personById',
+                    params: {
+                      id: item.person.id,
+                    },
+                  }"
+                >
+                  <v-icon>mdi-open-in-new</v-icon>
+                </v-btn>
+              </template>
+              {{ $t("actions.open_person_page", item.person) }}
+            </v-tooltip>
           </v-card-title>
           <v-card-text>
             <absence-reason-group-select
@@ -301,7 +320,7 @@ export default {
               v-bind="documentationPartProps"
               :loading="loading"
               :disabled="loading"
-              :participation="item"
+              :participations="[item]"
               :value="item.tardiness"
               @input="sendToServer([item], 'tardiness', $event)"
             />
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue
index 1a70d655e00a3bc6c85f002d417497282387af7a..d787f79740d11aa9419cb9c55d0364dd87451f59 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue
@@ -29,6 +29,7 @@
         @open="$emit('open')"
         @loading="loading = $event"
         @save="$emit('close')"
+        @dirty="dirty = $event"
       />
       <lesson-notes v-bind="documentationPartProps" />
     </v-card-text>
@@ -45,6 +46,7 @@
         v-if="documentation.canEdit"
         @click="save"
         :loading="loading"
+        :disabled="!dirty"
       />
       <cancel-button
         v-if="!documentation.canEdit"
@@ -76,12 +78,13 @@ export default {
     SaveButton,
     CancelButton,
   },
-  emits: ["open", "close"],
+  emits: ["open", "close", "dirty"],
   mixins: [documentationPartMixin],
   data() {
     return {
       loading: false,
       documentationsMutation: createOrUpdateDocumentations,
+      dirty: false,
     };
   },
   methods: {
@@ -90,6 +93,11 @@ export default {
       this.$emit("close");
     },
   },
+  watch: {
+    dirty(dirty) {
+      this.$emit("dirty", dirty);
+    },
+  },
 };
 </script>
 
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue
index 5b5dd101b76288cd1b09ba5418a452d70a9425fa..5508705271fe04166dc97d7937faf13e867d2351 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue
@@ -1,7 +1,11 @@
 <!-- Wrapper around Documentation.vue -->
 <!-- That uses it either as list item or as editable modal dialog. -->
 <template>
-  <mobile-fullscreen-dialog v-model="popup" max-width="500px">
+  <mobile-fullscreen-dialog
+    v-model="popup"
+    max-width="500px"
+    :persistent="dirty"
+  >
     <template #activator="activator">
       <!-- list view -> activate dialog -->
       <documentation
@@ -21,6 +25,7 @@
       :absence-reasons="absenceReasons"
       :subjects="subjects"
       @close="popup = false"
+      @dirty="dirty = $event"
     />
   </mobile-fullscreen-dialog>
 </template>
@@ -38,6 +43,7 @@ export default {
   data() {
     return {
       popup: false,
+      dirty: false,
     };
   },
   props: {
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue
index 3710a45f83c7d267ba281f95d2cbbaaae95f46f3..6693fa390b5ad558a198d0ca6a2d16570e239c64 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue
@@ -155,7 +155,7 @@ export default {
     documentationCacheUpdateMixin,
     documentationPartMixin,
   ],
-  emits: ["open"],
+  emits: ["open", "dirty"],
   data() {
     return {
       topic: null,
@@ -244,9 +244,25 @@ export default {
         }[this.appendIcon] || ""
       );
     },
+    dirty() {
+      return !(
+        this.topic === this.documentation.topic &&
+        this.homework === this.documentation.homework &&
+        this.groupNote === this.documentation.groupNote
+      );
+    },
   },
   mounted() {
     this.$on("save", this.handleAppendIconSuccess);
+
+    this.topic = this.documentation.topic;
+    this.homework = this.documentation.homework;
+    this.groupNote = this.documentation.groupNote;
+  },
+  watch: {
+    dirty(dirty) {
+      this.$emit("dirty", dirty);
+    },
   },
 };
 </script>
diff --git a/aleksis/apps/alsijil/frontend/components/injectables/group_actions/OpenCoursebook.vue b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/OpenCoursebook.vue
new file mode 100644
index 0000000000000000000000000000000000000000..c52dedfc1f94497ba9a82dbd342a5b47dead2821
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/OpenCoursebook.vue
@@ -0,0 +1,30 @@
+<script>
+import groupActionsMixin from "aleksis.core/components/group/actions/groupActionsMixin.js";
+export default {
+  name: "OpenCoursebook",
+  mixins: [groupActionsMixin],
+};
+</script>
+
+<template>
+  <v-list-item
+    :to="{
+      name: 'alsijil.coursebook',
+      params: {
+        filterType: 'all',
+        pageType: 'documentations',
+        objType: 'group',
+        objId: group.id,
+      },
+    }"
+  >
+    <v-list-item-icon>
+      <v-icon>mdi-book-education-outline</v-icon>
+    </v-list-item-icon>
+    <v-list-item-content>
+      <v-list-item-title>
+        {{ $t("actions.open_in_coursebook") }}
+      </v-list-item-title>
+    </v-list-item-content>
+  </v-list-item>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/injectables/group_actions/ShowAbsences.vue b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/ShowAbsences.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e582968e3c84d2e5afbd334233ee93138ef98540
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/ShowAbsences.vue
@@ -0,0 +1,30 @@
+<script>
+import groupActionsMixin from "aleksis.core/components/group/actions/groupActionsMixin.js";
+export default {
+  name: "ShowAbsences",
+  mixins: [groupActionsMixin],
+};
+</script>
+
+<template>
+  <v-list-item
+    :to="{
+      name: 'alsijil.coursebook',
+      params: {
+        filterType: 'all',
+        pageType: 'absences',
+        objType: 'group',
+        objId: group.id,
+      },
+    }"
+  >
+    <v-list-item-icon>
+      <v-icon>mdi-account-details-outline</v-icon>
+    </v-list-item-icon>
+    <v-list-item-content>
+      <v-list-item-title>
+        {{ $t("actions.show_absences") }}
+      </v-list-item-title>
+    </v-list-item-content>
+  </v-list-item>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/index.js b/aleksis/apps/alsijil/frontend/index.js
index a8e5bfbdc288b9e86fbe72e468e1a4a203847aff..d14e01baae3daefed313582cc936b0a16fa0b547 100644
--- a/aleksis/apps/alsijil/frontend/index.js
+++ b/aleksis/apps/alsijil/frontend/index.js
@@ -2,6 +2,20 @@ import { hasPersonValidator } from "aleksis.core/routeValidators";
 import { DateTime } from "luxon";
 
 export const collectionItems = {
+  coreGroupActions: [
+    {
+      key: "alsijil-open-coursebook",
+      component: () =>
+        import("./components/injectables/group_actions/OpenCoursebook.vue"),
+      isActive: () => true,
+    },
+    {
+      key: "alsijil-show-absences",
+      component: () =>
+        import("./components/injectables/group_actions/ShowAbsences.vue"),
+      isActive: () => true,
+    },
+  ],
   coreGroupOverview: [
     {
       tab: {
@@ -36,7 +50,7 @@ export default {
     titleKey: "alsijil.menu_title",
     icon: "mdi-account-group-outline",
     iconActive: "mdi-account-group",
-    validators: [hasPersonValidator],
+    permission: "alsijil.view_menu_rule",
   },
   props: {
     byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
diff --git a/aleksis/apps/alsijil/frontend/messages/de.json b/aleksis/apps/alsijil/frontend/messages/de.json
index 291db84f3142a5494dbcf45b2c12bd70c18d39ff..b1996bd114872aa39577f0f28eda6a19a9bf82e3 100644
--- a/aleksis/apps/alsijil/frontend/messages/de.json
+++ b/aleksis/apps/alsijil/frontend/messages/de.json
@@ -1,6 +1,7 @@
 {
   "actions": {
-    "back_to_overview": "Zurück zur Übersicht"
+    "back_to_overview": "Zurück zur Übersicht",
+    "open_person_page": "Detailansicht für {fullName} aufrufen"
   },
   "alsijil": {
     "absence": {
@@ -110,7 +111,7 @@
       "name": "Markierung",
       "short_name": "Abkürzung",
       "show_in_coursebook": "In Kursbuch-Ãœbersicht zeigen",
-      "show_in_coursebook_helptext": "Wenn aktiviert tauchen diese Markierungen in den Zeilen im Kursbuch auf.",
+      "show_in_coursebook_helptext": "Wenn aktiviert, tauchen diese Markierungen in den Zeilen im Kursbuch auf.",
       "title": "Zusätzliche Markierung",
       "title_plural": "Zusätzliche Markierungen"
     },
diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json
index 827f782725e34a90926f9751dad295013fdd6d5b..11b433cd4be554b3eb0306fe736a0de445eb3008 100644
--- a/aleksis/apps/alsijil/frontend/messages/en.json
+++ b/aleksis/apps/alsijil/frontend/messages/en.json
@@ -161,7 +161,10 @@
     }
   },
   "actions": {
-    "back_to_overview": "Back to overview"
+    "back_to_overview": "Back to overview",
+    "open_person_page": "Open detail view for {fullName}",
+    "open_in_coursebook": "View Coursebook",
+    "show_absences": "Open absence list"
   },
   "time": {
     "minutes": "minutes",
diff --git a/aleksis/apps/alsijil/frontend/messages/uk.json b/aleksis/apps/alsijil/frontend/messages/uk.json
index f567697a8af4deb02905af7eeaf0dd61e10c62f9..b758ad392183a05aa15e25b0aef4f09fdb401fb3 100644
--- a/aleksis/apps/alsijil/frontend/messages/uk.json
+++ b/aleksis/apps/alsijil/frontend/messages/uk.json
@@ -13,21 +13,97 @@
       "absences": {
         "action_for_selected": "Відмітити обраного відвідувача як: | Відмітити {count} відвідувачів як",
         "button": "Зареєструвати відсутності",
-        "title": "Зареєструвати відсутності"
+        "lessons": "Без уроків | 1 урок | {count} уроків",
+        "success": "Відсутність успішно зареєстрована.",
+        "summary": "Підсумки",
+        "title": "Зареєструвати відсутності",
+        "warning": "В обраному проміжку часу містяться наступні уроки. Перевірте, будь ласка, їх перед підтвердженням реєстрації відсутності."
       },
       "filter": {
+        "absences_exist": "Показати уроки лише з відсутніми відвідувачами",
         "courses": "Курси",
-        "groups": "Групи"
+        "filter_for_obj": "Фільтрувати за групою та курсом",
+        "groups": "Групи",
+        "missing": "Показати лише неповні уроки",
+        "own": "Показати лише власні уроки",
+        "page_type": {
+          "absences": "Показати відсутність",
+          "documentations": "Показати документацію"
+        }
       },
+      "information": {
+        "subject": {
+          "field": "Редагувати предмет"
+        }
+      },
+      "mark_as_absent_day": {
+        "action_button": "Продовжити відсутність",
+        "description": "Ви дійсно хочете позначити їх як {reason} до залишку дня?",
+        "title": "Помилка: немає особи | Як {reason} успішно вказано {name} | Як {reason} успішно відмічені {n} людей"
+      },
+      "menu_title": "Курсова книга",
+      "no_data": "Для обраних груп та курсів у цей проміжок часу уроків немає",
+      "no_results": "Результатів пошуку {search} немає",
+      "notes": {
+        "future": "Урок у майбутньому",
+        "show_list": "Список учасників"
+      },
+      "notices": {
+        "future": "Редагування цього уроку не дозволене, оскільки він у майбутньому.",
+        "no_entry": "Для цього уроку ще немає записів."
+      },
+      "page_title": "Курсова книга {name}",
+      "participation_status": "Стан участі",
       "participations": {
-        "present": "Присутній"
-      }
+        "present": "Присутній",
+        "present_number": "{present}/{total} присутніх"
+      },
+      "status": {
+        "available": "Документація доступна",
+        "cancelled": "Урок скасований",
+        "missing": "Документація відсутня",
+        "pending": "Урок очікується",
+        "running": "Урок триває",
+        "substitution": "Заміна уроку"
+      },
+      "summary": {
+        "group_note": {
+          "empty": "Групової нотатки немає",
+          "label": "Групова нотатка",
+          "value": "ГН: {groupNote}"
+        },
+        "homework": {
+          "empty": "Домашнього завдання немає",
+          "empty_yet": "Домашнє завдання ще не призначене.",
+          "label": "Домашнє завдання",
+          "value": "ДЗ: {homework}"
+        },
+        "topic": {
+          "label": "Тема",
+          "status": {
+            "error": "Під час збереження теми сталася помилка: {error}",
+            "success": "Тема успішно збережена"
+          }
+        }
+      },
+      "title_absences": "Курсова книга - відсутності",
+      "title_documentations": "Курсова книга",
+      "title_plural": "Курсова книга"
     },
     "excuse_types": {
       "menu_title": "Типи пояснень"
     },
     "extra_marks": {
-      "menu_title": "Додаткові відмітки"
+      "colour_bg": "Колір фону",
+      "colour_fg": "Колір переднього плану",
+      "create": "Створити додаткову позначку",
+      "menu_title": "Додаткові позначки",
+      "name": "Позначка",
+      "short_name": "Скорочення",
+      "show_in_coursebook": "Подивитися в огляді курсової книги",
+      "show_in_coursebook_helptext": "Якщо відмічено, ця додаткова позначка буде показана в курсовій книзі",
+      "title": "Додаткова позначка",
+      "title_plural": "Додаткові позначки"
     },
     "group_roles": {
       "menu_title_assign": "Призначити роль групи",
@@ -44,6 +120,18 @@
       "menu_title": "Мій огляд"
     },
     "personal_notes": {
+      "card": {
+        "title": "Особиста нотатка"
+      },
+      "confirm_delete": "Видалити нотатку?",
+      "confirm_delete_explanation": "Нотатку \"{note}\" щодо {name} буду видалено.",
+      "confirm_delete_tardiness": "Запізнення {name} на {tardiness} хвилин буде вилучене.",
+      "create_personal_note": "Додати нотатку",
+      "late": "Запізни(в|ла)ся",
+      "lesson_length_exceeded": "Запізнення перевищує час уроку",
+      "minutes_late": "вчасно | на хвилину пізніше | на {n} хвилин пізніше",
+      "minutes_late_current": "вчасно (на основі поточного часу) | на хвилину пізніше (на основі поточного часу) | на {n} хвилин пізніше (на основі поточного часу)",
+      "note": "Нотатка",
       "tardiness": "Запізнення"
     },
     "persons": {
@@ -52,5 +140,9 @@
     "week": {
       "menu_title": "Поточний тиждень"
     }
+  },
+  "time": {
+    "minutes": "хвилини",
+    "minutes_n": "жодної хвилини | одна хвилина | {n} хвилин"
   }
 }
diff --git a/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po
index dcf1d42ca73b6368729531ed4b7798b1ace1f3e4..b19906e0d4115909a5a9821ecb5f1197d9c47891 100644
--- a/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -152,20 +152,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -277,7 +277,7 @@ msgid "Short name"
 msgstr ""
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
@@ -399,8 +399,8 @@ msgstr ""
 msgid "Participation touched at"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
-#: aleksis/apps/alsijil/models.py:886
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899
 msgid "Documentation"
 msgstr ""
 
@@ -408,19 +408,19 @@ msgstr ""
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:804
 msgid "Groups of Person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:804
+#: aleksis/apps/alsijil/models.py:817
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:816
+#: aleksis/apps/alsijil/models.py:829
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
@@ -430,94 +430,94 @@ msgstr ""
 msgid "Tardiness"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:891
+#: aleksis/apps/alsijil/models.py:904
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:893
+#: aleksis/apps/alsijil/models.py:906
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:900
+#: aleksis/apps/alsijil/models.py:913
 msgid "Personal Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:901
+#: aleksis/apps/alsijil/models.py:914
 msgid "Personal Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:912
+#: aleksis/apps/alsijil/models.py:925
 msgid "A person got assigned the same extra mark multiple times per documentation."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:925
+#: aleksis/apps/alsijil/models.py:938
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:926
+#: aleksis/apps/alsijil/models.py:939
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:947
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:953
+#: aleksis/apps/alsijil/models.py:966
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:965
+#: aleksis/apps/alsijil/models.py:978
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:980
+#: aleksis/apps/alsijil/models.py:993
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:981
+#: aleksis/apps/alsijil/models.py:994
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:988
+#: aleksis/apps/alsijil/models.py:1001
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:989
+#: aleksis/apps/alsijil/models.py:1002
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:990
+#: aleksis/apps/alsijil/models.py:1003
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:991
+#: aleksis/apps/alsijil/models.py:1004
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:992
+#: aleksis/apps/alsijil/models.py:1005
 msgid "Can list all personal note filters"
 msgstr ""
 
@@ -638,6 +638,14 @@ msgstr ""
 msgid "If you leave it empty, no group type will be used."
 msgstr ""
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+msgid "List of ParticipationStatus IDs"
+msgstr ""
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
diff --git a/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po
index 32ea0b2793090ad547ce81871145a6d11216904e..a7226345968939040572306c0e35e3637a98da66 100644
--- a/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: 2024-08-19 09:46+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/de/>\n"
@@ -43,12 +43,8 @@ msgstr "{} bittet Sie, einige Klassenbucheinträge zu überprüfen."
 
 #: aleksis/apps/alsijil/actions.py:86
 #, python-brace-format
-msgid ""
-"We have successfully sent notifications to {count_teachers} persons for "
-"{count_items} lessons."
-msgstr ""
-"Wir haben erfolgreich Benachrichtigungen an {count_teachers} Personen für "
-"{count_items} Stunden gesendet."
+msgid "We have successfully sent notifications to {count_teachers} persons for {count_items} lessons."
+msgstr "Wir haben erfolgreich Benachrichtigungen an {count_teachers} Personen für {count_items} Stunden gesendet."
 
 #: aleksis/apps/alsijil/actions.py:92
 msgid "Ask teacher to check data"
@@ -68,9 +64,7 @@ msgstr "Persönliche Notiz zurücksetzen"
 
 #: aleksis/apps/alsijil/data_checks.py:49
 msgid "Ensure that there are no personal notes in cancelled lessons"
-msgstr ""
-"Sicherstellen, dass es keine persönlichen Notizen in ausgefallenen Stunden "
-"gibt"
+msgstr "Sicherstellen, dass es keine persönlichen Notizen in ausgefallenen Stunden gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:50
 msgid "The personal note is related to a cancelled lesson."
@@ -78,9 +72,7 @@ msgstr "Die persönliche Notiz ist einer ausgefallenen Stunde zugeordnet."
 
 #: aleksis/apps/alsijil/data_checks.py:77
 msgid "Ensure that 'groups_of_person' is set for every personal note"
-msgstr ""
-"Sicherstellen, dass \"groups_of_person\" für alle persönlichen Notizen "
-"gesetzt ist"
+msgstr "Sicherstellen, dass \"groups_of_person\" für alle persönlichen Notizen gesetzt ist"
 
 #: aleksis/apps/alsijil/data_checks.py:78
 msgid "The personal note has no group in 'groups_of_person'."
@@ -88,9 +80,7 @@ msgstr "Die persönliche Notiz hat keine Gruppe in \"groups_of_person\"."
 
 #: aleksis/apps/alsijil/data_checks.py:103
 msgid "Ensure that there are no filled out lesson documentations on holidays"
-msgstr ""
-"Sicherstellen, dass es keine ausgefüllten Stundendokumentationen in den "
-"Ferien gibt"
+msgstr "Sicherstellen, dass es keine ausgefüllten Stundendokumentationen in den Ferien gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:104
 msgid "The lesson documentation is on holidays."
@@ -98,9 +88,7 @@ msgstr "Die Stundendokumentation ist in den Ferien."
 
 #: aleksis/apps/alsijil/data_checks.py:137
 msgid "Ensure that there are no filled out personal notes on holidays"
-msgstr ""
-"Sicherstellen, dass es keine ausgefüllten persönlichen Notizen in den Ferien "
-"gibt"
+msgstr "Sicherstellen, dass es keine ausgefüllten persönlichen Notizen in den Ferien gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:138
 msgid "The personal note is on holidays."
@@ -108,14 +96,11 @@ msgstr "Die persönliche Notiz ist in den Ferien."
 
 #: aleksis/apps/alsijil/data_checks.py:166
 msgid "Ensure that there are no excused personal notes without an absence"
-msgstr ""
-"Sicherstellen, dass es keine entschuldigten persönlichen Notizen ohne eine "
-"Absenz gibt"
+msgstr "Sicherstellen, dass es keine entschuldigten persönlichen Notizen ohne eine Absenz gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:167
 msgid "The personal note is marked as excused, but not as absent."
-msgstr ""
-"Die persönliche Notiz ist als entschuldigt, aber nicht als abwesend markiert."
+msgstr "Die persönliche Notiz ist als entschuldigt, aber nicht als abwesend markiert."
 
 #: aleksis/apps/alsijil/filters.py:12
 msgid "After"
@@ -131,7 +116,6 @@ msgstr "Bevor"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:220
-#: aleksis/apps/alsijil/models.py:478
 msgid "Subject"
 msgstr "Fach"
 
@@ -149,9 +133,7 @@ msgstr "Hausaufgabe zur nächsten Stunde"
 
 #: aleksis/apps/alsijil/forms.py:53
 msgid "Carry over data to all other lessons with the same subject in this week"
-msgstr ""
-"Daten zu allen weiteren Stunden mit dem gleichen Fach in dieser Woche "
-"übernehmen"
+msgstr "Daten zu allen weiteren Stunden mit dem gleichen Fach in dieser Woche übernehmen"
 
 #: aleksis/apps/alsijil/forms.py:98 aleksis/apps/alsijil/forms.py:278
 #: aleksis/apps/alsijil/forms.py:370
@@ -167,28 +149,24 @@ msgstr "Lehrkraft"
 
 #: aleksis/apps/alsijil/forms.py:121
 msgid "You can't select a group and a teacher both."
-msgstr ""
-"Es kann nur entweder eine Gruppe oder eine Lehrkraft ausgewählt werden."
+msgstr "Es kann nur entweder eine Gruppe oder eine Lehrkraft ausgewählt werden."
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
-#: aleksis/apps/alsijil/models.py:763 aleksis/apps/alsijil/models.py:822
 msgid "Person"
 msgstr "Person"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
-#: aleksis/apps/alsijil/models.py:903
 msgid "Start date"
 msgstr "Startdatum"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
-#: aleksis/apps/alsijil/models.py:907
 msgid "End date"
 msgstr "Enddatum"
 
@@ -224,7 +202,6 @@ msgstr "Entschuldigt"
 #: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
-#: aleksis/apps/alsijil/models.py:84 aleksis/apps/alsijil/models.py:260
 msgid "Excuse type"
 msgstr "Entschuldigungsart"
 
@@ -252,7 +229,6 @@ msgstr "Hat eine Stunden-Dokumentation"
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
 #: aleksis/apps/alsijil/util/alsijil_helpers.py:331
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
 msgid "Event"
 msgstr "Veranstaltung"
 
@@ -297,12 +273,11 @@ msgid "Can register an absence for a person"
 msgstr "Kann eine Absenz für eine Person registrieren"
 
 #: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/models.py:437
 msgid "Short name"
 msgstr "Kurzname"
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
@@ -310,29 +285,21 @@ msgid "Name"
 msgstr "Name"
 
 #: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
-#: aleksis/apps/alsijil/models.py:68
 msgid "Count as absent"
 msgstr "Als abwesend zählen"
 
-#: aleksis/apps/alsijil/models.py:71 aleksis/apps/alsijil/models.py:70
-msgid ""
-"If checked, this excuse type will be counted as a missed lesson. If not "
-"checked,it won't show up in the absence report."
-msgstr ""
-"Wenn ausgewählt wird diese Entschuldigungsart als eine verpasste Stunde "
-"gezählt. Wenn nicht ausgewählt wird es nicht im Abwesenheitsbericht "
-"auftauchen."
+#: aleksis/apps/alsijil/models.py:71
+msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
+msgstr "Wenn ausgewählt wird diese Entschuldigungsart als eine verpasste Stunde gezählt. Wenn nicht ausgewählt wird es nicht im Abwesenheitsbericht auftauchen."
 
 #: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
-#: aleksis/apps/alsijil/models.py:85
 msgid "Excuse types"
 msgstr "Entschuldigungsarten"
 
 #: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
-#: aleksis/apps/alsijil/models.py:240 aleksis/apps/alsijil/models.py:339
 msgid "Year"
 msgstr "Jahr"
 
@@ -342,11 +309,10 @@ msgstr "Jahr"
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:331
-#: aleksis/apps/alsijil/models.py:265 aleksis/apps/alsijil/models.py:455
 msgid "Extra marks"
 msgstr "Zusätzliche Markierungen"
 
-#: aleksis/apps/alsijil/models.py:300 aleksis/apps/alsijil/models.py:299
+#: aleksis/apps/alsijil/models.py:300
 msgid "Personal note"
 msgstr "Persönliche Notiz"
 
@@ -354,7 +320,6 @@ msgstr "Persönliche Notiz"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
-#: aleksis/apps/alsijil/models.py:300
 msgid "Personal notes"
 msgstr "Persönliche Notizen"
 
@@ -364,7 +329,6 @@ msgstr "Persönliche Notizen"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:25
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:421
-#: aleksis/apps/alsijil/models.py:351
 msgid "Lesson topic"
 msgstr "Stundenthema"
 
@@ -374,7 +338,6 @@ msgstr "Stundenthema"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:33
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:422
-#: aleksis/apps/alsijil/models.py:352 aleksis/apps/alsijil/models.py:490
 msgid "Homework"
 msgstr "Hausaufgaben"
 
@@ -383,40 +346,37 @@ msgstr "Hausaufgaben"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:41
-#: aleksis/apps/alsijil/models.py:353
 msgid "Group note"
 msgstr "Gruppennotiz"
 
 #: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
-#: aleksis/apps/alsijil/models.py:403
 msgid "Lesson documentation"
 msgstr "Stunden-Dokumentation"
 
 #: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
-#: aleksis/apps/alsijil/models.py:404
 msgid "Lesson documentations"
 msgstr "Stunden-Dokumentationen"
 
-#: aleksis/apps/alsijil/models.py:441 aleksis/apps/alsijil/models.py:440
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
 msgstr "Vordergrundfarbe"
 
-#: aleksis/apps/alsijil/models.py:442 aleksis/apps/alsijil/models.py:441
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
 msgstr "Hintergrundfarbe"
 
-#: aleksis/apps/alsijil/models.py:444 aleksis/apps/alsijil/models.py:443
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
 msgstr "In Kursbuch anzeigen"
 
-#: aleksis/apps/alsijil/models.py:455 aleksis/apps/alsijil/models.py:454
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
 msgstr "Zusätzliche Markierung"
 
-#: aleksis/apps/alsijil/models.py:475 aleksis/apps/alsijil/models.py:474
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
 msgstr "Kurs"
 
@@ -424,350 +384,270 @@ msgstr "Kurs"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
-#: aleksis/apps/alsijil/models.py:486
 msgid "Teachers"
 msgstr "Lehrkräfte"
 
-#: aleksis/apps/alsijil/models.py:490 aleksis/apps/alsijil/models.py:489
+#: aleksis/apps/alsijil/models.py:490
 msgid "Lesson Topic"
 msgstr "Stundenthema"
 
-#: aleksis/apps/alsijil/models.py:492 aleksis/apps/alsijil/models.py:491
+#: aleksis/apps/alsijil/models.py:492
 msgid "Group Note"
 msgstr "Gruppennotiz"
 
-#: aleksis/apps/alsijil/models.py:496 aleksis/apps/alsijil/models.py:495
+#: aleksis/apps/alsijil/models.py:496
 msgid "Participation touched at"
 msgstr "Teilnahmestatus angelegt am"
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
-#: aleksis/apps/alsijil/models.py:886 aleksis/apps/alsijil/models.py:524
-#: aleksis/apps/alsijil/models.py:773 aleksis/apps/alsijil/models.py:829
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899
 msgid "Documentation"
 msgstr "Dokumentation"
 
-#: aleksis/apps/alsijil/models.py:526 aleksis/apps/alsijil/models.py:525
+#: aleksis/apps/alsijil/models.py:526
 msgid "Documentations"
 msgstr "Dokumentationen"
 
-#: aleksis/apps/alsijil/models.py:791 aleksis/apps/alsijil/models.py:766
+#: aleksis/apps/alsijil/models.py:804
 msgid "Groups of Person"
 msgstr "Gruppen der Person"
 
-#: aleksis/apps/alsijil/models.py:804 aleksis/apps/alsijil/models.py:779
+#: aleksis/apps/alsijil/models.py:817
 msgid "Absence Reason"
 msgstr "Abwesenheitsgrund"
 
-#: aleksis/apps/alsijil/models.py:816 aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:829
 msgid "Base Absence"
 msgstr "Basis-Abwesenheit"
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-#: aleksis/apps/alsijil/models.py:794
 msgid "Tardiness"
 msgstr "Verspätung"
 
-#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
-#: aleksis/apps/alsijil/models.py:805 aleksis/apps/alsijil/models.py:806
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 msgid "Participation Status"
 msgstr "Teilnahmestatus"
 
-#: aleksis/apps/alsijil/models.py:891 aleksis/apps/alsijil/models.py:834
+#: aleksis/apps/alsijil/models.py:904
 msgid "Note"
 msgstr "Notiz"
 
-#: aleksis/apps/alsijil/models.py:893 aleksis/apps/alsijil/models.py:836
+#: aleksis/apps/alsijil/models.py:906
 msgid "Extra Mark"
 msgstr "Zusätzliche Markierung"
 
-#: aleksis/apps/alsijil/models.py:900 aleksis/apps/alsijil/models.py:843
+#: aleksis/apps/alsijil/models.py:913
 msgid "Personal Note"
 msgstr "Persönliche Notiz"
 
-#: aleksis/apps/alsijil/models.py:901 aleksis/apps/alsijil/models.py:844
+#: aleksis/apps/alsijil/models.py:914
 msgid "Personal Notes"
 msgstr "Persönliche Notizen"
 
-#: aleksis/apps/alsijil/models.py:912 aleksis/apps/alsijil/models.py:855
-msgid ""
-"A person got assigned the same extra mark multiple times per documentation."
-msgstr ""
-"Eine Person hat die gleiche zusätzliche Markierung für eine Dokumentation "
-"mehrfach zugeordnet bekommen."
+#: aleksis/apps/alsijil/models.py:925
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr "Eine Person hat die gleiche zusätzliche Markierung für eine Dokumentation mehrfach zugeordnet bekommen."
 
-#: aleksis/apps/alsijil/models.py:925 aleksis/apps/alsijil/models.py:868
+#: aleksis/apps/alsijil/models.py:938
 msgid "Icon"
 msgstr "Symbol"
 
-#: aleksis/apps/alsijil/models.py:926 aleksis/apps/alsijil/models.py:869
+#: aleksis/apps/alsijil/models.py:939
 msgid "Colour"
 msgstr "Farbe"
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
-#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:890
 msgid "Group role"
 msgstr "Gruppenrolle"
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
-#: aleksis/apps/alsijil/models.py:876
 msgid "Group roles"
 msgstr "Gruppenrollen"
 
-#: aleksis/apps/alsijil/models.py:934 aleksis/apps/alsijil/models.py:877
+#: aleksis/apps/alsijil/models.py:947
 msgid "Can assign group role"
 msgstr "Kann Gruppenrolle zuweisen"
 
-#: aleksis/apps/alsijil/models.py:953 aleksis/apps/alsijil/models.py:896
+#: aleksis/apps/alsijil/models.py:966
 msgid "Assigned person"
 msgstr "Zugewiesene Person"
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
-#: aleksis/apps/alsijil/models.py:901
 msgid "Groups"
 msgstr "Gruppen"
 
-#: aleksis/apps/alsijil/models.py:965 aleksis/apps/alsijil/models.py:908
+#: aleksis/apps/alsijil/models.py:978
 msgid "Can be left empty if end date is not clear yet"
 msgstr "Kann frei gelassen werden, wenn das Enddatum noch nicht feststeht"
 
-#: aleksis/apps/alsijil/models.py:980 aleksis/apps/alsijil/models.py:923
+#: aleksis/apps/alsijil/models.py:993
 msgid "Group role assignment"
 msgstr "Zuweisung von Gruppenrollen"
 
-#: aleksis/apps/alsijil/models.py:981 aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:994
 msgid "Group role assignments"
 msgstr "Zuweisungen von Gruppenrollen"
 
-#: aleksis/apps/alsijil/models.py:988 aleksis/apps/alsijil/models.py:931
+#: aleksis/apps/alsijil/models.py:1001
 msgid "Can view lesson overview"
 msgstr "Kann die Stundenübersicht sehen"
 
-#: aleksis/apps/alsijil/models.py:989 aleksis/apps/alsijil/models.py:932
+#: aleksis/apps/alsijil/models.py:1002
 msgid "Can view week overview"
 msgstr "Kann die Wochenübersicht sehen"
 
-#: aleksis/apps/alsijil/models.py:990 aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:1003
 msgid "Can view full register"
 msgstr "Kann komplettes Klassenbuch sehen"
 
-#: aleksis/apps/alsijil/models.py:991 aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:1004
 msgid "Can register absence"
 msgstr "Kann eine Absenz registrieren"
 
-#: aleksis/apps/alsijil/models.py:992 aleksis/apps/alsijil/models.py:935
+#: aleksis/apps/alsijil/models.py:1005
 msgid "Can list all personal note filters"
 msgstr "Kann alle Filter für persönliche Notizen anzeigen"
 
 #: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
-#: aleksis/apps/alsijil/preferences.py:9
 msgid "Class register"
 msgstr "Klassenbuch"
 
 #: aleksis/apps/alsijil/preferences.py:24
-#: aleksis/apps/alsijil/preferences.py:17
 msgid "Block adding personal notes for cancelled lessons"
-msgstr ""
-"Blockiere das Hinzufügen von persönlichen Notizen für ausgefallene Stunden"
+msgstr "Blockiere das Hinzufügen von persönlichen Notizen für ausgefallene Stunden"
 
 #: aleksis/apps/alsijil/preferences.py:32
-#: aleksis/apps/alsijil/preferences.py:25
 msgid "Allow users to view their own personal notes"
 msgstr "Erlaube Benutzern, ihre eigenen persönlichen Notizen zu sehen"
 
 #: aleksis/apps/alsijil/preferences.py:41
-#: aleksis/apps/alsijil/preferences.py:34
-msgid ""
-"Allow primary group owners to register future absences for students in their "
-"groups"
-msgstr ""
-"Erlaube Primärgruppeninhabern Absenzen in der Zukunft für Mitglieder ihrer "
-"Gruppen zu registrieren"
+msgid "Allow primary group owners to register future absences for students in their groups"
+msgstr "Erlaube Primärgruppeninhabern Absenzen in der Zukunft für Mitglieder ihrer Gruppen zu registrieren"
 
 #: aleksis/apps/alsijil/preferences.py:51
-#: aleksis/apps/alsijil/preferences.py:44
-msgid ""
-"Grant the owner of a parent group the same privileges as the owners of the "
-"respective child groups"
-msgstr ""
-"Gebe dem Besitzer einer Elterngruppe die gleichen Rechte wie den Besitzern "
-"der entsprechenden Kindgruppen"
+msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
+msgstr "Gebe dem Besitzer einer Elterngruppe die gleichen Rechte wie den Besitzern der entsprechenden Kindgruppen"
 
 #: aleksis/apps/alsijil/preferences.py:61
-#: aleksis/apps/alsijil/preferences.py:54
-msgid ""
-"Allow original teachers to edit their lessons although they are substituted"
-msgstr ""
-"Erlaube den Ursprungslehrkräften, ihre Stunden zu bearbeiten, obwohl sie "
-"vertreten worden sind"
+msgid "Allow original teachers to edit their lessons although they are substituted"
+msgstr "Erlaube den Ursprungslehrkräften, ihre Stunden zu bearbeiten, obwohl sie vertreten worden sind"
 
 #: aleksis/apps/alsijil/preferences.py:70
-#: aleksis/apps/alsijil/preferences.py:63
-msgid ""
-"Carry over data from first lesson period to the following lesson periods in "
-"lessons over multiple periods"
+msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr "Daten von der ersten Stunde zu weiteren folgenden Stunden übernehmen"
 
 #: aleksis/apps/alsijil/preferences.py:73
-#: aleksis/apps/alsijil/preferences.py:66
-msgid ""
-"This will carry over data only if the data in the following periods are "
-"empty."
-msgstr ""
-"Dies wird die Daten nur übernehmen, wenn die Daten in den Folgestunden leer "
-"sind."
+msgid "This will carry over data only if the data in the following periods are empty."
+msgstr "Dies wird die Daten nur übernehmen, wenn die Daten in den Folgestunden leer sind."
 
 #: aleksis/apps/alsijil/preferences.py:82
-#: aleksis/apps/alsijil/preferences.py:75
-msgid ""
-"Allow carrying over data from any lesson period to all other "
-"lesson                 periods with the same lesson and in the same week"
-msgstr ""
-"Erlaube das Ãœbernehmen von Daten von einer Stunde zu allen weiteren Stunden "
-"mit dem gleichen Unterricht in der gleichen Woche"
+msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
+msgstr "Erlaube das Ãœbernehmen von Daten von einer Stunde zu allen weiteren Stunden mit dem gleichen Unterricht in der gleichen Woche"
 
 #: aleksis/apps/alsijil/preferences.py:86
-#: aleksis/apps/alsijil/preferences.py:79
-msgid ""
-"This will carry over data only if the data in the aforementioned periods are "
-"empty."
-msgstr ""
-"Dies wird die Daten nur übernehmen, wenn die Daten in den eben genannten "
-"Stunden leer sind."
+msgid "This will carry over data only if the data in the aforementioned periods are empty."
+msgstr "Dies wird die Daten nur übernehmen, wenn die Daten in den eben genannten Stunden leer sind."
 
 #: aleksis/apps/alsijil/preferences.py:95
-#: aleksis/apps/alsijil/preferences.py:88
-msgid ""
-"Carry over personal notes to all following lesson periods on the same day."
-msgstr ""
-"Persönliche Notizen in alle folgenden Unterrichtsstunden am gleichen Tag "
-"übernehmen."
+msgid "Carry over personal notes to all following lesson periods on the same day."
+msgstr "Persönliche Notizen in alle folgenden Unterrichtsstunden am gleichen Tag übernehmen."
 
 #: aleksis/apps/alsijil/preferences.py:104
-#: aleksis/apps/alsijil/preferences.py:97
-msgid ""
-"Allow teachers to open lesson periods on the same day and not just at the "
-"beginning of the period"
-msgstr ""
-"Erlaube Lehrkräften, Unterrichtsstunden bereits am gleichen Tag und nicht "
-"erst zu Beginn der Stunde zu öffnen"
+msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
+msgstr "Erlaube Lehrkräften, Unterrichtsstunden bereits am gleichen Tag und nicht erst zu Beginn der Stunde zu öffnen"
 
 #: aleksis/apps/alsijil/preferences.py:108
-#: aleksis/apps/alsijil/preferences.py:101
-msgid ""
-"Lessons in the past are not affected by this setting, you can open them "
-"whenever you want."
-msgstr ""
-"Unterrichtsstunden in der Vergangenheit werden nicht durch diese Einstellung "
-"beeinflusst, sie können immer geöffnet werden."
+msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
+msgstr "Unterrichtsstunden in der Vergangenheit werden nicht durch diese Einstellung beeinflusst, sie können immer geöffnet werden."
 
 #: aleksis/apps/alsijil/preferences.py:117
-#: aleksis/apps/alsijil/preferences.py:110
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr "Lehrkräften erlauben, Daten für Stunden in den Ferien hinzuzufügen"
 
 #: aleksis/apps/alsijil/preferences.py:126
-#: aleksis/apps/alsijil/preferences.py:119
-msgid ""
-"Allow group owners to assign group roles to the parents of the group's "
-"members"
-msgstr ""
-"Erlaube Gruppenbesitzern, Gruppenrollen für Eltern von Gruppenmitgliedern "
-"zuzuweisen"
+msgid "Allow group owners to assign group roles to the parents of the group's members"
+msgstr "Erlaube Gruppenbesitzern, Gruppenrollen für Eltern von Gruppenmitgliedern zuzuweisen"
 
 #: aleksis/apps/alsijil/preferences.py:135
-#: aleksis/apps/alsijil/preferences.py:128
 msgid "Show assigned group roles in week view"
 msgstr "Zugewiesene Gruppenrollen in der Wochenansicht zeigen"
 
 #: aleksis/apps/alsijil/preferences.py:136
-#: aleksis/apps/alsijil/preferences.py:129
 msgid "Only week view of groups"
 msgstr "Nur Wochenansicht von Gruppen"
 
 #: aleksis/apps/alsijil/preferences.py:144
-#: aleksis/apps/alsijil/preferences.py:137
 msgid "Show assigned group roles in lesson view"
 msgstr "Zugewiesene Gruppenrollen in der Stundenansicht anzeigen"
 
 #: aleksis/apps/alsijil/preferences.py:154
-#: aleksis/apps/alsijil/preferences.py:147
 msgid "Items per page in lessons table"
 msgstr "Einträge pro Seite in der Stundentabelle"
 
 #: aleksis/apps/alsijil/preferences.py:158
-#: aleksis/apps/alsijil/preferences.py:151
 msgid "Each page must show at least one item."
 msgstr "Jede Seite muss mindestens einen Eintrag anzeigen."
 
 #: aleksis/apps/alsijil/preferences.py:166
-#: aleksis/apps/alsijil/preferences.py:159
 msgid "Filter lessons by existence of their lesson documentation on default"
-msgstr ""
-"Stunden standardmäßig anhand der Existenz ihrer Stundendokumentation filtern"
+msgstr "Stunden standardmäßig anhand der Existenz ihrer Stundendokumentation filtern"
 
 #: aleksis/apps/alsijil/preferences.py:177
-#: aleksis/apps/alsijil/preferences.py:170
 msgid "Allow editing of all future documentations"
 msgstr "Bearbeiten von allen zukünftigen Dokumentationen erlauben"
 
 #: aleksis/apps/alsijil/preferences.py:180
-#: aleksis/apps/alsijil/preferences.py:173
-msgid ""
-"Allow editing of all documentations up to and including those on the current "
-"day"
-msgstr ""
-"Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag erlauben"
+msgid "Allow editing of all documentations up to and including those on the current day"
+msgstr "Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag erlauben"
 
 #: aleksis/apps/alsijil/preferences.py:185
-#: aleksis/apps/alsijil/preferences.py:178
-msgid ""
-"Allow editing of all documentations up to and including those on the current "
-"date and time"
-msgstr ""
-"Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag und zur "
-"aktuellen Uhrzeit erlauben"
+msgid "Allow editing of all documentations up to and including those on the current date and time"
+msgstr "Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag und zur aktuellen Uhrzeit erlauben"
 
 #: aleksis/apps/alsijil/preferences.py:190
-#: aleksis/apps/alsijil/preferences.py:183
 msgid "Set time range for which documentations may be edited"
 msgstr "Zeitraum setzen, in dem Dokumentationen bearbeitet werden dürfen"
 
 #: aleksis/apps/alsijil/preferences.py:201
-msgid ""
-"User is allowed to register absences for members of groups the user is an "
-"owner of with these group types"
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
 msgstr "Benutzer*innen dürfen Abwesenheiten für Mitglieder von Gruppen mit diesen Gruppentypen, in denen sie Besitzer*innen sind, registrieren"
 
 #: aleksis/apps/alsijil/preferences.py:205
-msgid ""
-"If you leave it empty, all member of groups the user is an owner of will be "
-"shown."
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
 msgstr "Wenn Sie es leer lassen, werden alle Mitglieder von Gruppen angezeigt, in denen Benutzer*innen Besitzer*innen sind."
 
 #: aleksis/apps/alsijil/preferences.py:217
-msgid ""
-"Group type of groups to be shown first in the group select field on the "
-"coursebook overview page"
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
 msgstr "Gruppentyp von Gruppen, die zuerst im Gruppenauswahlfeld auf der Kursbuchübersichtsseite angezeigt werden"
 
 #: aleksis/apps/alsijil/preferences.py:220
 msgid "If you leave it empty, no group type will be used."
 msgstr "Wenn Sie es leer lassen, wird kein Gruppentyp benutzt."
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+#, fuzzy
+#| msgid "Participation Status"
+msgid "List of ParticipationStatus IDs"
+msgstr "Teilnahmestatus"
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
@@ -868,13 +748,11 @@ msgstr " %(count)s betroffene Stunden "
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:40
 msgid ""
 "\n"
-"                  There are no affected lessons. Registering this absence "
-"won't have any effect.\n"
+"                  There are no affected lessons. Registering this absence won't have any effect.\n"
 "                "
 msgstr ""
 "\n"
-"                  Es gibt keine betroffenen Stunden. Das Eintragen dieser "
-"Abwesenheit wird keinen Effekt haben.\n"
+"                  Es gibt keine betroffenen Stunden. Das Eintragen dieser Abwesenheit wird keinen Effekt haben.\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:57
@@ -972,8 +850,7 @@ msgid ""
 "                "
 msgstr ""
 "\n"
-"                  Diese Stunde ist in den Ferien und kann somit nicht "
-"bearbeitet werden.\n"
+"                  Diese Stunde ist in den Ferien und kann somit nicht bearbeitet werden.\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:10
@@ -1112,13 +989,11 @@ msgstr "Keine Stunden verfügbar"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:434
 msgid ""
 "\n"
-"            There are no lessons for the selected group or teacher in this "
-"week.\n"
+"            There are no lessons for the selected group or teacher in this week.\n"
 "          "
 msgstr ""
 "\n"
-"            Es gibt keine Stunden für die ausgewählte Gruppe oder Lehrkraft "
-"in dieser Woche.\n"
+"            Es gibt keine Stunden für die ausgewählte Gruppe oder Lehrkraft in dieser Woche.\n"
 "          "
 
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/create.html:6
@@ -1136,18 +1011,13 @@ msgstr "Entschuldigungsart bearbeiten"
 #: aleksis/apps/alsijil/templates/alsijil/group_role/warning.html:4
 msgid ""
 "\n"
-"    This function should only be used to define alternatives to the default "
-"excuse which also will be counted extra.\n"
-"    Don't use this to create a default excuse or if you don't divide between "
-"different types of excuse.\n"
+"    This function should only be used to define alternatives to the default excuse which also will be counted extra.\n"
+"    Don't use this to create a default excuse or if you don't divide between different types of excuse.\n"
 "  "
 msgstr ""
 "\n"
-"    Diese Funktion sollte nur benutzt werden, um Alternativen zur normalen "
-"Entschuldigung, welche von sich aus extra gezählt wird, zu definieren.\n"
-"Benutzen Sie diese Funktion nicht, um eine Entschuldigungsart für normale "
-"Entschuldigungen zu erstellen oder wenn Sie nicht zwischen verschiedenen "
-"Entschuldigungsarten unterscheiden möchten.\n"
+"    Diese Funktion sollte nur benutzt werden, um Alternativen zur normalen Entschuldigung, welche von sich aus extra gezählt wird, zu definieren.\n"
+"Benutzen Sie diese Funktion nicht, um eine Entschuldigungsart für normale Entschuldigungen zu erstellen oder wenn Sie nicht zwischen verschiedenen Entschuldigungsarten unterscheiden möchten.\n"
 "  "
 
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
@@ -1215,14 +1085,12 @@ msgstr "Niemand zugewiesen."
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assigned_roles.html:41
 msgid ""
 "\n"
-"    You can get some additional actions for each group role assignment if "
-"you click on the name of the\n"
+"    You can get some additional actions for each group role assignment if you click on the name of the\n"
 "    corresponding person.\n"
 "  "
 msgstr ""
 "\n"
-"    Sie können zusätzliche Aktionen für jede Gruppenrollenzuweisung "
-"aufrufen, \n"
+"    Sie können zusätzliche Aktionen für jede Gruppenrollenzuweisung aufrufen, \n"
 "wenn Sie auf den Namen der entsprechenden Person klicken.\n"
 "  "
 
@@ -1231,12 +1099,8 @@ msgid "Stop"
 msgstr "Beenden"
 
 #: aleksis/apps/alsijil/templates/alsijil/notifications/check.html:1
-msgid ""
-"Please check if the following class register entries are complete and "
-"correct:"
-msgstr ""
-"Bitte prüfen Sie, ob die folgenden Klassenbucheinträge komplett und richtig "
-"sind:"
+msgid "Please check if the following class register entries are complete and correct:"
+msgstr "Bitte prüfen Sie, ob die folgenden Klassenbucheinträge komplett und richtig sind:"
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/absences.html:6
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:22
@@ -1396,17 +1260,13 @@ msgstr ""
 #, python-format
 msgid ""
 "\n"
-"            This seating plan is taken from the parent group of "
-"%(child_group)s.\n"
-"            If you want, you can take it over for your group and then "
-"customize it.\n"
+"            This seating plan is taken from the parent group of %(child_group)s.\n"
+"            If you want, you can take it over for your group and then customize it.\n"
 "          "
 msgstr ""
 "\n"
-"            Dieser Sitzplan wurde von der Elterngruppe von %(child_group)s "
-"übernommen.\n"
-"Wenn Sie wollen, können Sie ihn für Ihre Gruppe übernehmen und dann "
-"anpassen.\n"
+"            Dieser Sitzplan wurde von der Elterngruppe von %(child_group)s übernommen.\n"
+"Wenn Sie wollen, können Sie ihn für Ihre Gruppe übernehmen und dann anpassen.\n"
 "          "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:30
@@ -1425,13 +1285,11 @@ msgstr "Es gibt keinen Sitzplan für diese Stunde."
 #, python-format
 msgid ""
 "\n"
-"                  Create a new seating plan for %(group)s (%(subject)s) in "
-"%(room)s\n"
+"                  Create a new seating plan for %(group)s (%(subject)s) in %(room)s\n"
 "                "
 msgstr ""
 "\n"
-"                  Einen neuen Sitzplan für %(group)s (%(subject)s) in "
-"%(room)s erstellen\n"
+"                  Einen neuen Sitzplan für %(group)s (%(subject)s) in %(room)s erstellen\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:78
@@ -1442,8 +1300,7 @@ msgid ""
 "                  "
 msgstr ""
 "\n"
-"                    Einen neuen Sitzplan für %(group)s in %(room)s "
-"erstellen\n"
+"                    Einen neuen Sitzplan für %(group)s in %(room)s erstellen\n"
 "                  "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:6
@@ -1550,10 +1407,8 @@ msgid ""
 "      "
 msgstr ""
 "\n"
-"        Kopien des Klassenbuches, sowohl digital als auch als Ausdruck, "
-"dürfen\n"
-"            ausschließlich in der Schule und/oder auf von der Schule "
-"autorisierten Geräten\n"
+"        Kopien des Klassenbuches, sowohl digital als auch als Ausdruck, dürfen\n"
+"            ausschließlich in der Schule und/oder auf von der Schule autorisierten Geräten\n"
 "            gespeichert werden.\n"
 "      "
 
@@ -1565,8 +1420,7 @@ msgid ""
 "      "
 msgstr ""
 "\n"
-"        Die Leitung der Gruppe sowie die Schulleitung bestätigen die obigen "
-"Hinweise sowie\n"
+"        Die Leitung der Gruppe sowie die Schulleitung bestätigen die obigen Hinweise sowie\n"
 "            die Richtigkeit des Ausdrucks.\n"
 "      "
 
@@ -1699,20 +1553,14 @@ msgid "Notes"
 msgstr "Notizen"
 
 #: aleksis/apps/alsijil/views.py:112
-msgid ""
-"You either selected an invalid lesson or there is currently no lesson in "
-"progress."
+msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr ""
 "Sie haben eine ungültige Stunde ausgewählt oder es\n"
 "      läuft momentan keine Stunde."
 
 #: aleksis/apps/alsijil/views.py:145
-msgid ""
-"You are not allowed to create a lesson documentation for a lesson in the "
-"future."
-msgstr ""
-"Ihnen ist es nicht erlaubt, eine Eintragung für eine Unterrichtsstunde in "
-"der Zukunft vorzunehmen."
+msgid "You are not allowed to create a lesson documentation for a lesson in the future."
+msgstr "Ihnen ist es nicht erlaubt, eine Eintragung für eine Unterrichtsstunde in der Zukunft vorzunehmen."
 
 #: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
diff --git a/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po
index 37bfd8669e34b3235d3f1de6bbd93d5402b9aacf..e55b8425a72abdf35161d085119ea8c66480aa50 100644
--- a/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: 2021-06-16 11:59+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: French <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/fr/>\n"
@@ -168,20 +168,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr "Personne"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr "Date de début"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr "Date de fin"
@@ -313,7 +313,7 @@ msgid "Short name"
 msgstr "Prénom"
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
@@ -447,8 +447,8 @@ msgstr "Groupe"
 msgid "Participation touched at"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
-#: aleksis/apps/alsijil/models.py:886
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899
 #, fuzzy
 #| msgid "Lesson documentation"
 msgid "Documentation"
@@ -460,25 +460,25 @@ msgstr "Documentation de cours"
 msgid "Documentations"
 msgstr "Documentation de cours"
 
-#: aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:804
 #, fuzzy
 #| msgid "Group"
 msgid "Groups of Person"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:804
+#: aleksis/apps/alsijil/models.py:817
 #, fuzzy
 #| msgid "Absences"
 msgid "Absence Reason"
 msgstr "Absences"
 
-#: aleksis/apps/alsijil/models.py:816
+#: aleksis/apps/alsijil/models.py:829
 #, fuzzy
 #| msgid "Absences"
 msgid "Base Absence"
 msgstr "Absences"
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
@@ -488,52 +488,52 @@ msgstr "Absences"
 msgid "Tardiness"
 msgstr "Retard"
 
-#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:891
+#: aleksis/apps/alsijil/models.py:904
 #, fuzzy
 #| msgid "Notes"
 msgid "Note"
 msgstr "Notes"
 
-#: aleksis/apps/alsijil/models.py:893
+#: aleksis/apps/alsijil/models.py:906
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:900
+#: aleksis/apps/alsijil/models.py:913
 #, fuzzy
 #| msgid "Personal notes"
 msgid "Personal Note"
 msgstr "Notes personnelles"
 
-#: aleksis/apps/alsijil/models.py:901
+#: aleksis/apps/alsijil/models.py:914
 #, fuzzy
 #| msgid "Personal notes"
 msgid "Personal Notes"
 msgstr "Notes personnelles"
 
-#: aleksis/apps/alsijil/models.py:912
+#: aleksis/apps/alsijil/models.py:925
 msgid "A person got assigned the same extra mark multiple times per documentation."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:925
+#: aleksis/apps/alsijil/models.py:938
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:926
+#: aleksis/apps/alsijil/models.py:939
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 #, fuzzy
 #| msgid "Group"
 msgid "Group role"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
@@ -542,19 +542,19 @@ msgstr "Groupe"
 msgid "Group roles"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:947
 #, fuzzy
 #| msgid "Persons in group"
 msgid "Can assign group role"
 msgstr "Personnes en groupe"
 
-#: aleksis/apps/alsijil/models.py:953
+#: aleksis/apps/alsijil/models.py:966
 #, fuzzy
 #| msgid "Absences"
 msgid "Assigned person"
 msgstr "Absences"
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
@@ -563,41 +563,41 @@ msgstr "Absences"
 msgid "Groups"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:965
+#: aleksis/apps/alsijil/models.py:978
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:980
+#: aleksis/apps/alsijil/models.py:993
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:981
+#: aleksis/apps/alsijil/models.py:994
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:988
+#: aleksis/apps/alsijil/models.py:1001
 #, fuzzy
 #| msgid "Personal overview"
 msgid "Can view lesson overview"
 msgstr "Vue d'ensemble personnelle"
 
-#: aleksis/apps/alsijil/models.py:989
+#: aleksis/apps/alsijil/models.py:1002
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:990
+#: aleksis/apps/alsijil/models.py:1003
 #, fuzzy
 #| msgid "Class register"
 msgid "Can view full register"
 msgstr "Registre de la classe"
 
-#: aleksis/apps/alsijil/models.py:991
+#: aleksis/apps/alsijil/models.py:1004
 #, fuzzy
 #| msgid "Register absence"
 msgid "Can register absence"
 msgstr "Registre de Absence"
 
-#: aleksis/apps/alsijil/models.py:992
+#: aleksis/apps/alsijil/models.py:1005
 #, fuzzy
 #| msgid "List of all personal note filters"
 msgid "Can list all personal note filters"
@@ -722,6 +722,14 @@ msgstr ""
 msgid "If you leave it empty, no group type will be used."
 msgstr ""
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+msgid "List of ParticipationStatus IDs"
+msgstr ""
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
diff --git a/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po
index ffbc44cdc05e07e5e46c4578ae0e7db6a6fd31fa..0033b6f01cea4f36709faf87ecad3bf58e193743 100644
--- a/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: 2020-07-26 14:08+0000\n"
 "Last-Translator: Julian <leuckerj@gmail.com>\n"
 "Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/la/>\n"
@@ -152,20 +152,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr "Persona"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -279,7 +279,7 @@ msgid "Short name"
 msgstr "Primus nomen"
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
@@ -407,8 +407,8 @@ msgstr "Grex"
 msgid "Participation touched at"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
-#: aleksis/apps/alsijil/models.py:886
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899
 msgid "Documentation"
 msgstr ""
 
@@ -416,21 +416,21 @@ msgstr ""
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:804
 #, fuzzy
 #| msgid "Group"
 msgid "Groups of Person"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:804
+#: aleksis/apps/alsijil/models.py:817
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:816
+#: aleksis/apps/alsijil/models.py:829
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
@@ -440,50 +440,50 @@ msgstr ""
 msgid "Tardiness"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:891
+#: aleksis/apps/alsijil/models.py:904
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:893
+#: aleksis/apps/alsijil/models.py:906
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:900
+#: aleksis/apps/alsijil/models.py:913
 #, fuzzy
 #| msgid "Person"
 msgid "Personal Note"
 msgstr "Persona"
 
-#: aleksis/apps/alsijil/models.py:901
+#: aleksis/apps/alsijil/models.py:914
 #, fuzzy
 #| msgid "Person"
 msgid "Personal Notes"
 msgstr "Persona"
 
-#: aleksis/apps/alsijil/models.py:912
+#: aleksis/apps/alsijil/models.py:925
 msgid "A person got assigned the same extra mark multiple times per documentation."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:925
+#: aleksis/apps/alsijil/models.py:938
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:926
+#: aleksis/apps/alsijil/models.py:939
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 #, fuzzy
 #| msgid "Group"
 msgid "Group role"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
@@ -492,15 +492,15 @@ msgstr "Grex"
 msgid "Group roles"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:947
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:953
+#: aleksis/apps/alsijil/models.py:966
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
@@ -509,35 +509,35 @@ msgstr ""
 msgid "Groups"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:965
+#: aleksis/apps/alsijil/models.py:978
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:980
+#: aleksis/apps/alsijil/models.py:993
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:981
+#: aleksis/apps/alsijil/models.py:994
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:988
+#: aleksis/apps/alsijil/models.py:1001
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:989
+#: aleksis/apps/alsijil/models.py:1002
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:990
+#: aleksis/apps/alsijil/models.py:1003
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:991
+#: aleksis/apps/alsijil/models.py:1004
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:992
+#: aleksis/apps/alsijil/models.py:1005
 msgid "Can list all personal note filters"
 msgstr ""
 
@@ -658,6 +658,14 @@ msgstr ""
 msgid "If you leave it empty, no group type will be used."
 msgstr ""
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+msgid "List of ParticipationStatus IDs"
+msgstr ""
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
diff --git a/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po
index 40892069d224a5e6ef62ed3a0ce7fe80720ca876..75da0e9e55e72bc44d6c90c74e7ac69b89867b54 100644
--- a/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -151,20 +151,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -276,7 +276,7 @@ msgid "Short name"
 msgstr ""
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
@@ -398,8 +398,8 @@ msgstr ""
 msgid "Participation touched at"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
-#: aleksis/apps/alsijil/models.py:886
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899
 msgid "Documentation"
 msgstr ""
 
@@ -407,19 +407,19 @@ msgstr ""
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:804
 msgid "Groups of Person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:804
+#: aleksis/apps/alsijil/models.py:817
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:816
+#: aleksis/apps/alsijil/models.py:829
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
@@ -429,94 +429,94 @@ msgstr ""
 msgid "Tardiness"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:891
+#: aleksis/apps/alsijil/models.py:904
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:893
+#: aleksis/apps/alsijil/models.py:906
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:900
+#: aleksis/apps/alsijil/models.py:913
 msgid "Personal Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:901
+#: aleksis/apps/alsijil/models.py:914
 msgid "Personal Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:912
+#: aleksis/apps/alsijil/models.py:925
 msgid "A person got assigned the same extra mark multiple times per documentation."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:925
+#: aleksis/apps/alsijil/models.py:938
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:926
+#: aleksis/apps/alsijil/models.py:939
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:947
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:953
+#: aleksis/apps/alsijil/models.py:966
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:965
+#: aleksis/apps/alsijil/models.py:978
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:980
+#: aleksis/apps/alsijil/models.py:993
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:981
+#: aleksis/apps/alsijil/models.py:994
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:988
+#: aleksis/apps/alsijil/models.py:1001
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:989
+#: aleksis/apps/alsijil/models.py:1002
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:990
+#: aleksis/apps/alsijil/models.py:1003
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:991
+#: aleksis/apps/alsijil/models.py:1004
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:992
+#: aleksis/apps/alsijil/models.py:1005
 msgid "Can list all personal note filters"
 msgstr ""
 
@@ -637,6 +637,14 @@ msgstr ""
 msgid "If you leave it empty, no group type will be used."
 msgstr ""
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+msgid "List of ParticipationStatus IDs"
+msgstr ""
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
diff --git a/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po
index b5a37983138f9498ff9cea509867a17a8c920ab0..d0eac6f75175022d5ecc52bfb883af62065137d7 100644
--- a/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po
@@ -7,15 +7,18 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: 2023-05-26 04:38+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
-"Language-Team: Russian <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/ru/>\n"
+"Language-Team: Russian <https://translate.edugit.org/projects/aleksis/"
+"aleksis-app-alsijil/ru/>\n"
 "Language: ru\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
+"%100>=11 && n%100<=14)? 2 : 3);\n"
 "X-Generator: Weblate 4.12.1\n"
 
 #: aleksis/apps/alsijil/actions.py:18
@@ -43,8 +46,11 @@ msgstr "{} просит Вас проверить некоторые запис
 
 #: aleksis/apps/alsijil/actions.py:86
 #, python-brace-format
-msgid "We have successfully sent notifications to {count_teachers} persons for {count_items} lessons."
-msgstr "Мы отправили уведомление для {count_teachers} чел. о {count_items} уроках."
+msgid ""
+"We have successfully sent notifications to {count_teachers} persons for "
+"{count_items} lessons."
+msgstr ""
+"Мы отправили уведомление для {count_teachers} чел. о {count_items} уроках."
 
 #: aleksis/apps/alsijil/actions.py:92
 msgid "Ask teacher to check data"
@@ -152,21 +158,24 @@ msgid "You can't select a group and a teacher both."
 msgstr "Вы не можете одновременно выбрать группу и преподавателя."
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 msgid "Person"
 msgstr "Физлицо"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
+#: aleksis/apps/alsijil/models.py:960
 msgid "Start date"
 msgstr "Дата начала"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
+#: aleksis/apps/alsijil/models.py:964
 msgid "End date"
 msgstr "Дата окончания"
 
@@ -277,10 +286,11 @@ msgid "Short name"
 msgstr "Короткое имя"
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
+#: aleksis/apps/alsijil/models.py:924
 msgid "Name"
 msgstr "Полное имя"
 
@@ -289,8 +299,12 @@ msgid "Count as absent"
 msgstr "Количество отсутствующих"
 
 #: aleksis/apps/alsijil/models.py:71
-msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
-msgstr "Если отмечено, этот тип объяснительной будет засчитан как пропущенный урок. Если не отмечено, то без записи в отчет о пропусках."
+msgid ""
+"If checked, this excuse type will be counted as a missed lesson. If not "
+"checked,it won't show up in the absence report."
+msgstr ""
+"Если отмечено, этот тип объяснительной будет засчитан как пропущенный урок. "
+"Если не отмечено, то без записи в отчет о пропусках."
 
 #: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
@@ -389,13 +403,11 @@ msgstr "Преподаватели"
 
 #: aleksis/apps/alsijil/models.py:490
 #, fuzzy
-#| msgid "Lesson topic"
 msgid "Lesson Topic"
 msgstr "Тема урока"
 
 #: aleksis/apps/alsijil/models.py:492
 #, fuzzy
-#| msgid "Group note"
 msgid "Group Note"
 msgstr "Групповая заметка"
 
@@ -403,143 +415,141 @@ msgstr "Групповая заметка"
 msgid "Participation touched at"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899 aleksis/apps/alsijil/models.py:798
 #: aleksis/apps/alsijil/models.py:886
 #, fuzzy
-#| msgid "Lesson documentation"
 msgid "Documentation"
 msgstr "Учебный материал"
 
 #: aleksis/apps/alsijil/models.py:526
 #, fuzzy
-#| msgid "Lesson documentations"
 msgid "Documentations"
 msgstr "Учебные материалы"
 
-#: aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:804 aleksis/apps/alsijil/models.py:791
 #, fuzzy
-#| msgid "Group roles"
 msgid "Groups of Person"
 msgstr "Роли групп"
 
-#: aleksis/apps/alsijil/models.py:804
+#: aleksis/apps/alsijil/models.py:817 aleksis/apps/alsijil/models.py:804
 #, fuzzy
-#| msgid "Absences"
 msgid "Absence Reason"
 msgstr "Пропуски"
 
-#: aleksis/apps/alsijil/models.py:816
+#: aleksis/apps/alsijil/models.py:829 aleksis/apps/alsijil/models.py:816
 #, fuzzy
-#| msgid "Absences"
 msgid "Base Absence"
 msgstr "Пропуски"
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+#: aleksis/apps/alsijil/models.py:819
 msgid "Tardiness"
 msgstr "Опоздание"
 
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 #: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:891
+#: aleksis/apps/alsijil/models.py:904 aleksis/apps/alsijil/models.py:891
 #, fuzzy
-#| msgid "Notes"
 msgid "Note"
 msgstr "Заметки"
 
-#: aleksis/apps/alsijil/models.py:893
+#: aleksis/apps/alsijil/models.py:906 aleksis/apps/alsijil/models.py:893
 #, fuzzy
-#| msgid "Extra mark"
 msgid "Extra Mark"
 msgstr "Дополнительная отметка"
 
-#: aleksis/apps/alsijil/models.py:900
+#: aleksis/apps/alsijil/models.py:913 aleksis/apps/alsijil/models.py:900
 #, fuzzy
-#| msgid "Personal note"
 msgid "Personal Note"
 msgstr "Личная заметка"
 
-#: aleksis/apps/alsijil/models.py:901
+#: aleksis/apps/alsijil/models.py:914 aleksis/apps/alsijil/models.py:901
 #, fuzzy
-#| msgid "Personal notes"
 msgid "Personal Notes"
 msgstr "Личные заметки"
 
-#: aleksis/apps/alsijil/models.py:912
-msgid "A person got assigned the same extra mark multiple times per documentation."
+#: aleksis/apps/alsijil/models.py:925 aleksis/apps/alsijil/models.py:912
+msgid ""
+"A person got assigned the same extra mark multiple times per documentation."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:925
+#: aleksis/apps/alsijil/models.py:938 aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr "Иконка"
 
-#: aleksis/apps/alsijil/models.py:926
+#: aleksis/apps/alsijil/models.py:939 aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr "Цвет"
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 msgid "Group role"
 msgstr "Роль группы"
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
+#: aleksis/apps/alsijil/models.py:933
 msgid "Group roles"
 msgstr "Роли групп"
 
-#: aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:947 aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr "Может назначать роль группы"
 
-#: aleksis/apps/alsijil/models.py:953
+#: aleksis/apps/alsijil/models.py:966 aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr "Назначенное физлицо"
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
+#: aleksis/apps/alsijil/models.py:958
 msgid "Groups"
 msgstr "Группы"
 
-#: aleksis/apps/alsijil/models.py:965
+#: aleksis/apps/alsijil/models.py:978 aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr "Если нет точной конечной даты, можно оставить незаполненным"
 
-#: aleksis/apps/alsijil/models.py:980
+#: aleksis/apps/alsijil/models.py:993 aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr "Назначение роли группы"
 
-#: aleksis/apps/alsijil/models.py:981
+#: aleksis/apps/alsijil/models.py:994 aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr "Назначение ролей групп"
 
-#: aleksis/apps/alsijil/models.py:988
+#: aleksis/apps/alsijil/models.py:1001 aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr "Может просматривать обзор урока"
 
-#: aleksis/apps/alsijil/models.py:989
+#: aleksis/apps/alsijil/models.py:1002 aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr "Может просматривать недельный обзор"
 
-#: aleksis/apps/alsijil/models.py:990
+#: aleksis/apps/alsijil/models.py:1003 aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr "Может просматривать весь журнал"
 
-#: aleksis/apps/alsijil/models.py:991
+#: aleksis/apps/alsijil/models.py:1004 aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr "Может регистрировать отсутствие"
 
-#: aleksis/apps/alsijil/models.py:992
+#: aleksis/apps/alsijil/models.py:1005 aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr "Может просматривать все фильтры личных заметок"
 
@@ -557,52 +567,92 @@ msgid "Allow users to view their own personal notes"
 msgstr "Разрешить пользователям просматривать свои личные заметки"
 
 #: aleksis/apps/alsijil/preferences.py:41
-msgid "Allow primary group owners to register future absences for students in their groups"
-msgstr "Разрешить владельцам основных групп регистрировать будущие пропуски студентов в своих группах"
+msgid ""
+"Allow primary group owners to register future absences for students in their "
+"groups"
+msgstr ""
+"Разрешить владельцам основных групп регистрировать будущие пропуски "
+"студентов в своих группах"
 
 #: aleksis/apps/alsijil/preferences.py:51
-msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
-msgstr "Наделить владельца родительской группы такими же правами, как у владельца соответствующих дочерних групп"
+msgid ""
+"Grant the owner of a parent group the same privileges as the owners of the "
+"respective child groups"
+msgstr ""
+"Наделить владельца родительской группы такими же правами, как у владельца "
+"соответствующих дочерних групп"
 
 #: aleksis/apps/alsijil/preferences.py:61
-msgid "Allow original teachers to edit their lessons although they are substituted"
-msgstr "Разрешить изначальным преподавателям редактировать свои уроки даже после их замены"
+msgid ""
+"Allow original teachers to edit their lessons although they are substituted"
+msgstr ""
+"Разрешить изначальным преподавателям редактировать свои уроки даже после их "
+"замены"
 
 #: aleksis/apps/alsijil/preferences.py:70
-msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
-msgstr "Переносить данные с первого урока в расписании на текущие уроки через несколько уроков"
+msgid ""
+"Carry over data from first lesson period to the following lesson periods in "
+"lessons over multiple periods"
+msgstr ""
+"Переносить данные с первого урока в расписании на текущие уроки через "
+"несколько уроков"
 
 #: aleksis/apps/alsijil/preferences.py:73
-msgid "This will carry over data only if the data in the following periods are empty."
-msgstr "Это перенесёт данные только в случае отсутствия данных в последующих уроках."
+msgid ""
+"This will carry over data only if the data in the following periods are "
+"empty."
+msgstr ""
+"Это перенесёт данные только в случае отсутствия данных в последующих уроках."
 
 #: aleksis/apps/alsijil/preferences.py:82
-msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
-msgstr "Разрешить перенос данных с любого урока на все такие же уроки                 с таким же номером в расписании на той же неделе"
+msgid ""
+"Allow carrying over data from any lesson period to all other "
+"lesson                 periods with the same lesson and in the same week"
+msgstr ""
+"Разрешить перенос данных с любого урока на все такие же "
+"уроки                 с таким же номером в расписании на той же неделе"
 
 #: aleksis/apps/alsijil/preferences.py:86
-msgid "This will carry over data only if the data in the aforementioned periods are empty."
-msgstr "Это перенесёт данные только если в упомянутых выше уроках данные не заполнены."
+msgid ""
+"This will carry over data only if the data in the aforementioned periods are "
+"empty."
+msgstr ""
+"Это перенесёт данные только если в упомянутых выше уроках данные не "
+"заполнены."
 
 #: aleksis/apps/alsijil/preferences.py:95
-msgid "Carry over personal notes to all following lesson periods on the same day."
+msgid ""
+"Carry over personal notes to all following lesson periods on the same day."
 msgstr "Переносить личные заметки на все последующие уроки того же дня."
 
 #: aleksis/apps/alsijil/preferences.py:104
-msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
-msgstr "Разрешить преподавателям открывать уроки в тот же день, а не только в начале уроков"
+msgid ""
+"Allow teachers to open lesson periods on the same day and not just at the "
+"beginning of the period"
+msgstr ""
+"Разрешить преподавателям открывать уроки в тот же день, а не только в начале "
+"уроков"
 
 #: aleksis/apps/alsijil/preferences.py:108
-msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
-msgstr "Эти настройки не влияют на прошлые уроки. Вы можете открывать их когда-угодно."
+msgid ""
+"Lessons in the past are not affected by this setting, you can open them "
+"whenever you want."
+msgstr ""
+"Эти настройки не влияют на прошлые уроки. Вы можете открывать их когда-"
+"угодно."
 
 #: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
-msgstr "Разрешить преподавателям добавлять учебный материал (данные для уроков) на выходных"
+msgstr ""
+"Разрешить преподавателям добавлять учебный материал (данные для уроков) на "
+"выходных"
 
 #: aleksis/apps/alsijil/preferences.py:126
-msgid "Allow group owners to assign group roles to the parents of the group's members"
-msgstr "Разрешить владельцам групп назначать роли групп родителям участников групп"
+msgid ""
+"Allow group owners to assign group roles to the parents of the group's "
+"members"
+msgstr ""
+"Разрешить владельцам групп назначать роли групп родителям участников групп"
 
 #: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
@@ -633,11 +683,15 @@ msgid "Allow editing of all future documentations"
 msgstr ""
 
 #: aleksis/apps/alsijil/preferences.py:180
-msgid "Allow editing of all documentations up to and including those on the current day"
+msgid ""
+"Allow editing of all documentations up to and including those on the current "
+"day"
 msgstr ""
 
 #: aleksis/apps/alsijil/preferences.py:185
-msgid "Allow editing of all documentations up to and including those on the current date and time"
+msgid ""
+"Allow editing of all documentations up to and including those on the current "
+"date and time"
 msgstr ""
 
 #: aleksis/apps/alsijil/preferences.py:190
@@ -645,21 +699,35 @@ msgid "Set time range for which documentations may be edited"
 msgstr ""
 
 #: aleksis/apps/alsijil/preferences.py:201
-msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgid ""
+"User is allowed to register absences for members of groups the user is an "
+"owner of with these group types"
 msgstr ""
 
 #: aleksis/apps/alsijil/preferences.py:205
-msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgid ""
+"If you leave it empty, all member of groups the user is an owner of will be "
+"shown."
 msgstr ""
 
 #: aleksis/apps/alsijil/preferences.py:217
-msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgid ""
+"Group type of groups to be shown first in the group select field on the "
+"coursebook overview page"
 msgstr ""
 
 #: aleksis/apps/alsijil/preferences.py:220
 msgid "If you leave it empty, no group type will be used."
 msgstr ""
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+msgid "List of ParticipationStatus IDs"
+msgstr ""
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
@@ -682,7 +750,6 @@ msgstr "Объяснительная"
 
 #: aleksis/apps/alsijil/tables.py:136
 #, fuzzy, python-brace-format
-#| msgid "{value}' late"
 msgid "{value}' tardiness"
 msgstr "{value}' задержка"
 
@@ -692,7 +759,6 @@ msgstr ""
 
 #: aleksis/apps/alsijil/tasks.py:49
 #, fuzzy
-#| msgid "Start date"
 msgid "Sort data ..."
 msgstr "Дата начала"
 
@@ -706,7 +772,6 @@ msgstr ""
 
 #: aleksis/apps/alsijil/tasks.py:133
 #, fuzzy
-#| msgid "Statistics"
 msgid "Load statistics ..."
 msgstr "Статистика"
 
@@ -765,11 +830,13 @@ msgstr " %(count)s зависимых уроков "
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:40
 msgid ""
 "\n"
-"                  There are no affected lessons. Registering this absence won't have any effect.\n"
+"                  There are no affected lessons. Registering this absence "
+"won't have any effect.\n"
 "                "
 msgstr ""
 "\n"
-"                  Зависимых уроков нет. Регистрация пропуска ни на что не влияет.\n"
+"                  Зависимых уроков нет. Регистрация пропуска ни на что не "
+"влияет.\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:57
@@ -867,7 +934,8 @@ msgid ""
 "                "
 msgstr ""
 "\n"
-"                  Этот урок перекрывает выходные и его нельзя редактировать.\n"
+"                  Этот урок перекрывает выходные и его нельзя "
+"редактировать.\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:10
@@ -1006,11 +1074,13 @@ msgstr "Нет доступных уроков"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:434
 msgid ""
 "\n"
-"            There are no lessons for the selected group or teacher in this week.\n"
+"            There are no lessons for the selected group or teacher in this "
+"week.\n"
 "          "
 msgstr ""
 "\n"
-"            Для выбранной группы или преподавателя на этой неделе уроков нет.\n"
+"            Для выбранной группы или преподавателя на этой неделе уроков "
+"нет.\n"
 "          "
 
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/create.html:6
@@ -1028,13 +1098,17 @@ msgstr "Редактировать тип объяснительной"
 #: aleksis/apps/alsijil/templates/alsijil/group_role/warning.html:4
 msgid ""
 "\n"
-"    This function should only be used to define alternatives to the default excuse which also will be counted extra.\n"
-"    Don't use this to create a default excuse or if you don't divide between different types of excuse.\n"
+"    This function should only be used to define alternatives to the default "
+"excuse which also will be counted extra.\n"
+"    Don't use this to create a default excuse or if you don't divide between "
+"different types of excuse.\n"
 "  "
 msgstr ""
 "\n"
-"    Эта функция используется только для определения альтернатив к объяснительным по-умолчанию, которые тоже будут учтены.\n"
-"    Не используйте этот функционал для создания объяснительной по-умолчанию или если не ведёте разделение по типам.\n"
+"    Эта функция используется только для определения альтернатив к "
+"объяснительным по-умолчанию, которые тоже будут учтены.\n"
+"    Не используйте этот функционал для создания объяснительной по-умолчанию "
+"или если не ведёте разделение по типам.\n"
 "  "
 
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
@@ -1102,12 +1176,14 @@ msgstr "Никто не назначен."
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assigned_roles.html:41
 msgid ""
 "\n"
-"    You can get some additional actions for each group role assignment if you click on the name of the\n"
+"    You can get some additional actions for each group role assignment if "
+"you click on the name of the\n"
 "    corresponding person.\n"
 "  "
 msgstr ""
 "\n"
-"    Вы можете получить некоторые дополнительные действия для каждого назначения роли группы после клика\n"
+"    Вы можете получить некоторые дополнительные действия для каждого "
+"назначения роли группы после клика\n"
 "     на имя соответствующего лица.\n"
 "  "
 
@@ -1116,8 +1192,12 @@ msgid "Stop"
 msgstr "Стоп"
 
 #: aleksis/apps/alsijil/templates/alsijil/notifications/check.html:1
-msgid "Please check if the following class register entries are complete and correct:"
-msgstr "Проверьте, пожалуйста, эти записи в классном журнале на полноту и корректность:"
+msgid ""
+"Please check if the following class register entries are complete and "
+"correct:"
+msgstr ""
+"Проверьте, пожалуйста, эти записи в классном журнале на полноту и "
+"корректность:"
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/absences.html:6
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:22
@@ -1277,8 +1357,10 @@ msgstr ""
 #, python-format
 msgid ""
 "\n"
-"            This seating plan is taken from the parent group of %(child_group)s.\n"
-"            If you want, you can take it over for your group and then customize it.\n"
+"            This seating plan is taken from the parent group of "
+"%(child_group)s.\n"
+"            If you want, you can take it over for your group and then "
+"customize it.\n"
 "          "
 msgstr ""
 "\n"
@@ -1302,11 +1384,13 @@ msgstr "Для этого урока нет плана рассадки."
 #, python-format
 msgid ""
 "\n"
-"                  Create a new seating plan for %(group)s (%(subject)s) in %(room)s\n"
+"                  Create a new seating plan for %(group)s (%(subject)s) in "
+"%(room)s\n"
 "                "
 msgstr ""
 "\n"
-"                  Создать новый план рассадки %(group)s (%(subject)s) в %(room)s\n"
+"                  Создать новый план рассадки %(group)s (%(subject)s) в "
+"%(room)s\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:78
@@ -1451,7 +1535,7 @@ msgstr "Сокращения"
 
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:78
 msgid "Late"
-msgstr "Опоздания"
+msgstr "Опоздал(а)"
 
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:89
 msgid "Custom excuse types"
@@ -1570,11 +1654,15 @@ msgid "Notes"
 msgstr "Заметки"
 
 #: aleksis/apps/alsijil/views.py:112
-msgid "You either selected an invalid lesson or there is currently no lesson in progress."
+msgid ""
+"You either selected an invalid lesson or there is currently no lesson in "
+"progress."
 msgstr "Вы или выбрали неправильный урок, или сейчас урока нет."
 
 #: aleksis/apps/alsijil/views.py:145
-msgid "You are not allowed to create a lesson documentation for a lesson in the future."
+msgid ""
+"You are not allowed to create a lesson documentation for a lesson in the "
+"future."
 msgstr "Вам нельзя создавать учебные материалы для уроков в будущем."
 
 #: aleksis/apps/alsijil/views.py:262
@@ -1587,19 +1675,16 @@ msgstr "Личные заметки сохранены."
 
 #: aleksis/apps/alsijil/views.py:656
 #, fuzzy
-#| msgid "Generate printout"
 msgid "Generate full register printout for {}"
 msgstr "Подготовить к печати"
 
 #: aleksis/apps/alsijil/views.py:657
 #, fuzzy
-#| msgid "Generate printout"
 msgid "Generate full register printout …"
 msgstr "Подготовить к печати"
 
 #: aleksis/apps/alsijil/views.py:658
 #, fuzzy
-#| msgid "The personal note has been deleted."
 msgid "The printout has been generated successfully."
 msgstr "Личная заметка удалена."
 
diff --git a/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po
index 40892069d224a5e6ef62ed3a0ce7fe80720ca876..75da0e9e55e72bc44d6c90c74e7ac69b89867b54 100644
--- a/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -151,20 +151,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -276,7 +276,7 @@ msgid "Short name"
 msgstr ""
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
@@ -398,8 +398,8 @@ msgstr ""
 msgid "Participation touched at"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
-#: aleksis/apps/alsijil/models.py:886
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899
 msgid "Documentation"
 msgstr ""
 
@@ -407,19 +407,19 @@ msgstr ""
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:804
 msgid "Groups of Person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:804
+#: aleksis/apps/alsijil/models.py:817
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:816
+#: aleksis/apps/alsijil/models.py:829
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
@@ -429,94 +429,94 @@ msgstr ""
 msgid "Tardiness"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:891
+#: aleksis/apps/alsijil/models.py:904
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:893
+#: aleksis/apps/alsijil/models.py:906
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:900
+#: aleksis/apps/alsijil/models.py:913
 msgid "Personal Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:901
+#: aleksis/apps/alsijil/models.py:914
 msgid "Personal Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:912
+#: aleksis/apps/alsijil/models.py:925
 msgid "A person got assigned the same extra mark multiple times per documentation."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:925
+#: aleksis/apps/alsijil/models.py:938
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:926
+#: aleksis/apps/alsijil/models.py:939
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:947
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:953
+#: aleksis/apps/alsijil/models.py:966
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:965
+#: aleksis/apps/alsijil/models.py:978
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:980
+#: aleksis/apps/alsijil/models.py:993
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:981
+#: aleksis/apps/alsijil/models.py:994
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:988
+#: aleksis/apps/alsijil/models.py:1001
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:989
+#: aleksis/apps/alsijil/models.py:1002
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:990
+#: aleksis/apps/alsijil/models.py:1003
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:991
+#: aleksis/apps/alsijil/models.py:1004
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:992
+#: aleksis/apps/alsijil/models.py:1005
 msgid "Can list all personal note filters"
 msgstr ""
 
@@ -637,6 +637,14 @@ msgstr ""
 msgid "If you leave it empty, no group type will be used."
 msgstr ""
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+msgid "List of ParticipationStatus IDs"
+msgstr ""
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
diff --git a/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po
index 6eeacbadcf3cccf7d38c4e8e09458e8f66ba0338..c9afd310db11fc318cd95d59d2597f7d0e8f1772 100644
--- a/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po
@@ -7,15 +7,19 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"POT-Creation-Date: 2024-09-30 12:25+0200\n"
 "PO-Revision-Date: 2024-08-27 15:25+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
-"Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/uk/>\n"
+"Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/"
+"aleksis-app-alsijil/uk/>\n"
 "Language: uk\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
+"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != "
+"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % "
+"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || "
+"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
 "X-Generator: Weblate 5.0.2\n"
 
 #: aleksis/apps/alsijil/actions.py:18
@@ -43,8 +47,11 @@ msgstr "{} просить Вас перевірити деякі записи к
 
 #: aleksis/apps/alsijil/actions.py:86
 #, python-brace-format
-msgid "We have successfully sent notifications to {count_teachers} persons for {count_items} lessons."
-msgstr "Ми надіслали сповіщення для {count_teachers} осіб щодо {count_items} уроків."
+msgid ""
+"We have successfully sent notifications to {count_teachers} persons for "
+"{count_items} lessons."
+msgstr ""
+"Ми надіслали сповіщення для {count_teachers} осіб щодо {count_items} уроків."
 
 #: aleksis/apps/alsijil/actions.py:92
 msgid "Ask teacher to check data"
@@ -72,7 +79,8 @@ msgstr "У скасованого уроку є пов'язані особист
 
 #: aleksis/apps/alsijil/data_checks.py:77
 msgid "Ensure that 'groups_of_person' is set for every personal note"
-msgstr "Переконайтеся, що для кожної особистої нотатки установлені \"групи_осіб\""
+msgstr ""
+"Переконайтеся, що для кожної особистої нотатки установлені \"групи_осіб\""
 
 #: aleksis/apps/alsijil/data_checks.py:78
 msgid "The personal note has no group in 'groups_of_person'."
@@ -96,7 +104,8 @@ msgstr "Ці особисті нотатки є на вихідних."
 
 #: aleksis/apps/alsijil/data_checks.py:166
 msgid "Ensure that there are no excused personal notes without an absence"
-msgstr "Переконайтеся, що немає особистих нотаток щодо пояснення без відсутності"
+msgstr ""
+"Переконайтеся, що немає особистих нотаток щодо пояснення без відсутності"
 
 #: aleksis/apps/alsijil/data_checks.py:167
 msgid "The personal note is marked as excused, but not as absent."
@@ -133,7 +142,8 @@ msgstr "Домашня робота на наступний урок"
 
 #: aleksis/apps/alsijil/forms.py:53
 msgid "Carry over data to all other lessons with the same subject in this week"
-msgstr "Перенести дані на усі інші уроки з таким самим предметом на цьому тижні"
+msgstr ""
+"Перенести дані на усі інші уроки з таким самим предметом на цьому тижні"
 
 #: aleksis/apps/alsijil/forms.py:98 aleksis/apps/alsijil/forms.py:278
 #: aleksis/apps/alsijil/forms.py:370
@@ -152,21 +162,24 @@ msgid "You can't select a group and a teacher both."
 msgstr "Ви не можете обрати одночасно групу та викладача."
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
+#: aleksis/apps/alsijil/models.py:801 aleksis/apps/alsijil/models.py:892
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 msgid "Person"
 msgstr "Особа"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:960
+#: aleksis/apps/alsijil/models.py:973
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
+#: aleksis/apps/alsijil/models.py:960
 msgid "Start date"
 msgstr "Дата початку"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:964
+#: aleksis/apps/alsijil/models.py:977
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
+#: aleksis/apps/alsijil/models.py:964
 msgid "End date"
 msgstr "Дата закінчення"
 
@@ -277,10 +290,11 @@ msgid "Short name"
 msgstr "Коротке ім'я"
 
 #: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
-#: aleksis/apps/alsijil/models.py:924
+#: aleksis/apps/alsijil/models.py:937
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
+#: aleksis/apps/alsijil/models.py:924
 msgid "Name"
 msgstr "Повне ім'я"
 
@@ -289,8 +303,12 @@ msgid "Count as absent"
 msgstr "Кількість відсутніх"
 
 #: aleksis/apps/alsijil/models.py:71
-msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
-msgstr "Якщо відмічено, цей тип пояснення буде зарахований як пропущений урок. Якщо не відмічено, то без запису у звіт відсутності."
+msgid ""
+"If checked, this excuse type will be counted as a missed lesson. If not "
+"checked,it won't show up in the absence report."
+msgstr ""
+"Якщо відмічено, цей тип пояснення буде зарахований як пропущений урок. Якщо "
+"не відмічено, то без запису у звіт відсутності."
 
 #: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
@@ -310,7 +328,7 @@ msgstr "Рік"
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:331
 msgid "Extra marks"
-msgstr "Додаткові відмітки"
+msgstr "Додаткові позначки"
 
 #: aleksis/apps/alsijil/models.py:300
 msgid "Personal note"
@@ -339,7 +357,7 @@ msgstr "Тема уроку"
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:33
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:422
 msgid "Homework"
-msgstr "Домашня робота"
+msgstr "Домашнє завдання"
 
 #: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
@@ -399,7 +417,8 @@ msgstr "Групова нотатка"
 msgid "Participation touched at"
 msgstr "Статус участі створений о"
 
-#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:811
+#: aleksis/apps/alsijil/models.py:899 aleksis/apps/alsijil/models.py:798
 #: aleksis/apps/alsijil/models.py:886
 msgid "Documentation"
 msgstr "Документація"
@@ -408,116 +427,123 @@ msgstr "Документація"
 msgid "Documentations"
 msgstr "Документація"
 
-#: aleksis/apps/alsijil/models.py:791
+#: aleksis/apps/alsijil/models.py:804 aleksis/apps/alsijil/models.py:791
 msgid "Groups of Person"
 msgstr "Групи осіб"
 
-#: aleksis/apps/alsijil/models.py:804
+#: aleksis/apps/alsijil/models.py:817 aleksis/apps/alsijil/models.py:804
 msgid "Absence Reason"
 msgstr "Причина відсутності"
 
-#: aleksis/apps/alsijil/models.py:816
+#: aleksis/apps/alsijil/models.py:829 aleksis/apps/alsijil/models.py:816
 msgid "Base Absence"
 msgstr "Основна відсутність"
 
-#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/models.py:832 aleksis/apps/alsijil/tables.py:98
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+#: aleksis/apps/alsijil/models.py:819
 msgid "Tardiness"
 msgstr "Запізнення"
 
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:876
 #: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr "Стан участі"
 
-#: aleksis/apps/alsijil/models.py:891
+#: aleksis/apps/alsijil/models.py:904 aleksis/apps/alsijil/models.py:891
 msgid "Note"
 msgstr "Нотатка"
 
-#: aleksis/apps/alsijil/models.py:893
+#: aleksis/apps/alsijil/models.py:906 aleksis/apps/alsijil/models.py:893
 msgid "Extra Mark"
 msgstr "Додаткова позначка"
 
-#: aleksis/apps/alsijil/models.py:900
+#: aleksis/apps/alsijil/models.py:913 aleksis/apps/alsijil/models.py:900
 msgid "Personal Note"
 msgstr "Особиста нотатка"
 
-#: aleksis/apps/alsijil/models.py:901
+#: aleksis/apps/alsijil/models.py:914 aleksis/apps/alsijil/models.py:901
 msgid "Personal Notes"
 msgstr "Особисті нотатки"
 
-#: aleksis/apps/alsijil/models.py:912
-msgid "A person got assigned the same extra mark multiple times per documentation."
-msgstr "Особа отримувала в документації однакову додаткову позначку неодноразово."
+#: aleksis/apps/alsijil/models.py:925 aleksis/apps/alsijil/models.py:912
+msgid ""
+"A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+"Особа отримувала в документації однакову додаткову позначку неодноразово."
 
-#: aleksis/apps/alsijil/models.py:925
+#: aleksis/apps/alsijil/models.py:938 aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr "Піктограма"
 
-#: aleksis/apps/alsijil/models.py:926
+#: aleksis/apps/alsijil/models.py:939 aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr "Колір"
 
-#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
+#: aleksis/apps/alsijil/models.py:945 aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 msgid "Group role"
 msgstr "Роль групи"
 
-#: aleksis/apps/alsijil/models.py:933
+#: aleksis/apps/alsijil/models.py:946
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
+#: aleksis/apps/alsijil/models.py:933
 msgid "Group roles"
 msgstr "Ролі груп"
 
-#: aleksis/apps/alsijil/models.py:934
+#: aleksis/apps/alsijil/models.py:947 aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr "Може призначати роль групи"
 
-#: aleksis/apps/alsijil/models.py:953
+#: aleksis/apps/alsijil/models.py:966 aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr "Призначена особа"
 
-#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
+#: aleksis/apps/alsijil/models.py:971 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
+#: aleksis/apps/alsijil/models.py:958
 msgid "Groups"
 msgstr "Групи"
 
-#: aleksis/apps/alsijil/models.py:965
+#: aleksis/apps/alsijil/models.py:978 aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr "Якщо немає точної кінцевої дати, можна залишити порожнім"
 
-#: aleksis/apps/alsijil/models.py:980
+#: aleksis/apps/alsijil/models.py:993 aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr "Призначення ролі групи"
 
-#: aleksis/apps/alsijil/models.py:981
+#: aleksis/apps/alsijil/models.py:994 aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr "Призначення ролі групи"
 
-#: aleksis/apps/alsijil/models.py:988
+#: aleksis/apps/alsijil/models.py:1001 aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr "Може бачити огляд уроку"
 
-#: aleksis/apps/alsijil/models.py:989
+#: aleksis/apps/alsijil/models.py:1002 aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr "Може бачити огляд тижня"
 
-#: aleksis/apps/alsijil/models.py:990
+#: aleksis/apps/alsijil/models.py:1003 aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr "Може бачити весь журнал"
 
-#: aleksis/apps/alsijil/models.py:991
+#: aleksis/apps/alsijil/models.py:1004 aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr "Може реєструвати пропуск"
 
-#: aleksis/apps/alsijil/models.py:992
+#: aleksis/apps/alsijil/models.py:1005 aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr "Може бачити усі фільтри особистих нотаток"
 
@@ -535,51 +561,87 @@ msgid "Allow users to view their own personal notes"
 msgstr "Дозволити користувачам переглядати власні особисті нотатки"
 
 #: aleksis/apps/alsijil/preferences.py:41
-msgid "Allow primary group owners to register future absences for students in their groups"
-msgstr "Дозволити власникам основних груп реєструвати майбутні пропуски студентів у своїх групах"
+msgid ""
+"Allow primary group owners to register future absences for students in their "
+"groups"
+msgstr ""
+"Дозволити власникам основних груп реєструвати майбутні пропуски студентів у "
+"своїх групах"
 
 #: aleksis/apps/alsijil/preferences.py:51
-msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
-msgstr "Надати власнику батьківської групи такі самі повноваження, як і власникам відповідних підлеглих груп"
+msgid ""
+"Grant the owner of a parent group the same privileges as the owners of the "
+"respective child groups"
+msgstr ""
+"Надати власнику батьківської групи такі самі повноваження, як і власникам "
+"відповідних підлеглих груп"
 
 #: aleksis/apps/alsijil/preferences.py:61
-msgid "Allow original teachers to edit their lessons although they are substituted"
-msgstr "Дозволити початковим викладачам редагувати свої уроки навіть після їх заміни"
+msgid ""
+"Allow original teachers to edit their lessons although they are substituted"
+msgstr ""
+"Дозволити початковим викладачам редагувати свої уроки навіть після їх заміни"
 
 #: aleksis/apps/alsijil/preferences.py:70
-msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
-msgstr "Переносити дані з першого уроку в розкладі на поточні уроки через декілька уроків"
+msgid ""
+"Carry over data from first lesson period to the following lesson periods in "
+"lessons over multiple periods"
+msgstr ""
+"Переносити дані з першого уроку в розкладі на поточні уроки через декілька "
+"уроків"
 
 #: aleksis/apps/alsijil/preferences.py:73
-msgid "This will carry over data only if the data in the following periods are empty."
+msgid ""
+"This will carry over data only if the data in the following periods are "
+"empty."
 msgstr "Це перенесе дані лише в тому разі, коли в поточних уроках даних немає."
 
 #: aleksis/apps/alsijil/preferences.py:82
-msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
-msgstr "Дозволити переносити дані з будь-якого уроку на усі інші                 уроки з таким самим номером урока на тому самому тижні"
+msgid ""
+"Allow carrying over data from any lesson period to all other "
+"lesson                 periods with the same lesson and in the same week"
+msgstr ""
+"Дозволити переносити дані з будь-якого уроку на усі інші                 "
+"уроки з таким самим номером урока на тому самому тижні"
 
 #: aleksis/apps/alsijil/preferences.py:86
-msgid "This will carry over data only if the data in the aforementioned periods are empty."
-msgstr "Це перенесе дані лише в тому разі, коли у вищезгаданих уроках даних немає."
+msgid ""
+"This will carry over data only if the data in the aforementioned periods are "
+"empty."
+msgstr ""
+"Це перенесе дані лише в тому разі, коли у вищезгаданих уроках даних немає."
 
 #: aleksis/apps/alsijil/preferences.py:95
-msgid "Carry over personal notes to all following lesson periods on the same day."
+msgid ""
+"Carry over personal notes to all following lesson periods on the same day."
 msgstr "Переносити особисті нотатки до всіх наступних уроків того ж дня."
 
 #: aleksis/apps/alsijil/preferences.py:104
-msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
-msgstr "Дозволити викладачам відкривати уроки того самого дня і не лише на початку уроків"
+msgid ""
+"Allow teachers to open lesson periods on the same day and not just at the "
+"beginning of the period"
+msgstr ""
+"Дозволити викладачам відкривати уроки того самого дня і не лише на початку "
+"уроків"
 
 #: aleksis/apps/alsijil/preferences.py:108
-msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
-msgstr "Ці налаштування не впливають на минулі уроки. Ви можете відкривати їх будь-коли."
+msgid ""
+"Lessons in the past are not affected by this setting, you can open them "
+"whenever you want."
+msgstr ""
+"Ці налаштування не впливають на минулі уроки. Ви можете відкривати їх будь-"
+"коли."
 
 #: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
-msgstr "Дозволити викладачам додавати навчальний матеріал (дані для уроків) на вихідних"
+msgstr ""
+"Дозволити викладачам додавати навчальний матеріал (дані для уроків) на "
+"вихідних"
 
 #: aleksis/apps/alsijil/preferences.py:126
-msgid "Allow group owners to assign group roles to the parents of the group's members"
+msgid ""
+"Allow group owners to assign group roles to the parents of the group's "
+"members"
 msgstr "Дозволити власникам груп призначати ролі груп батькам учасників груп"
 
 #: aleksis/apps/alsijil/preferences.py:135
@@ -611,33 +673,60 @@ msgid "Allow editing of all future documentations"
 msgstr "Дозволити редагувати усю майбутню документацію"
 
 #: aleksis/apps/alsijil/preferences.py:180
-msgid "Allow editing of all documentations up to and including those on the current day"
-msgstr "Дозволити редагувати усю попередню документацію, сьогоднішній день включно"
+msgid ""
+"Allow editing of all documentations up to and including those on the current "
+"day"
+msgstr ""
+"Дозволити редагувати усю попередню документацію, сьогоднішній день включно"
 
 #: aleksis/apps/alsijil/preferences.py:185
-msgid "Allow editing of all documentations up to and including those on the current date and time"
-msgstr "Дозволити редагувати усю попередню документацію, по теперішній час включно"
+msgid ""
+"Allow editing of all documentations up to and including those on the current "
+"date and time"
+msgstr ""
+"Дозволити редагувати усю попередню документацію, по теперішній час включно"
 
 #: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
 msgstr "Встановити інтервал часу, за який можна змінювати документацію"
 
 #: aleksis/apps/alsijil/preferences.py:201
-msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
-msgstr "Користувачу дозволено реєструвати відсутність для учасників в групах вказаних типів, де він/вона є власником"
+msgid ""
+"User is allowed to register absences for members of groups the user is an "
+"owner of with these group types"
+msgstr ""
+"Користувачу дозволено реєструвати відсутність для учасників в групах "
+"вказаних типів, де він/вона є власником"
 
 #: aleksis/apps/alsijil/preferences.py:205
-msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
-msgstr "Якщо залишити незаповненим, будуть показані усі учасники усіх груп, де користувач є власником."
+msgid ""
+"If you leave it empty, all member of groups the user is an owner of will be "
+"shown."
+msgstr ""
+"Якщо залишити незаповненим, будуть показані усі учасники усіх груп, де "
+"користувач є власником."
 
 #: aleksis/apps/alsijil/preferences.py:217
-msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
-msgstr "Тип груп, який слід показувати першим у полі вибору груп на сторінці огляду курсової книги"
+msgid ""
+"Group type of groups to be shown first in the group select field on the "
+"coursebook overview page"
+msgstr ""
+"Тип груп, який слід показувати першим у полі вибору груп на сторінці огляду "
+"курсової книги"
 
 #: aleksis/apps/alsijil/preferences.py:220
 msgid "If you leave it empty, no group type will be used."
 msgstr "Якщо залишити порожнім, жодний тип групи не буде використаний."
 
+#: aleksis/apps/alsijil/schema/participation_status.py:84
+#, fuzzy
+msgid "List of ParticipationStatus IDs"
+msgstr "Стан участі"
+
+#: aleksis/apps/alsijil/schema/participation_status.py:146
+msgid "Extended absence reason from coursebook."
+msgstr ""
+
 #: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
@@ -738,11 +827,13 @@ msgstr " %(count)s залежних уроків "
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:40
 msgid ""
 "\n"
-"                  There are no affected lessons. Registering this absence won't have any effect.\n"
+"                  There are no affected lessons. Registering this absence "
+"won't have any effect.\n"
 "                "
 msgstr ""
 "\n"
-"                  Залежних уроків немає. Реєстрація пропуску ні на що не впливає.\n"
+"                  Залежних уроків немає. Реєстрація пропуску ні на що не "
+"впливає.\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:57
@@ -979,7 +1070,8 @@ msgstr "Немає доступних уроків"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:434
 msgid ""
 "\n"
-"            There are no lessons for the selected group or teacher in this week.\n"
+"            There are no lessons for the selected group or teacher in this "
+"week.\n"
 "          "
 msgstr ""
 "\n"
@@ -1001,13 +1093,17 @@ msgstr "Редагувати тип пояснення"
 #: aleksis/apps/alsijil/templates/alsijil/group_role/warning.html:4
 msgid ""
 "\n"
-"    This function should only be used to define alternatives to the default excuse which also will be counted extra.\n"
-"    Don't use this to create a default excuse or if you don't divide between different types of excuse.\n"
+"    This function should only be used to define alternatives to the default "
+"excuse which also will be counted extra.\n"
+"    Don't use this to create a default excuse or if you don't divide between "
+"different types of excuse.\n"
 "  "
 msgstr ""
 "\n"
-"    Ця функція використовується лише для визначення альтернатив до типового пояснення, яке додатково буде враховане.\n"
-"    Не користуйтеся цим для створення типового пояснення або якщо не розділяєте на типи пояснень.\n"
+"    Ця функція використовується лише для визначення альтернатив до типового "
+"пояснення, яке додатково буде враховане.\n"
+"    Не користуйтеся цим для створення типового пояснення або якщо не "
+"розділяєте на типи пояснень.\n"
 "  "
 
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
@@ -1075,12 +1171,14 @@ msgstr "Ніхто не призначений."
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assigned_roles.html:41
 msgid ""
 "\n"
-"    You can get some additional actions for each group role assignment if you click on the name of the\n"
+"    You can get some additional actions for each group role assignment if "
+"you click on the name of the\n"
 "    corresponding person.\n"
 "  "
 msgstr ""
 "\n"
-"    Ви можете отримати деякі додаткові дії для кожного призначення ролі групи після кліку\n"
+"    Ви можете отримати деякі додаткові дії для кожного призначення ролі "
+"групи після кліку\n"
 "     на ім'я відповідної особи.\n"
 "  "
 
@@ -1089,8 +1187,11 @@ msgid "Stop"
 msgstr "Стоп"
 
 #: aleksis/apps/alsijil/templates/alsijil/notifications/check.html:1
-msgid "Please check if the following class register entries are complete and correct:"
-msgstr "Перевірте, будь ласка, чи ці записи класного журналу повні та правильні:"
+msgid ""
+"Please check if the following class register entries are complete and "
+"correct:"
+msgstr ""
+"Перевірте, будь ласка, чи ці записи класного журналу повні та правильні:"
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/absences.html:6
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:22
@@ -1250,12 +1351,15 @@ msgstr ""
 #, python-format
 msgid ""
 "\n"
-"            This seating plan is taken from the parent group of %(child_group)s.\n"
-"            If you want, you can take it over for your group and then customize it.\n"
+"            This seating plan is taken from the parent group of "
+"%(child_group)s.\n"
+"            If you want, you can take it over for your group and then "
+"customize it.\n"
 "          "
 msgstr ""
 "\n"
-"            Цей план розсадження взятий із батьківської групи %(child_group)s.\n"
+"            Цей план розсадження взятий із батьківської групи "
+"%(child_group)s.\n"
 "            При необхідності, Ви можете його налаштувати під свою групу.\n"
 "          "
 
@@ -1275,11 +1379,13 @@ msgstr "Для цього уроку немає плану розсадженн
 #, python-format
 msgid ""
 "\n"
-"                  Create a new seating plan for %(group)s (%(subject)s) in %(room)s\n"
+"                  Create a new seating plan for %(group)s (%(subject)s) in "
+"%(room)s\n"
 "                "
 msgstr ""
 "\n"
-"                  Створити новий план розсадження %(group)s (%(subject)s) у %(room)s\n"
+"                  Створити новий план розсадження %(group)s (%(subject)s) у "
+"%(room)s\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:78
@@ -1428,7 +1534,7 @@ msgstr "Запізнення"
 
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:89
 msgid "Custom excuse types"
-msgstr "Користувацькі типи пояснень"
+msgstr "Користувацькі типи пояснювальних"
 
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:101
 msgid "Custom excuse types (not counted as absent)"
@@ -1543,11 +1649,15 @@ msgid "Notes"
 msgstr "Нотатки"
 
 #: aleksis/apps/alsijil/views.py:112
-msgid "You either selected an invalid lesson or there is currently no lesson in progress."
+msgid ""
+"You either selected an invalid lesson or there is currently no lesson in "
+"progress."
 msgstr "Або Ви обрали неправильний урок, або зараз уроку немає."
 
 #: aleksis/apps/alsijil/views.py:145
-msgid "You are not allowed to create a lesson documentation for a lesson in the future."
+msgid ""
+"You are not allowed to create a lesson documentation for a lesson in the "
+"future."
 msgstr "Вам не дозволено створювати учбові матеріали для уроку у майбутньому."
 
 #: aleksis/apps/alsijil/views.py:262
diff --git a/aleksis/apps/alsijil/managers.py b/aleksis/apps/alsijil/managers.py
index dc29fae25c189c7c2250279a6e8d86206f52e520..983a29b8d73196278afc3de7b1e25f484e62f02f 100644
--- a/aleksis/apps/alsijil/managers.py
+++ b/aleksis/apps/alsijil/managers.py
@@ -1,141 +1,21 @@
 from datetime import date, datetime
 from typing import TYPE_CHECKING, Optional, Sequence, Union
 
-from django.db.models import Case, ExpressionWrapper, F, Func, QuerySet, Value, When
-from django.db.models.fields import DateField
-from django.db.models.functions import Concat
+from django.db.models import QuerySet
 from django.db.models.query import Prefetch
 from django.db.models.query_utils import Q
-from django.utils.translation import gettext as _
 
 from calendarweek import CalendarWeek
 
-from aleksis.apps.chronos.managers import DateRangeQuerySetMixin
-from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations, RecurrencePolymorphicManager
+from aleksis.core.managers import (
+    AlekSISBaseManagerWithoutMigrations,
+    RecurrencePolymorphicManager,
+)
 
 if TYPE_CHECKING:
     from aleksis.core.models import Group
 
 
-class RegisterObjectRelatedQuerySet(QuerySet):
-    """Common queryset for personal notes and lesson documentations with shared API."""
-
-    def _get_weekday_to_date(self, weekday_name, year_name="year", week_name="week"):
-        """Get a ORM function which converts a weekday, a week and a year to a date."""
-        return ExpressionWrapper(
-            Func(
-                Concat(F(year_name), F(week_name)),
-                Value("IYYYIW"),
-                output_field=DateField(),
-                function="TO_DATE",
-            )
-            + F(weekday_name),
-            output_field=DateField(),
-        )
-
-    def annotate_day(self) -> QuerySet:
-        """Annotate every personal note/lesson documentation with the real date.
-
-        Attribute name: ``day``
-
-        .. note::
-            For events, this will annotate ``None``.
-        """
-        return self.annotate(
-            day=Case(
-                When(
-                    lesson_period__isnull=False,
-                    then=self._get_weekday_to_date("lesson_period__period__weekday"),
-                ),
-                When(
-                    extra_lesson__isnull=False,
-                    then=self._get_weekday_to_date(
-                        "extra_lesson__period__weekday", "extra_lesson__year", "extra_lesson__week"
-                    ),
-                ),
-            )
-        )
-
-    def annotate_date_range(self) -> QuerySet:
-        """Annotate every personal note/lesson documentation with the real date.
-
-        Attribute names: ``day_start``, ``day_end``
-
-        .. note::
-            For lesson periods and extra lessons,
-            this will annotate the same date for start and end day.
-        """
-        return self.annotate_day().annotate(
-            day_start=Case(
-                When(day__isnull=False, then="day"),
-                When(day__isnull=True, then="event__date_start"),
-            ),
-            day_end=Case(
-                When(day__isnull=False, then="day"),
-                When(day__isnull=True, then="event__date_end"),
-            ),
-        )
-
-    def annotate_subject(self) -> QuerySet:
-        """Annotate lesson documentations with the subjects."""
-        return self.annotate(
-            subject=Case(
-                When(
-                    lesson_period__isnull=False,
-                    then="lesson_period__lesson__subject__name",
-                ),
-                When(
-                    extra_lesson__isnull=False,
-                    then="extra_lesson__subject__name",
-                ),
-                default=Value(_("Event")),
-            )
-        )
-
-
-class PersonalNoteManager(AlekSISBaseManagerWithoutMigrations):
-    """Manager adding specific methods to personal notes."""
-
-    def get_queryset(self):
-        """Ensure all related lesson and person data are loaded as well."""
-        return (
-            super()
-            .get_queryset()
-            .select_related(
-                "person",
-                "excuse_type",
-                "lesson_period",
-                "lesson_period__lesson",
-                "lesson_period__lesson__subject",
-                "lesson_period__period",
-                "lesson_period__lesson__validity",
-                "lesson_period__lesson__validity__school_term",
-                "event",
-                "extra_lesson",
-                "extra_lesson__subject",
-            )
-            .prefetch_related("extra_marks")
-        )
-
-
-class PersonalNoteQuerySet(RegisterObjectRelatedQuerySet, QuerySet):
-    def not_empty(self):
-        """Get all not empty personal notes."""
-        return self.filter(
-            ~Q(remarks="") | Q(absent=True) | ~Q(tardiness=0) | Q(extra_marks__isnull=False)
-        )
-
-
-class LessonDocumentationManager(AlekSISBaseManagerWithoutMigrations):
-    pass
-
-
-class LessonDocumentationQuerySet(RegisterObjectRelatedQuerySet, QuerySet):
-    def not_empty(self):
-        """Get all not empty lesson documentations."""
-        return self.filter(~Q(topic="") | ~Q(group_note="") | ~Q(homework=""))
-
-
 class GroupRoleManager(AlekSISBaseManagerWithoutMigrations):
     pass
 
@@ -164,7 +44,7 @@ class GroupRoleAssignmentManager(AlekSISBaseManagerWithoutMigrations):
     pass
 
 
-class GroupRoleAssignmentQuerySet(DateRangeQuerySetMixin, QuerySet):
+class GroupRoleAssignmentQuerySet(QuerySet):
     def within_dates(self, start: date, end: date):
         """Filter for all role assignments within a date range."""
         return self.filter(
diff --git a/aleksis/apps/alsijil/migrations/0001_initial.py b/aleksis/apps/alsijil/migrations/0001_initial.py
index 344efdac0df75135647a3093ee20b8d228570b22..faa5eab2c749d234ccadaf31eeff97bb448dbc8e 100644
--- a/aleksis/apps/alsijil/migrations/0001_initial.py
+++ b/aleksis/apps/alsijil/migrations/0001_initial.py
@@ -41,7 +41,6 @@ class Migration(migrations.Migration):
                     models.CharField(
                         max_length=30,
                         unique=True,
-                        validators=[aleksis.apps.alsijil.models.isidentifier],
                         verbose_name="Identifier",
                     ),
                 ),
diff --git a/aleksis/apps/alsijil/migrations/0007_personal_note_lesson_documentation_year.py b/aleksis/apps/alsijil/migrations/0007_personal_note_lesson_documentation_year.py
index 4cf8743b0ba5a2e16e0e1dcb491fe49b72dbb9c4..133cff82fb04e5a04e31dfe16f3efa58cc7f2133 100644
--- a/aleksis/apps/alsijil/migrations/0007_personal_note_lesson_documentation_year.py
+++ b/aleksis/apps/alsijil/migrations/0007_personal_note_lesson_documentation_year.py
@@ -1,8 +1,7 @@
 # Generated by Django 3.0.9 on 2020-08-15 09:39
 
 from django.db import migrations, models
-
-import aleksis.apps.chronos.util.date
+from django.utils import timezone
 
 
 def migrate_data(apps, schema_editor):
@@ -39,7 +38,7 @@ class Migration(migrations.Migration):
             model_name="lessondocumentation",
             name="year",
             field=models.IntegerField(
-                default=aleksis.apps.chronos.util.date.get_current_year,
+                default=lambda: timezone.now().year,
                 verbose_name="Year",
             ),
         ),
@@ -47,7 +46,7 @@ class Migration(migrations.Migration):
             model_name="personalnote",
             name="year",
             field=models.IntegerField(
-                default=aleksis.apps.chronos.util.date.get_current_year,
+                default=lambda: timezone.now().year,
                 verbose_name="Year",
             ),
         ),
diff --git a/aleksis/apps/alsijil/migrations/0009_group_roles.py b/aleksis/apps/alsijil/migrations/0009_group_roles.py
index 78f6f3666cb385a10e6d7101738e66b987fc68f2..ce978b3833b2c9b961c237ef24a4873710b5b0fd 100644
--- a/aleksis/apps/alsijil/migrations/0009_group_roles.py
+++ b/aleksis/apps/alsijil/migrations/0009_group_roles.py
@@ -44,6 +44,6 @@ class Migration(migrations.Migration):
                 'verbose_name': 'Group role assignment',
                 'verbose_name_plural': 'Group role assignments',
             },
-            bases=(aleksis.apps.chronos.managers.GroupPropertiesMixin, models.Model),
+            bases=(models.Model,),
         ),
     ]
diff --git a/aleksis/apps/alsijil/migrations/0010_events_extra_lessons.py b/aleksis/apps/alsijil/migrations/0010_events_extra_lessons.py
index 1c3bf9ec00c3d3242fd17cbc2ef60d1b097a4458..39878398d3aae13a0c0f870f4fb8f34d88c1450e 100644
--- a/aleksis/apps/alsijil/migrations/0010_events_extra_lessons.py
+++ b/aleksis/apps/alsijil/migrations/0010_events_extra_lessons.py
@@ -1,6 +1,5 @@
 # Generated by Django 3.1.5 on 2021-01-10 15:48
 
-import aleksis.apps.chronos.util.date
 from django.db import migrations, models
 import django.db.models.deletion
 
diff --git a/aleksis/apps/alsijil/migrations/0024_check_new_models.py b/aleksis/apps/alsijil/migrations/0024_check_new_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..da52a87226e6591939b5888c37667ea79ab7f47f
--- /dev/null
+++ b/aleksis/apps/alsijil/migrations/0024_check_new_models.py
@@ -0,0 +1,27 @@
+from django.db import migrations, models
+
+from django.apps import apps as global_apps
+
+def check_for_migration(apps, schema_editor):
+    if global_apps.is_installed('aleksis.apps.lesrooster'):
+        return
+
+    ExcuseType = apps.get_model('alsijil', 'ExcuseType')
+    PersonalNote = apps.get_model('alsijil', 'PersonalNote')
+    LessonDocumentation = apps.get_model('alsijil', 'LessonDocumentation')
+
+    model_types = [ExcuseType, PersonalNote, LessonDocumentation]
+
+    for model in model_types:
+        if model.objects.exists():
+            raise RuntimeError("You have legacy data. Please install AlekSIS-App-Lesrooster to migrate them.")
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('alsijil', '0023_add_tardiness_and_rework_constraints'),
+    ]
+
+    operations = [
+        migrations.RunPython(check_for_migration),
+    ]
diff --git a/aleksis/apps/alsijil/migrations/0025_remove_old_models.py b/aleksis/apps/alsijil/migrations/0025_remove_old_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..3093767e6a32330540ea6e90ea398bca03eeb7c6
--- /dev/null
+++ b/aleksis/apps/alsijil/migrations/0025_remove_old_models.py
@@ -0,0 +1,62 @@
+# Generated by Django 5.0.8 on 2024-08-06 16:18
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('alsijil', '0024_check_new_models'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='personalnote',
+            name='excuse_type',
+        ),
+        migrations.RemoveField(
+            model_name='lessondocumentation',
+            name='event',
+        ),
+        migrations.RemoveField(
+            model_name='lessondocumentation',
+            name='extra_lesson',
+        ),
+        migrations.RemoveField(
+            model_name='lessondocumentation',
+            name='lesson_period',
+        ),
+        migrations.RemoveField(
+            model_name='personalnote',
+            name='event',
+        ),
+        migrations.RemoveField(
+            model_name='personalnote',
+            name='extra_lesson',
+        ),
+        migrations.RemoveField(
+            model_name='personalnote',
+            name='extra_marks',
+        ),
+        migrations.RemoveField(
+            model_name='personalnote',
+            name='groups_of_person',
+        ),
+        migrations.RemoveField(
+            model_name='personalnote',
+            name='lesson_period',
+        ),
+        migrations.RemoveField(
+            model_name='personalnote',
+            name='person',
+        ),
+        migrations.DeleteModel(
+            name='ExcuseType',
+        ),
+        migrations.DeleteModel(
+            name='LessonDocumentation',
+        ),
+        migrations.DeleteModel(
+            name='PersonalNote',
+        ),
+    ]
diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py
index 33b3a15009a61146fe88ec62f6114ee55efd277b..8270ddb91bc494b0b7bb2032b3c06c99f3671edb 100644
--- a/aleksis/apps/alsijil/model_extensions.py
+++ b/aleksis/apps/alsijil/model_extensions.py
@@ -1,189 +1,12 @@
-from datetime import date
-from typing import Dict, Iterable, Iterator, Optional, Union
-
-from django.db.models import Exists, FilteredRelation, OuterRef, Q, QuerySet
-from django.db.models.aggregates import Count, Sum
-from django.urls import reverse
 from django.utils.translation import gettext as _
 
-from calendarweek import CalendarWeek
+from django.db.models import FilteredRelation, Q, QuerySet
+from django.db.models.aggregates import Count, Sum
 
-from aleksis.apps.alsijil.managers import PersonalNoteQuerySet
-from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
 from aleksis.apps.kolego.models import AbsenceReason
 from aleksis.core.models import Group, Person, SchoolTerm
-from aleksis.core.util.core_helpers import get_site_preferences
-
-from .models import Documentation, ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
-
-
-def alsijil_url(
-    self: Union[LessonPeriod, Event, ExtraLesson], week: Optional[CalendarWeek] = None
-) -> str:
-    """Build URL for the detail page of register objects.
-
-    Works with `LessonPeriod`, `Event` and `ExtraLesson`.
-
-    On `LessonPeriod` objects, it will work with annotated or passed weeks.
-    """
-    if isinstance(self, LessonPeriod):
-        week = week or self.week
-        return reverse("lesson_period", args=[week.year, week.week, self.pk])
-    else:
-        return reverse(self.label_, args=[self.pk])
-
-
-LessonPeriod.property_(alsijil_url)
-LessonPeriod.method(alsijil_url, "get_alsijil_url")
-Event.property_(alsijil_url)
-Event.method(alsijil_url, "get_alsijil_url")
-ExtraLesson.property_(alsijil_url)
-ExtraLesson.method(alsijil_url, "get_alsijil_url")
 
-
-@Person.method
-def mark_absent(
-    self,
-    day: date,
-    from_period: int = 0,
-    absent: bool = True,
-    excused: bool = False,
-    excuse_type: Optional[ExcuseType] = None,
-    remarks: str = "",
-    to_period: Optional[int] = None,
-    dry_run: bool = False,
-):
-    """Mark a person absent for all lessons in a day, optionally starting with a period number.
-
-    This function creates `PersonalNote` objects for every `LessonPeriod` and `ExtraLesson`
-    the person participates in on the selected day and marks them as absent/excused.
-
-    :param dry_run: With this activated, the function won't change any data
-        and just return the count of affected lessons
-
-    :return: Count of affected lesson periods
-
-    ..note:: Only available when AlekSIS-App-Alsijil is installed.
-
-    :Date: 2019-11-10
-    :Authors:
-        - Dominik George <dominik.george@teckids.org>
-    """
-    wanted_week = CalendarWeek.from_date(day)
-
-    # Get all lessons of this person on the specified day
-    lesson_periods = (
-        self.lesson_periods_as_participant.on_day(day)
-        .filter(period__period__gte=from_period)
-        .annotate_week(wanted_week)
-    )
-    extra_lessons = (
-        ExtraLesson.objects.filter(groups__members=self)
-        .on_day(day)
-        .filter(period__period__gte=from_period)
-    )
-
-    if to_period:
-        lesson_periods = lesson_periods.filter(period__period__lte=to_period)
-        extra_lessons = extra_lessons.filter(period__period__lte=to_period)
-
-    # Create and update all personal notes for the discovered lesson periods
-    if not dry_run:
-        for register_object in list(lesson_periods) + list(extra_lessons):
-            if isinstance(register_object, LessonPeriod):
-                sub = register_object.get_substitution()
-                q_attrs = dict(
-                    week=wanted_week.week, year=wanted_week.year, lesson_period=register_object
-                )
-            else:
-                sub = None
-                q_attrs = dict(extra_lesson=register_object)
-
-            if sub and sub.cancelled:
-                continue
-
-            personal_note, created = (
-                PersonalNote.objects.select_related(None)
-                .prefetch_related(None)
-                .update_or_create(
-                    person=self,
-                    defaults={
-                        "absent": absent,
-                        "excused": excused,
-                        "excuse_type": excuse_type,
-                    },
-                    **q_attrs,
-                )
-            )
-            personal_note.groups_of_person.set(self.member_of.all())
-
-            if remarks:
-                if personal_note.remarks:
-                    personal_note.remarks += "; %s" % remarks
-                else:
-                    personal_note.remarks = remarks
-                personal_note.save()
-
-    return lesson_periods.count() + extra_lessons.count()
-
-
-def get_personal_notes(
-    self, persons: QuerySet, wanted_week: Optional[CalendarWeek] = None
-) -> PersonalNoteQuerySet:
-    """Get all personal notes for that register object in a specified week.
-
-    The week is optional for extra lessons and events as they have own date information.
-
-    Returns all linked `PersonalNote` objects,
-    filtered by the given week for `LessonPeriod` objects,
-    creating those objects that haven't been created yet.
-
-    ..note:: Only available when AlekSIS-App-Alsijil is installed.
-
-    :Date: 2019-11-09
-    :Authors:
-        - Dominik George <dominik.george@teckids.org>
-    """
-    # Find all persons in the associated groups that do not yet have a personal note for this lesson
-    if isinstance(self, LessonPeriod):
-        q_attrs = dict(week=wanted_week.week, year=wanted_week.year, lesson_period=self)
-    elif isinstance(self, Event):
-        q_attrs = dict(event=self)
-    else:
-        q_attrs = dict(extra_lesson=self)
-
-    missing_persons = persons.annotate(
-        no_personal_notes=~Exists(PersonalNote.objects.filter(person__pk=OuterRef("pk"), **q_attrs))
-    ).filter(
-        member_of__in=Group.objects.filter(pk__in=self.get_groups().all()),
-        no_personal_notes=True,
-    )
-
-    # Create all missing personal notes
-    new_personal_notes = [
-        PersonalNote(
-            person=person,
-            **q_attrs,
-        )
-        for person in missing_persons
-    ]
-    PersonalNote.objects.bulk_create(new_personal_notes)
-
-    for personal_note in new_personal_notes:
-        personal_note.groups_of_person.set(personal_note.person.member_of.all())
-
-    return (
-        PersonalNote.objects.filter(**q_attrs, person__in=persons)
-        .select_related(None)
-        .prefetch_related(None)
-        .select_related("person", "excuse_type")
-        .prefetch_related("extra_marks")
-    )
-
-
-LessonPeriod.method(get_personal_notes)
-Event.method(get_personal_notes)
-ExtraLesson.method(get_personal_notes)
+from .models import Documentation, ExtraMark
 
 # Dynamically add extra permissions to Group and Person models in core
 # Note: requires migrate afterwards
@@ -211,290 +34,6 @@ Group.add_permission("assign_grouprole", _("Can assign a group role for this gro
 Person.add_permission("register_absence_person", _("Can register an absence for a person"))
 
 
-@LessonPeriod.method
-def get_lesson_documentation(
-    self, week: Optional[CalendarWeek] = None
-) -> Union[LessonDocumentation, None]:
-    """Get lesson documentation object for this lesson."""
-    if not week:
-        week = self.week
-    # Use all to make effect of prefetched data
-    doc_filter = filter(
-        lambda d: d.week == week.week and d.year == week.year,
-        self.documentations.all(),
-    )
-    try:
-        return next(doc_filter)
-    except StopIteration:
-        return None
-
-
-def get_lesson_documentation_single(
-    self, week: Optional[CalendarWeek] = None
-) -> Union[LessonDocumentation, None]:
-    """Get lesson documentation object for this event/extra lesson."""
-    if self.documentations.exists():
-        return self.documentations.all()[0]
-    return None
-
-
-Event.method(get_lesson_documentation_single, "get_lesson_documentation")
-ExtraLesson.method(get_lesson_documentation_single, "get_lesson_documentation")
-
-
-@LessonPeriod.method
-def get_or_create_lesson_documentation(
-    self, week: Optional[CalendarWeek] = None
-) -> LessonDocumentation:
-    """Get or create lesson documentation object for this lesson."""
-    if not week:
-        week = self.week
-    lesson_documentation, __ = LessonDocumentation.objects.get_or_create(
-        lesson_period=self, week=week.week, year=week.year
-    )
-    return lesson_documentation
-
-
-def get_or_create_lesson_documentation_single(
-    self, week: Optional[CalendarWeek] = None
-) -> LessonDocumentation:
-    """Get or create lesson documentation object for this event/extra lesson."""
-    lesson_documentation, created = LessonDocumentation.objects.get_or_create(**{self.label_: self})
-    return lesson_documentation
-
-
-Event.method(get_or_create_lesson_documentation_single, "get_or_create_lesson_documentation")
-ExtraLesson.method(get_or_create_lesson_documentation_single, "get_or_create_lesson_documentation")
-
-
-@LessonPeriod.method
-def get_absences(self, week: Optional[CalendarWeek] = None) -> Iterator:
-    """Get all personal notes of absent persons for this lesson."""
-    if not week:
-        week = self.week
-
-    return filter(
-        lambda p: p.week == week.week and p.year == week.year and p.absent,
-        self.personal_notes.all(),
-    )
-
-
-def get_absences_simple(self, week: Optional[CalendarWeek] = None) -> Iterator:
-    """Get all personal notes of absent persons for this event/extra lesson."""
-    return filter(lambda p: p.absent, self.personal_notes.all())
-
-
-Event.method(get_absences_simple, "get_absences")
-ExtraLesson.method(get_absences_simple, "get_absences")
-
-
-@LessonPeriod.method
-def get_excused_absences(self, week: Optional[CalendarWeek] = None) -> QuerySet:
-    """Get all personal notes of excused absent persons for this lesson."""
-    if not week:
-        week = self.week
-    return self.personal_notes.filter(week=week.week, year=week.year, absent=True, excused=True)
-
-
-def get_excused_absences_simple(self, week: Optional[CalendarWeek] = None) -> QuerySet:
-    """Get all personal notes of excused absent persons for this event/extra lesson."""
-    return self.personal_notes.filter(absent=True, excused=True)
-
-
-Event.method(get_excused_absences_simple, "get_excused_absences")
-ExtraLesson.method(get_excused_absences_simple, "get_excused_absences")
-
-
-@LessonPeriod.method
-def get_unexcused_absences(self, week: Optional[CalendarWeek] = None) -> QuerySet:
-    """Get all personal notes of unexcused absent persons for this lesson."""
-    if not week:
-        week = self.week
-    return self.personal_notes.filter(week=week.week, year=week.year, absent=True, excused=False)
-
-
-def get_unexcused_absences_simple(self, week: Optional[CalendarWeek] = None) -> QuerySet:
-    """Get all personal notes of unexcused absent persons for this event/extra lesson."""
-    return self.personal_notes.filter(absent=True, excused=False)
-
-
-Event.method(get_unexcused_absences_simple, "get_unexcused_absences")
-ExtraLesson.method(get_unexcused_absences_simple, "get_unexcused_absences")
-
-
-@LessonPeriod.method
-def get_tardinesses(self, week: Optional[CalendarWeek] = None) -> QuerySet:
-    """Get all personal notes of late persons for this lesson."""
-    if not week:
-        week = self.week
-    return self.personal_notes.filter(week=week.week, year=week.year, tardiness__gt=0)
-
-
-def get_tardinesses_simple(self, week: Optional[CalendarWeek] = None) -> QuerySet:
-    """Get all personal notes of late persons for this event/extra lesson."""
-    return self.personal_notes.filter(tardiness__gt=0)
-
-
-Event.method(get_tardinesses_simple, "get_tardinesses")
-ExtraLesson.method(get_tardinesses_simple, "get_tardinesses")
-
-
-@LessonPeriod.method
-def get_extra_marks(self, week: Optional[CalendarWeek] = None) -> Dict[ExtraMark, QuerySet]:
-    """Get all statistics on extra marks for this lesson."""
-    if not week:
-        week = self.week
-
-    stats = {}
-    for extra_mark in ExtraMark.objects.all():
-        qs = self.personal_notes.filter(week=week.week, year=week.year, extra_marks=extra_mark)
-        if qs:
-            stats[extra_mark] = qs
-
-    return stats
-
-
-def get_extra_marks_simple(self, week: Optional[CalendarWeek] = None) -> Dict[ExtraMark, QuerySet]:
-    """Get all statistics on extra marks for this event/extra lesson."""
-    stats = {}
-    for extra_mark in ExtraMark.objects.all():
-        qs = self.personal_notes.filter(extra_marks=extra_mark)
-        if qs:
-            stats[extra_mark] = qs
-
-    return stats
-
-
-Event.method(get_extra_marks_simple, "get_extra_marks")
-ExtraLesson.method(get_extra_marks_simple, "get_extra_marks")
-
-
-@Group.class_method
-def get_groups_with_lessons(cls: Group):
-    """Get all groups which have related lessons or child groups with related lessons."""
-    group_pks = (
-        cls.objects.for_current_school_term_or_all()
-        .annotate(lessons_count=Count("lessons"))
-        .filter(lessons_count__gt=0)
-        .values_list("pk", flat=True)
-    )
-    groups = cls.objects.filter(Q(child_groups__pk__in=group_pks) | Q(pk__in=group_pks)).distinct()
-
-    return groups
-
-
-@Person.method
-def get_owner_groups_with_lessons(self: Person):
-    """Get all groups the person is an owner of and which have related lessons.
-
-    Groups which have child groups with related lessons are also included, as well as all
-    child groups of the groups owned by the person with related lessons if the
-    inherit_privileges_from_parent_group preference is turned on.
-    """
-    if get_site_preferences()["alsijil__inherit_privileges_from_parent_group"]:
-        return (
-            Group.get_groups_with_lessons()
-            .filter(Q(owners=self) | Q(parent_groups__owners=self))
-            .distinct()
-        )
-    return Group.get_groups_with_lessons().filter(owners=self).distinct()
-
-
-@Group.method
-def generate_person_list_with_class_register_statistics(
-    self: Group, persons: Optional[Iterable] = None
-) -> QuerySet:
-    """Get with class register statistics annotated list of all members."""
-    if persons is None:
-        persons = self.members.all()
-
-    lesson_periods = LessonPeriod.objects.filter(
-        lesson__validity__school_term=self.school_term
-    ).filter(Q(lesson__groups=self) | Q(lesson__groups__parent_groups=self))
-    extra_lessons = ExtraLesson.objects.filter(school_term=self.school_term).filter(
-        Q(groups=self) | Q(groups__parent_groups=self)
-    )
-    events = Event.objects.filter(school_term=self.school_term).filter(
-        Q(groups=self) | Q(groups__parent_groups=self)
-    )
-
-    persons = persons.select_related("primary_group", "primary_group__school_term").order_by(
-        "last_name", "first_name"
-    )
-    persons = persons.annotate(
-        filtered_personal_notes=FilteredRelation(
-            "personal_notes",
-            condition=(
-                Q(personal_notes__event__in=events)
-                | Q(personal_notes__lesson_period__in=lesson_periods)
-                | Q(personal_notes__extra_lesson__in=extra_lessons)
-            ),
-        )
-    ).annotate(
-        absences_count=Count(
-            "filtered_personal_notes",
-            filter=Q(filtered_personal_notes__absent=True)
-            & ~Q(filtered_personal_notes__excuse_type__count_as_absent=False),
-            distinct=True,
-        ),
-        excused=Count(
-            "filtered_personal_notes",
-            filter=Q(
-                filtered_personal_notes__absent=True,
-                filtered_personal_notes__excused=True,
-            )
-            & ~Q(filtered_personal_notes__excuse_type__count_as_absent=False),
-            distinct=True,
-        ),
-        excused_without_excuse_type=Count(
-            "filtered_personal_notes",
-            filter=Q(
-                filtered_personal_notes__absent=True,
-                filtered_personal_notes__excused=True,
-                filtered_personal_notes__excuse_type__isnull=True,
-            ),
-            distinct=True,
-        ),
-        unexcused=Count(
-            "filtered_personal_notes",
-            filter=Q(filtered_personal_notes__absent=True, filtered_personal_notes__excused=False),
-            distinct=True,
-        ),
-        tardiness=Sum("filtered_personal_notes__tardiness"),
-        tardiness_count=Count(
-            "filtered_personal_notes",
-            filter=Q(filtered_personal_notes__tardiness__gt=0),
-            distinct=True,
-        ),
-    )
-
-    for extra_mark in ExtraMark.objects.all():
-        persons = persons.annotate(
-            **{
-                extra_mark.count_label: Count(
-                    "filtered_personal_notes",
-                    filter=Q(filtered_personal_notes__extra_marks=extra_mark),
-                    distinct=True,
-                )
-            }
-        )
-
-    for excuse_type in ExcuseType.objects.all():
-        persons = persons.annotate(
-            **{
-                excuse_type.count_label: Count(
-                    "filtered_personal_notes",
-                    filter=Q(
-                        filtered_personal_notes__absent=True,
-                        filtered_personal_notes__excuse_type=excuse_type,
-                    ),
-                    distinct=True,
-                )
-            }
-        )
-
-    return persons
-
 
 def annotate_person_statistics(
     persons: QuerySet[Person], participations_filter: Q, personal_notes_filter: Q
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 5303a40e57e3bf7ac6bbeba98da2f6e206be6007..fb9e57c40fa66ce75b93e11c451cb87a6b21f69c 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -1,12 +1,10 @@
-from datetime import date, datetime
-from typing import Optional, Union
-from urllib.parse import urlparse
+from datetime import datetime
+from typing import Optional
 
 from django.contrib.auth.models import User
 from django.core.exceptions import PermissionDenied
 from django.db import models
 from django.db.models import QuerySet
-from django.db.models.constraints import CheckConstraint
 from django.db.models.query_utils import Q
 from django.http import HttpRequest
 from django.urls import reverse
@@ -15,420 +13,27 @@ from django.utils.formats import date_format
 from django.utils.timezone import localdate, localtime, now
 from django.utils.translation import gettext_lazy as _
 
-from calendarweek import CalendarWeek
 from colorfield.fields import ColorField
 
-from aleksis.apps.alsijil.data_checks import (
-    ExcusesWithoutAbsences,
-    LessonDocumentationOnHolidaysDataCheck,
-    NoGroupsOfPersonsSetInPersonalNotesDataCheck,
-    NoPersonalNotesInCancelledLessonsDataCheck,
-    PersonalNoteOnHolidaysDataCheck,
-)
 from aleksis.apps.alsijil.managers import (
     DocumentationManager,
     GroupRoleAssignmentManager,
     GroupRoleAssignmentQuerySet,
     GroupRoleManager,
     GroupRoleQuerySet,
-    LessonDocumentationManager,
-    LessonDocumentationQuerySet,
     ParticipationStatusManager,
-    PersonalNoteManager,
-    PersonalNoteQuerySet,
 )
-from aleksis.apps.chronos.managers import GroupPropertiesMixin
-from aleksis.apps.chronos.mixins import WeekRelatedMixin
-from aleksis.apps.chronos.models import Event, ExtraLesson, LessonEvent, LessonPeriod, TimePeriod
-from aleksis.apps.chronos.util.format import format_m2m
+from aleksis.apps.chronos.models import LessonEvent
 from aleksis.apps.cursus.models import Course, Subject
 from aleksis.apps.kolego.models import Absence as KolegoAbsence
 from aleksis.apps.kolego.models import AbsenceReason
 from aleksis.core.data_checks import field_validation_data_check_factory
 from aleksis.core.mixins import ExtensibleModel, GlobalPermissionModel
-from aleksis.core.models import CalendarEvent, Group, Person, SchoolTerm
+from aleksis.core.models import CalendarEvent, Group, Person
 from aleksis.core.util.core_helpers import get_site_preferences
 from aleksis.core.util.model_helpers import ICONS
 
 
-def isidentifier(value: str) -> bool:
-    return value.isidentifier()
-
-
-class ExcuseType(ExtensibleModel):
-    """An type of excuse.
-
-    Can be used to count different types of absences separately.
-    """
-
-    short_name = models.CharField(max_length=255, unique=True, verbose_name=_("Short name"))
-    name = models.CharField(max_length=255, unique=True, verbose_name=_("Name"))
-
-    count_as_absent = models.BooleanField(
-        default=True,
-        verbose_name=_("Count as absent"),
-        help_text=_(
-            "If checked, this excuse type will be counted as a missed lesson. If not checked,"
-            "it won't show up in the absence report."
-        ),
-    )
-
-    def __str__(self):
-        return f"{self.name} ({self.short_name})"
-
-    @property
-    def count_label(self):
-        return f"excuse_type_{self.id}_count"
-
-    class Meta:
-        ordering = ["name"]
-        verbose_name = _("Excuse type")
-        verbose_name_plural = _("Excuse types")
-
-
-lesson_related_constraint_q = (
-    Q(
-        lesson_period__isnull=False,
-        event__isnull=True,
-        extra_lesson__isnull=True,
-        week__isnull=False,
-        year__isnull=False,
-    )
-    | Q(
-        lesson_period__isnull=True,
-        event__isnull=False,
-        extra_lesson__isnull=True,
-        week__isnull=True,
-        year__isnull=True,
-    )
-    | Q(
-        lesson_period__isnull=True,
-        event__isnull=True,
-        extra_lesson__isnull=False,
-        week__isnull=True,
-        year__isnull=True,
-    )
-)
-
-
-class RegisterObjectRelatedMixin(WeekRelatedMixin):
-    """Mixin with common API for lesson documentations and personal notes."""
-
-    @property
-    def register_object(
-        self: Union["LessonDocumentation", "PersonalNote"],
-    ) -> Union[LessonPeriod, Event, ExtraLesson]:
-        """Get the object related to this lesson documentation or personal note."""
-        if self.lesson_period:
-            return self.lesson_period
-        elif self.event:
-            return self.event
-        else:
-            return self.extra_lesson
-
-    @property
-    def register_object_key(self: Union["LessonDocumentation", "PersonalNote"]) -> str:
-        """Get a unique reference to the related object related."""
-        if self.week and self.year:
-            return f"{self.register_object.pk}_{self.week}_{self.year}"
-        else:
-            return self.register_object.pk
-
-    @property
-    def calendar_week(self: Union["LessonDocumentation", "PersonalNote"]) -> CalendarWeek:
-        """Get the calendar week of this lesson documentation or personal note.
-
-        .. note::
-
-            As events can be longer than one week,
-            this will return the week of the start date for events.
-        """
-        if self.lesson_period:
-            return CalendarWeek(week=self.week, year=self.year)
-        elif self.extra_lesson:
-            return self.extra_lesson.calendar_week
-        else:
-            return CalendarWeek.from_date(self.register_object.date_start)
-
-    @property
-    def school_term(self: Union["LessonDocumentation", "PersonalNote"]) -> SchoolTerm:
-        """Get the school term of the related register object."""
-        if self.lesson_period:
-            return self.lesson_period.lesson.validity.school_term
-        else:
-            return self.register_object.school_term
-
-    @property
-    def date(self: Union["LessonDocumentation", "PersonalNote"]) -> Optional[date]:
-        """Get the date of this lesson documentation or personal note.
-
-        :: warning::
-
-            As events can be longer than one day,
-            this will return `None` for events.
-        """
-        if self.lesson_period:
-            return super().date
-        elif self.extra_lesson:
-            return self.extra_lesson.date
-        return None
-
-    @property
-    def date_formatted(self: Union["LessonDocumentation", "PersonalNote"]) -> str:
-        """Get a formatted version of the date of this object.
-
-        Lesson periods, extra lessons: formatted date
-        Events: formatted date range
-        """
-        return (
-            date_format(self.date)
-            if self.date
-            else f"{date_format(self.event.date_start)}–{date_format(self.event.date_end)}"
-        )
-
-    @property
-    def period(self: Union["LessonDocumentation", "PersonalNote"]) -> TimePeriod:
-        """Get the date of this lesson documentation or personal note.
-
-        :: warning::
-
-            As events can be longer than one day,
-            this will return `None` for events.
-        """
-        if self.event:
-            return self.event.period_from
-        else:
-            return self.register_object.period
-
-    @property
-    def period_formatted(self: Union["LessonDocumentation", "PersonalNote"]) -> str:
-        """Get a formatted version of the period of this object.
-
-        Lesson periods, extra lessons: formatted period
-        Events: formatted period range
-        """
-        return (
-            f"{self.period.period}."
-            if not self.event
-            else f"{self.event.period_from.period}.–{self.event.period_to.period}."
-        )
-
-    def get_absolute_url(self: Union["LessonDocumentation", "PersonalNote"]) -> str:
-        """Get the absolute url of the detail view for the related register object."""
-        return self.register_object.get_alsijil_url(self.calendar_week)
-
-
-class PersonalNote(RegisterObjectRelatedMixin, ExtensibleModel):
-    """A personal note about a single person.
-
-    Used in the class register to note absences, excuses
-    and remarks about a student in a single lesson period.
-    """
-
-    data_checks = [
-        NoPersonalNotesInCancelledLessonsDataCheck,
-        NoGroupsOfPersonsSetInPersonalNotesDataCheck,
-        PersonalNoteOnHolidaysDataCheck,
-        ExcusesWithoutAbsences,
-    ]
-
-    objects = PersonalNoteManager.from_queryset(PersonalNoteQuerySet)()
-
-    person = models.ForeignKey("core.Person", models.CASCADE, related_name="personal_notes")
-    groups_of_person = models.ManyToManyField("core.Group", related_name="+")
-
-    week = models.IntegerField(blank=True, null=True)
-    year = models.IntegerField(verbose_name=_("Year"), blank=True, null=True)
-
-    lesson_period = models.ForeignKey(
-        "chronos.LessonPeriod", models.CASCADE, related_name="personal_notes", blank=True, null=True
-    )
-    event = models.ForeignKey(
-        "chronos.Event", models.CASCADE, related_name="personal_notes", blank=True, null=True
-    )
-    extra_lesson = models.ForeignKey(
-        "chronos.ExtraLesson", models.CASCADE, related_name="personal_notes", blank=True, null=True
-    )
-
-    absent = models.BooleanField(default=False)
-    tardiness = models.PositiveSmallIntegerField(default=0)
-    excused = models.BooleanField(default=False)
-    excuse_type = models.ForeignKey(
-        ExcuseType,
-        on_delete=models.SET_NULL,
-        null=True,
-        blank=True,
-        verbose_name=_("Excuse type"),
-    )
-
-    remarks = models.CharField(max_length=200, blank=True)
-
-    extra_marks = models.ManyToManyField("ExtraMark", blank=True, verbose_name=_("Extra marks"))
-
-    def save(self, *args, **kwargs):
-        if self.excuse_type:
-            self.excused = True
-        if not self.absent:
-            self.excused = False
-            self.excuse_type = None
-        super().save(*args, **kwargs)
-
-    def reset_values(self):
-        """Reset all saved data to default values.
-
-        .. warning ::
-
-            This won't save the data, please execute ``save`` extra.
-        """
-        defaults = PersonalNote()
-
-        self.absent = defaults.absent
-        self.tardiness = defaults.tardiness
-        self.excused = defaults.excused
-        self.excuse_type = defaults.excuse_type
-        self.remarks = defaults.remarks
-        self.extra_marks.clear()
-
-    def __str__(self) -> str:
-        return f"{self.date_formatted}, {self.lesson_period}, {self.person}"
-
-    def get_absolute_url(self) -> str:
-        """Get the absolute url of the detail view for the related register object."""
-        return urlparse(super().get_absolute_url())._replace(fragment="personal-notes").geturl()
-
-    class Meta:
-        verbose_name = _("Personal note")
-        verbose_name_plural = _("Personal notes")
-        ordering = [
-            "year",
-            "week",
-            "lesson_period__period__weekday",
-            "lesson_period__period__period",
-            "person__last_name",
-            "person__first_name",
-        ]
-        constraints = [
-            CheckConstraint(
-                check=lesson_related_constraint_q, name="one_relation_only_personal_note"
-            ),
-            models.UniqueConstraint(
-                fields=("week", "year", "lesson_period", "person"),
-                name="unique_note_per_lp",
-            ),
-            models.UniqueConstraint(
-                fields=("week", "year", "event", "person"),
-                name="unique_note_per_ev",
-            ),
-            models.UniqueConstraint(
-                fields=("week", "year", "extra_lesson", "person"),
-                name="unique_note_per_el",
-            ),
-        ]
-
-
-class LessonDocumentation(RegisterObjectRelatedMixin, ExtensibleModel):
-    """A documentation on a single lesson period.
-
-    Non-personal, includes the topic and homework of the lesson.
-    """
-
-    objects = LessonDocumentationManager.from_queryset(LessonDocumentationQuerySet)()
-
-    data_checks = [LessonDocumentationOnHolidaysDataCheck]
-
-    week = models.IntegerField(blank=True, null=True)
-    year = models.IntegerField(verbose_name=_("Year"), blank=True, null=True)
-
-    lesson_period = models.ForeignKey(
-        "chronos.LessonPeriod", models.CASCADE, related_name="documentations", blank=True, null=True
-    )
-    event = models.ForeignKey(
-        "chronos.Event", models.CASCADE, related_name="documentations", blank=True, null=True
-    )
-    extra_lesson = models.ForeignKey(
-        "chronos.ExtraLesson", models.CASCADE, related_name="documentations", blank=True, null=True
-    )
-
-    topic = models.CharField(verbose_name=_("Lesson topic"), max_length=200, blank=True)
-    homework = models.CharField(verbose_name=_("Homework"), max_length=200, blank=True)
-    group_note = models.CharField(verbose_name=_("Group note"), max_length=200, blank=True)
-
-    def carry_over_data(self, all_periods_of_lesson: LessonPeriod):
-        """Carry over data to given periods in this lesson if data is not already set.
-
-        Both forms of carrying over data can be deactivated using site preferences
-        ``alsijil__carry_over_next_periods`` and ``alsijil__allow_carry_over_same_week``
-        respectively.
-        """
-        for period in all_periods_of_lesson:
-            lesson_documentation = period.get_or_create_lesson_documentation(
-                CalendarWeek(week=self.week, year=self.year)
-            )
-
-            changed = False
-
-            if not lesson_documentation.topic:
-                lesson_documentation.topic = self.topic
-                changed = True
-
-            if not lesson_documentation.homework:
-                lesson_documentation.homework = self.homework
-                changed = True
-
-            if not lesson_documentation.group_note:
-                lesson_documentation.group_note = self.group_note
-                changed = True
-
-            if changed:
-                lesson_documentation.save(carry_over_next_periods=False)
-
-    def __str__(self) -> str:
-        return f"{self.lesson_period}, {self.date_formatted}"
-
-    def save(self, carry_over_next_periods=True, *args, **kwargs):
-        if (
-            get_site_preferences()["alsijil__carry_over_next_periods"]
-            and (self.topic or self.homework or self.group_note)
-            and self.lesson_period
-            and carry_over_next_periods
-        ):
-            self.carry_over_data(
-                LessonPeriod.objects.filter(
-                    lesson=self.lesson_period.lesson,
-                    period__weekday=self.lesson_period.period.weekday,
-                )
-            )
-        super().save(*args, **kwargs)
-
-    class Meta:
-        verbose_name = _("Lesson documentation")
-        verbose_name_plural = _("Lesson documentations")
-        ordering = [
-            "year",
-            "week",
-            "lesson_period__period__weekday",
-            "lesson_period__period__period",
-        ]
-        constraints = [
-            CheckConstraint(
-                check=lesson_related_constraint_q,
-                name="one_relation_only_lesson_documentation",
-            ),
-            models.UniqueConstraint(
-                fields=("week", "year", "lesson_period"),
-                name="unique_documentation_per_lp",
-            ),
-            models.UniqueConstraint(
-                fields=("week", "year", "event"),
-                name="unique_documentation_per_ev",
-            ),
-            models.UniqueConstraint(
-                fields=("week", "year", "extra_lesson"),
-                name="unique_documentation_per_el",
-            ),
-        ]
-
-
 class ExtraMark(ExtensibleModel):
     """A model for extra marks.
 
@@ -517,7 +122,7 @@ class Documentation(CalendarEvent):
         start_datetime = CalendarEvent.value_start_datetime(self)
         end_datetime = CalendarEvent.value_end_datetime(self)
         return (
-            f"{format_m2m(self.get_groups())} {self.get_subject()}"
+            f"{','.join([str(g) for g in self.get_groups()])} {self.get_subject()}"
             + f" {start_datetime} - {end_datetime}"
         )
 
@@ -950,7 +555,7 @@ class GroupRole(ExtensibleModel):
         return reverse("edit_group_role", args=[self.id])
 
 
-class GroupRoleAssignment(GroupPropertiesMixin, ExtensibleModel):
+class GroupRoleAssignment(ExtensibleModel):
     objects = GroupRoleAssignmentManager.from_queryset(GroupRoleAssignmentQuerySet)()
 
     role = models.ForeignKey(
diff --git a/aleksis/apps/alsijil/preferences.py b/aleksis/apps/alsijil/preferences.py
index 74ed22802286eaeccc4d92b6c1e255364c3503b2..b0d8fbc42c9227e32c227bf3ac10ef9c1635fba6 100644
--- a/aleksis/apps/alsijil/preferences.py
+++ b/aleksis/apps/alsijil/preferences.py
@@ -1,122 +1,19 @@
-from django.core.exceptions import ValidationError
 from django.utils.translation import gettext_lazy as _
 
 from dynamic_preferences.preferences import Section
 from dynamic_preferences.types import (
     BooleanPreference,
     ChoicePreference,
-    IntegerPreference,
     ModelChoicePreference,
     ModelMultipleChoicePreference,
 )
 
 from aleksis.core.models import GroupType
-from aleksis.core.registries import person_preferences_registry, site_preferences_registry
+from aleksis.core.registries import site_preferences_registry
 
 alsijil = Section("alsijil", verbose_name=_("Class register"))
 
 
-@site_preferences_registry.register
-class BlockPersonalNotesForCancelled(BooleanPreference):
-    section = alsijil
-    name = "block_personal_notes_for_cancelled"
-    default = True
-    verbose_name = _("Block adding personal notes for cancelled lessons")
-
-
-@site_preferences_registry.register
-class ViewOwnPersonalNotes(BooleanPreference):
-    section = alsijil
-    name = "view_own_personal_notes"
-    default = True
-    verbose_name = _("Allow users to view their own personal notes")
-
-
-@site_preferences_registry.register
-class RegisterAbsenceAsPrimaryGroupOwner(BooleanPreference):
-    section = alsijil
-    name = "register_absence_as_primary_group_owner"
-    default = True
-    verbose_name = _(
-        "Allow primary group owners to register future absences for students in their groups"
-    )
-
-
-@site_preferences_registry.register
-class InheritPrivilegesFromParentGroup(BooleanPreference):
-    section = alsijil
-    name = "inherit_privileges_from_parent_group"
-    default = True
-    verbose_name = _(
-        "Grant the owner of a parent group the same privileges "
-        "as the owners of the respective child groups"
-    )
-
-
-@site_preferences_registry.register
-class EditLessonDocumentationAsOriginalTeacher(BooleanPreference):
-    section = alsijil
-    name = "edit_lesson_documentation_as_original_teacher"
-    default = True
-    verbose_name = _("Allow original teachers to edit their lessons although they are substituted")
-
-
-@site_preferences_registry.register
-class CarryOverDataToNextPeriods(BooleanPreference):
-    section = alsijil
-    name = "carry_over_next_periods"
-    default = True
-    verbose_name = _(
-        "Carry over data from first lesson period to the "
-        "following lesson periods in lessons over multiple periods"
-    )
-    help_text = _("This will carry over data only if the data in the following periods are empty.")
-
-
-@site_preferences_registry.register
-class AllowCarryOverLessonDocumentationToCurrentWeek(BooleanPreference):
-    section = alsijil
-    name = "allow_carry_over_same_week"
-    default = False
-    verbose_name = _(
-        "Allow carrying over data from any lesson period to all other lesson \
-                periods with the same lesson and in the same week"
-    )
-    help_text = _(
-        "This will carry over data only if the data in the aforementioned periods are empty."
-    )
-
-
-@site_preferences_registry.register
-class CarryOverPersonalNotesToNextPeriods(BooleanPreference):
-    section = alsijil
-    name = "carry_over_personal_notes"
-    default = True
-    verbose_name = _("Carry over personal notes to all following lesson periods on the same day.")
-
-
-@site_preferences_registry.register
-class AllowOpenPeriodsOnSameDay(BooleanPreference):
-    section = alsijil
-    name = "open_periods_same_day"
-    default = False
-    verbose_name = _(
-        "Allow teachers to open lesson periods on the "
-        "same day and not just at the beginning of the period"
-    )
-    help_text = _(
-        "Lessons in the past are not affected by this setting, you can open them whenever you want."
-    )
-
-
-@site_preferences_registry.register
-class AllowEntriesInHolidays(BooleanPreference):
-    section = alsijil
-    name = "allow_entries_in_holidays"
-    default = False
-    verbose_name = _("Allow teachers to add data for lessons in holidays")
-
-
 @site_preferences_registry.register
 class GroupOwnersCanAssignRolesToParents(BooleanPreference):
     section = alsijil
@@ -127,45 +24,6 @@ class GroupOwnersCanAssignRolesToParents(BooleanPreference):
     )
 
 
-@person_preferences_registry.register
-class ShowGroupRolesInWeekView(BooleanPreference):
-    section = alsijil
-    name = "group_roles_in_week_view"
-    default = True
-    verbose_name = _("Show assigned group roles in week view")
-    help_text = _("Only week view of groups")
-
-
-@person_preferences_registry.register
-class ShowGroupRolesInLessonView(BooleanPreference):
-    section = alsijil
-    name = "group_roles_in_lesson_view"
-    default = True
-    verbose_name = _("Show assigned group roles in lesson view")
-
-
-@person_preferences_registry.register
-class RegisterObjectsTableItemsPerPage(IntegerPreference):
-    """Preference how many items are shown per page in ``RegisterObjectTable``."""
-
-    section = alsijil
-    name = "register_objects_table_items_per_page"
-    default = 100
-    verbose_name = _("Items per page in lessons table")
-
-    def validate(self, value):
-        if value < 1:
-            raise ValidationError(_("Each page must show at least one item."))
-
-
-@person_preferences_registry.register
-class DefaultLessonDocumentationFilter(BooleanPreference):
-    section = alsijil
-    name = "default_lesson_documentation_filter"
-    default = True
-    verbose_name = _("Filter lessons by existence of their lesson documentation on default")
-
-
 @site_preferences_registry.register
 class AllowEditFutureDocumentations(ChoicePreference):
     """Time range for which documentations may be edited."""
diff --git a/aleksis/apps/alsijil/rules.py b/aleksis/apps/alsijil/rules.py
index 171a0765737f8b68164af78b3823c96481eef044..602663b519a0680ca88de3813ecbc38df64579a8 100644
--- a/aleksis/apps/alsijil/rules.py
+++ b/aleksis/apps/alsijil/rules.py
@@ -20,9 +20,7 @@ from .util.predicates import (
     can_view_documentation,
     can_view_participation_status,
     can_view_personal_note,
-    has_lesson_group_object_perm,
     has_person_group_object_perm,
-    has_personal_note_group_perm,
     is_course_group_owner,
     is_course_member,
     is_course_teacher,
@@ -33,143 +31,10 @@ from .util.predicates import (
     is_in_allowed_time_range_for_participation_status,
     is_lesson_event_group_owner,
     is_lesson_event_teacher,
-    is_lesson_original_teacher,
-    is_lesson_parent_group_owner,
-    is_lesson_participant,
-    is_lesson_teacher,
-    is_none,
-    is_own_personal_note,
     is_owner_of_any_group,
     is_parent_group_owner,
     is_person_group_owner,
-    is_person_primary_group_owner,
-    is_personal_note_lesson_original_teacher,
-    is_personal_note_lesson_parent_group_owner,
-    is_personal_note_lesson_teacher,
-    is_teacher,
-)
-
-# View lesson
-view_register_object_predicate = has_person & (
-    is_none  # View is opened as "Current lesson"
-    | is_lesson_teacher
-    | is_lesson_original_teacher
-    | is_lesson_participant
-    | is_lesson_parent_group_owner
-    | has_global_perm("alsijil.view_lesson")
-    | has_lesson_group_object_perm("core.view_week_class_register_group")
-)
-add_perm("alsijil.view_register_object_rule", view_register_object_predicate)
-
-# View lesson in menu
-add_perm("alsijil.view_lesson_menu_rule", has_person)
-
-# View lesson personal notes
-view_lesson_personal_notes_predicate = view_register_object_predicate & (
-    ~is_lesson_participant
-    | is_lesson_teacher
-    | is_lesson_original_teacher
-    | (
-        is_lesson_parent_group_owner
-        & is_site_preference_set("alsijil", "inherit_privileges_from_parent_group")
-    )
-    | has_global_perm("alsijil.view_personalnote")
-    | has_lesson_group_object_perm("core.view_personalnote_group")
-)
-add_perm("alsijil.view_register_object_personalnote_rule", view_lesson_personal_notes_predicate)
-
-# Edit personal note
-edit_lesson_personal_note_predicate = view_lesson_personal_notes_predicate & (
-    is_lesson_teacher
-    | (
-        is_lesson_original_teacher
-        & is_site_preference_set("alsijil", "edit_lesson_documentation_as_original_teacher")
-    )
-    | (
-        is_lesson_parent_group_owner
-        & is_site_preference_set("alsijil", "inherit_privileges_from_parent_group")
-    )
-    | has_global_perm("alsijil.change_personalnote")
-    | has_lesson_group_object_perm("core.edit_personalnote_group")
-)
-add_perm("alsijil.edit_register_object_personalnote_rule", edit_lesson_personal_note_predicate)
-
-# View personal note
-view_personal_note_predicate = has_person & (
-    (is_own_personal_note & is_site_preference_set("alsijil", "view_own_personal_notes"))
-    | is_personal_note_lesson_teacher
-    | is_personal_note_lesson_original_teacher
-    | is_personal_note_lesson_parent_group_owner
-    | has_global_perm("alsijil.view_personalnote")
-    | has_personal_note_group_perm("core.view_personalnote_group")
-)
-add_perm("alsijil.view_personalnote_rule", view_personal_note_predicate)
-
-# Edit personal note
-edit_personal_note_predicate = view_personal_note_predicate & (
-    ~is_own_personal_note
-    & ~(
-        is_personal_note_lesson_original_teacher
-        | ~is_site_preference_set("alsijil", "edit_lesson_documentation_as_original_teacher")
-    )
-    | (
-        is_personal_note_lesson_parent_group_owner
-        | is_site_preference_set("alsijil", "inherit_privileges_from_parent_group")
-    )
-    | has_global_perm("alsijil.view_personalnote")
-    | has_personal_note_group_perm("core.edit_personalnote_group")
-)
-add_perm("alsijil.edit_personalnote_rule", edit_personal_note_predicate)
-
-# View lesson documentation
-view_lesson_documentation_predicate = view_register_object_predicate
-add_perm("alsijil.view_lessondocumentation_rule", view_lesson_documentation_predicate)
-
-# Edit lesson documentation
-edit_lesson_documentation_predicate = view_register_object_predicate & (
-    is_lesson_teacher
-    | (
-        is_lesson_original_teacher
-        & is_site_preference_set("alsijil", "edit_lesson_documentation_as_original_teacher")
-    )
-    | (
-        is_lesson_parent_group_owner
-        & is_site_preference_set("alsijil", "inherit_privileges_from_parent_group")
-    )
-    | has_global_perm("alsijil.change_lessondocumentation")
-    | has_lesson_group_object_perm("core.edit_lessondocumentation_group")
-)
-add_perm("alsijil.edit_lessondocumentation_rule", edit_lesson_documentation_predicate)
-
-# View week overview
-view_week_predicate = has_person & (
-    is_current_person
-    | is_group_member
-    | is_group_owner
-    | (
-        is_parent_group_owner
-        & is_site_preference_set("alsijil", "inherit_privileges_from_parent_group")
-    )
-    | has_global_perm("alsijil.view_week")
-    | has_object_perm("core.view_week_class_register_group")
-)
-add_perm("alsijil.view_week_rule", view_week_predicate)
-
-# View week overview in menu
-add_perm("alsijil.view_week_menu_rule", has_person)
-
-# View week personal notes
-view_week_personal_notes_predicate = has_person & (
-    (is_current_person & is_teacher)
-    | is_group_owner
-    | (
-        is_parent_group_owner
-        & is_site_preference_set("alsijil", "inherit_privileges_from_parent_group")
-    )
-    | has_global_perm("alsijil.view_personalnote")
-    | has_object_perm("core.view_personalnote_group")
 )
-add_perm("alsijil.view_week_personalnote_rule", view_week_personal_notes_predicate)
 
 # Register absence
 view_register_absence_predicate = has_person & (
@@ -197,86 +62,6 @@ view_full_register_predicate = has_person & (
 )
 add_perm("alsijil.view_full_register_rule", view_full_register_predicate)
 
-# View students list
-view_my_students_predicate = has_person & is_teacher
-add_perm("alsijil.view_my_students_rule", view_my_students_predicate)
-
-# View groups list
-view_my_groups_predicate = has_person & is_teacher
-add_perm("alsijil.view_my_groups_rule", view_my_groups_predicate)
-
-# View students list
-view_students_list_predicate = view_my_groups_predicate & (
-    is_group_owner
-    | (
-        is_parent_group_owner
-        & is_site_preference_set("alsijil", "inherit_privileges_from_parent_group")
-    )
-    | has_global_perm("alsijil.view_personalnote")
-    | has_object_perm("core.view_personalnote_group")
-)
-add_perm("alsijil.view_students_list_rule", view_students_list_predicate)
-
-# View person overview
-view_person_overview_predicate = has_person & (
-    (is_current_person & is_site_preference_set("alsijil", "view_own_personal_notes"))
-    | is_person_group_owner
-)
-add_perm("alsijil.view_person_overview_rule", view_person_overview_predicate)
-
-# View person overview
-view_person_overview_menu_predicate = has_person
-add_perm("alsijil.view_person_overview_menu_rule", view_person_overview_menu_predicate)
-
-# View person overview personal notes
-view_person_overview_personal_notes_predicate = view_person_overview_predicate & (
-    (is_current_person & is_site_preference_set("alsijil", "view_own_personal_notes"))
-    | is_person_primary_group_owner
-    | has_global_perm("alsijil.view_personalnote")
-    | has_person_group_object_perm("core.view_personalnote_group")
-)
-add_perm(
-    "alsijil.view_person_overview_personalnote_rule",
-    view_person_overview_personal_notes_predicate,
-)
-
-# Edit person overview personal notes
-edit_person_overview_personal_notes_predicate = view_person_overview_predicate & (
-    ~is_current_person
-    | has_global_perm("alsijil.change_personalnote")
-    | has_person_group_object_perm("core.edit_personalnote_group")
-)
-add_perm(
-    "alsijil.edit_person_overview_personalnote_rule",
-    edit_person_overview_personal_notes_predicate,
-)
-
-# View person statistics on personal notes
-view_person_statistics_personal_notes_predicate = view_person_overview_personal_notes_predicate
-add_perm(
-    "alsijil.view_person_statistics_personalnote_rule",
-    view_person_statistics_personal_notes_predicate,
-)
-
-# View excuse type list
-view_excusetypes_predicate = has_person & has_global_perm("alsijil.view_excusetype")
-add_perm("alsijil.view_excusetypes_rule", view_excusetypes_predicate)
-
-# Add excuse type
-add_excusetype_predicate = view_excusetypes_predicate & has_global_perm("alsijil.add_excusetype")
-add_perm("alsijil.add_excusetype_rule", add_excusetype_predicate)
-
-# Edit excuse type
-edit_excusetype_predicate = view_excusetypes_predicate & has_global_perm(
-    "alsijil.change_excusetype"
-)
-add_perm("alsijil.edit_excusetype_rule", edit_excusetype_predicate)
-
-# Delete excuse type
-delete_excusetype_predicate = view_excusetypes_predicate & has_global_perm(
-    "alsijil.delete_excusetype"
-)
-add_perm("alsijil.delete_excusetype_rule", delete_excusetype_predicate)
 
 # View extra mark list
 view_extramarks_predicate = has_person & has_global_perm("alsijil.view_extramark")
@@ -327,17 +112,6 @@ view_assigned_group_roles_predicate = has_person & (
 )
 add_perm("alsijil.view_assigned_grouproles_rule", view_assigned_group_roles_predicate)
 
-view_assigned_group_roles_register_object_predicate = has_person & (
-    is_lesson_teacher
-    | is_lesson_original_teacher
-    | is_lesson_parent_group_owner
-    | has_global_perm("alsijil.assign_grouprole")
-)
-add_perm(
-    "alsijil.view_assigned_grouproles_for_register_object",
-    view_assigned_group_roles_register_object_predicate,
-)
-
 assign_group_role_person_predicate = has_person & (
     is_person_group_owner | has_global_perm("alsijil.assign_grouprole")
 )
@@ -466,3 +240,10 @@ add_perm(
     "alsijil.edit_personal_note_rule",
     edit_personal_note_predicate,
 )
+
+# View parent menu entry
+view_menu_predicate = has_person & (view_documentations_menu_predicate | view_extramarks_predicate)
+add_perm(
+    "alsijil.view_menu_rule",
+    view_menu_predicate,
+)
diff --git a/aleksis/apps/alsijil/static/css/alsijil/alsijil.css b/aleksis/apps/alsijil/static/css/alsijil/alsijil.css
deleted file mode 100644
index a30fb99bb8b239ca38f7f4d627329594387741c6..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/static/css/alsijil/alsijil.css
+++ /dev/null
@@ -1,60 +0,0 @@
-table.datatable a {
-  color: inherit !important;
-}
-
-table a.tr-link {
-  display: block;
-  width: inherit;
-  height: inherit;
-}
-
-.collapsible-icon-right {
-  align-self: end;
-  flex-grow: 100;
-  text-align: right !important;
-}
-
-@media only screen and (min-width: 1201px) {
-  .hide-on-extra-large-only {
-    display: none;
-  }
-}
-
-@media only screen and (max-width: 1200px) {
-  .show-on-extra-large {
-    display: none;
-  }
-}
-
-@media only screen and (max-width: 600px) {
-  .collection .collection-item.avatar {
-    padding-left: 20px;
-  }
-  .collection .collection-item.avatar:not(.circle-clipper) > .circle {
-    position: relative;
-    margin-bottom: 10px;
-  }
-}
-
-.collapsible li .show-on-active {
-  display: none;
-}
-
-.collapsible li.active .show-on-active {
-  display: block;
-}
-
-th.chip-height {
-  height: 67px;
-  line-height: 2.2;
-}
-
-.collection-item.chip-height {
-  height: 52px;
-  line-height: 2.2;
-}
-
-li.collection-item.button-height {
-  height: 58px;
-  line-height: 2.5;
-}
diff --git a/aleksis/apps/alsijil/static/css/alsijil/lesson.css b/aleksis/apps/alsijil/static/css/alsijil/lesson.css
deleted file mode 100644
index 3ca0427a81bba0793b78d41d884a5810f0080f74..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/static/css/alsijil/lesson.css
+++ /dev/null
@@ -1,140 +0,0 @@
-.alsijil-check-box {
-  margin-right: 10px;
-}
-
-.alsijil-check-box [type="checkbox"] {
-  padding-left: 30px;
-}
-
-.alsijil-lesson-cancelled {
-  text-decoration: line-through;
-}
-
-.alsijil-tardiness-text {
-  vertical-align: super;
-}
-
-@media only screen and (max-width: 992px) {
-  .no-mobile-card {
-    border: unset;
-    padding: unset;
-    margin: unset;
-    box-shadow: unset;
-  }
-  .no-mobile-card .card-content {
-    padding: unset;
-  }
-  table.alsijil-table.horizontal-on-small {
-    display: block;
-    max-width: calc(100vw - 40px);
-  }
-  table.alsijil-table.horizontal-on-small thead {
-    display: none;
-  }
-  table.alsijil-table.horizontal-on-small tbody {
-    overflow-x: scroll;
-    display: flex;
-    column-gap: 1rem;
-    flex-wrap: nowrap;
-    align-items: stretch;
-    scroll-snap-type: x proximity;
-  }
-
-  table.alsijil-table.horizontal-on-small tr {
-    flex-basis: min(75vw, 400px);
-    flex-shrink: 0;
-    flex-grow: 1;
-    border-radius: 8px;
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    scroll-snap-align: center;
-    transition: all 0.5s;
-    margin: 0.5rem 0 1rem 0;
-    background-color: #fff !important;
-    box-shadow:
-      0 2px 2px 0 rgba(0, 0, 0, 0.14),
-      0 3px 1px -2px rgba(0, 0, 0, 0.12),
-      0 1px 5px 0 rgba(0, 0, 0, 0.2);
-    padding: 24px;
-  }
-  table.alsijil-table.horizontal-on-small tr:first-of-type {
-    margin-inline-start: 0.4rem;
-    -moz-margin-start: 0.4rem;
-    -webkit-margin-start: 0.4rem;
-  }
-
-  table.alsijil-table.horizontal-on-small tr:last-of-type {
-    margin-inline-end: 0.4rem;
-    -moz-margin-end: 0.4rem;
-    -webkit-margin-end: 0.4rem;
-  }
-  table.alsijil-table.horizontal-on-small td.center-align {
-    text-align: left;
-  }
-  table.alsijil-table.horizontal-on-small .person-name {
-    font-size: 24px;
-    font-weight: 300;
-    display: block;
-    line-height: 32px;
-    margin-bottom: 8px;
-  }
-}
-
-.alsijil-time-head,
-.alsijil-object-head {
-  display: block;
-}
-
-.alsijil-time-head {
-  font-size: 2rem;
-  line-height: 1.1;
-}
-
-.alsijil-object-head {
-  font-size: 3rem;
-}
-
-@media only screen and (max-width: 600px) {
-  .alsijil-time-head {
-    font-size: 1.5rem;
-  }
-
-  .alsijil-object-head {
-    font-size: 2.2rem;
-    line-height: 1.4;
-  }
-}
-
-.alsijil-nav {
-  line-height: 36px;
-}
-
-.alsijil-header-nav-button {
-  height: 66px;
-  padding: 0;
-}
-
-.alsijil-header-nav-button.left {
-  margin-right: 5px;
-}
-
-.alsijil-header-nav-button.right {
-  margin-left: 5px;
-}
-
-.alsijil-header-nav-button i.material-icons {
-  line-height: 60px;
-  height: 60px;
-  font-size: 40px;
-}
-
-.alsijil-nav-header {
-  width: calc(100% + 40px);
-  padding: 10px 20px;
-  margin: -10px -20px 0;
-}
-
-.tabs-icons .tab svg.iconify {
-  display: block;
-}
diff --git a/aleksis/apps/alsijil/static/css/alsijil/person.css b/aleksis/apps/alsijil/static/css/alsijil/person.css
deleted file mode 100644
index d385d7b69e031fafd4c21d037b70b8f973e99b74..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/static/css/alsijil/person.css
+++ /dev/null
@@ -1,100 +0,0 @@
-span.input-field.inline > .select-wrapper > input {
-  color: red;
-  padding: 14px 0 0 0;
-  line-height: 2px;
-  height: 36px;
-  vertical-align: middle;
-}
-
-span.input-field.inline > .select-wrapper .caret {
-  top: 12px !important;
-}
-
-@media screen and (min-width: 1400px) {
-  li.collection-item form {
-    margin: -30px 0 -30px 0;
-  }
-
-  li.collection-item#title #select_all_span {
-    margin-top: 5px;
-  }
-}
-
-.collection {
-  overflow: visible;
-  overflow-x: hidden;
-}
-
-#select_all_container {
-  display: none;
-}
-
-#select_all_box:indeterminate + span:not(.lever):before {
-  top: -4px;
-  left: -6px;
-  width: 10px;
-  height: 12px;
-  border-top: none;
-  border-left: none;
-  border-right: white 2px solid;
-  border-bottom: none;
-  transform: rotate(90deg);
-  backface-visibility: hidden;
-  transform-origin: 100% 100%;
-}
-
-#select_all_box:indeterminate + span:not(.lever):after {
-  top: 0;
-  width: 20px;
-  height: 20px;
-  border: 2px solid currentColor;
-  background-color: currentColor;
-  z-index: 0;
-}
-
-#select_all_box_text {
-  color: #9e9e9e !important;
-}
-
-td.material-icons {
-  display: table-cell;
-}
-
-.medium-high {
-  position: relative;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, 50%);
-}
-
-@media screen and (min-width: 600px) {
-  /* On medium and up devices */
-  .medium-high-right {
-    float: right;
-    transform: translate(0%, 50%);
-  }
-}
-
-@media screen and (max-width: 600px) {
-  /* Only on small devices */
-  .full-width-s {
-    width: 100%;
-  }
-
-  #heading {
-    display: block;
-  }
-  #heading + a {
-    float: none !important;
-  }
-}
-
-.overflow-x-scroll {
-  overflow-x: scroll;
-}
-
-figure.modal-content figcaption {
-  font-weight: 300;
-  font-size: 2.28rem;
-  line-height: 110%;
-}
diff --git a/aleksis/apps/alsijil/static/css/alsijil/week_view.css b/aleksis/apps/alsijil/static/css/alsijil/week_view.css
deleted file mode 100644
index a42111f55e31d3b3f518873218c76fd246106afe..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/static/css/alsijil/week_view.css
+++ /dev/null
@@ -1,122 +0,0 @@
-@media screen and (max-width: 600px) {
-  #toggle-row button[type="submit"] {
-    width: 100%;
-    margin-bottom: 1em;
-  }
-}
-
-.horizontal-scroll-container {
-  overflow-x: scroll;
-  display: flex;
-  column-gap: 1rem;
-  flex-wrap: nowrap;
-  align-items: stretch;
-  scroll-snap-type: x proximity;
-}
-
-.horizontal-scroll-container.vertical {
-  flex-wrap: wrap;
-  overflow-x: inherit;
-}
-
-.horizontal-scroll-container.vertical .horizontal-scroll-card {
-  margin-inline: 0;
-}
-
-dl {
-  margin: 0;
-  padding: 0;
-}
-
-dt {
-  font-weight: bold;
-}
-
-dd {
-  margin: 0;
-  padding: unset;
-}
-
-.horizontal-scroll-card {
-  flex-basis: min(75vw, 400px);
-  flex-shrink: 0;
-  flex-grow: 1;
-  border-radius: 8px;
-  display: flex;
-  flex-direction: column;
-  justify-content: space-between;
-  scroll-snap-align: center;
-  transition: all 0.5s;
-}
-
-.horizontal-scroll-card:first-of-type {
-  margin-inline-start: 0.4rem;
-  -moz-margin-start: 0.4rem;
-  -webkit-margin-start: 0.4rem;
-}
-
-.horizontal-scroll-card:last-of-type {
-  margin-inline-end: 0.4rem;
-  -moz-margin-end: 0.4rem;
-  -webkit-margin-end: 0.4rem;
-}
-
-.horizontal-scroll-card .card-action {
-  margin-bottom: 5px;
-}
-
-.horizontal-scroll-card .card-content .card-title {
-  display: flex;
-  justify-content: space-between;
-}
-
-.horizontal-scroll-card .card-content .card-title .subject {
-  flex-grow: 5;
-}
-
-.horizontal-scroll-card .one-line {
-  display: grid;
-  grid-auto-flow: column;
-  grid-template-rows: 1fr 1fr;
-}
-
-p.subtitle {
-  display: flex;
-  justify-content: space-between;
-  align-items: flex-end;
-}
-
-.btn-superflat ~ span {
-  line-height: 24px;
-}
-
-.btn-superflat,
-.btn-superflat:focus,
-.btn-superflat:active {
-  border: none;
-  line-height: 1;
-  height: 24px;
-  background: none;
-  font-weight: normal;
-}
-
-.btn-superflat i.material-icons {
-  vertical-align: middle;
-}
-
-.btn-superflat:hover {
-  cursor: pointer;
-}
-
-.unfold-trigger i.material-icons {
-  transition: transform 0.5s 0s ease-in-out;
-  transform: rotate(-90deg);
-}
-
-.unfold-trigger.vertical i.material-icons {
-  transform: rotate(-180deg);
-}
-
-.tabs-icons .tab svg.iconify {
-  display: block;
-}
diff --git a/aleksis/apps/alsijil/static/js/alsijil/week_view.js b/aleksis/apps/alsijil/static/js/alsijil/week_view.js
deleted file mode 100644
index 69124b9c41e656948bbced89b06b8d4edaf3b3c2..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/static/js/alsijil/week_view.js
+++ /dev/null
@@ -1,20 +0,0 @@
-$(document).ready(function () {
-  $("#id_group").change(function () {
-    $("#id_teacher").val("").formSelect();
-  });
-  $("#id_teacher").change(function () {
-    $("#id_group").val("").formSelect();
-  });
-  $("#toggle-row.pre-hidden").hide();
-});
-$("#toggle-button").click(function () {
-  $("#toggle-row").toggle();
-});
-$(".unfold-trigger").click(function (event) {
-  let target = event.target;
-  target.classList.toggle("vertical");
-  let next_container = $(target).parent().next(".horizontal-scroll-container");
-  if (next_container.length >= 1) {
-    next_container[0].classList.toggle("vertical");
-  }
-});
diff --git a/aleksis/apps/alsijil/tables.py b/aleksis/apps/alsijil/tables.py
index f17a214930f2d7f2e40a1662b491eab587bda0ad..acc3bcbed6f8da596bec2f40dbcdf238f5046a95 100644
--- a/aleksis/apps/alsijil/tables.py
+++ b/aleksis/apps/alsijil/tables.py
@@ -1,45 +1,9 @@
 from django.template.loader import render_to_string
-from django.utils.safestring import mark_safe
 from django.utils.translation import gettext_lazy as _
 
 import django_tables2 as tables
 from django_tables2.utils import A
 
-from aleksis.apps.chronos.models import Event, LessonPeriod
-from aleksis.core.util.tables import SelectColumn
-
-from .models import PersonalNote
-
-
-class ExcuseTypeTable(tables.Table):
-    class Meta:
-        attrs = {"class": "highlight"}
-
-    name = tables.LinkColumn("edit_excuse_type", args=[A("id")])
-    short_name = tables.Column()
-    count_as_absent = tables.BooleanColumn(
-        verbose_name=_("Count as absent"),
-        accessor="count_as_absent",
-    )
-    edit = tables.LinkColumn(
-        "edit_excuse_type",
-        args=[A("id")],
-        text=_("Edit"),
-        attrs={"a": {"class": "btn-flat waves-effect waves-orange orange-text"}},
-    )
-    delete = tables.LinkColumn(
-        "delete_excuse_type",
-        args=[A("id")],
-        text=_("Delete"),
-        attrs={"a": {"class": "btn-flat waves-effect waves-red red-text"}},
-    )
-
-    def before_render(self, request):
-        if not request.user.has_perm("alsijil.edit_excusetype_rule"):
-            self.columns.hide("edit")
-        if not request.user.has_perm("alsijil.delete_excusetype_rule"):
-            self.columns.hide("delete")
-
 
 class GroupRoleTable(tables.Table):
     class Meta:
@@ -68,137 +32,3 @@ class GroupRoleTable(tables.Table):
             self.columns.hide("edit")
         if not request.user.has_perm("alsijil.delete_grouprole_rule"):
             self.columns.hide("delete")
-
-
-class PersonalNoteTable(tables.Table):
-    selected = SelectColumn(attrs={"input": {"name": "selected_objects"}}, accessor=A("pk"))
-    date = tables.Column(
-        verbose_name=_("Date"), accessor=A("date_formatted"), order_by=A("day_start"), linkify=True
-    )
-    period = tables.Column(
-        verbose_name=_("Period"),
-        accessor=A("period_formatted"),
-        order_by=A("order_period"),
-        linkify=True,
-    )
-    groups = tables.Column(
-        verbose_name=_("Groups"),
-        accessor=A("register_object__group_names"),
-        order_by=A("order_groups"),
-        linkify=True,
-    )
-    teachers = tables.Column(
-        verbose_name=_("Teachers"),
-        accessor=A("register_object__teacher_names"),
-        order_by=A("order_teachers"),
-        linkify=True,
-    )
-    subject = tables.Column(verbose_name=_("Subject"), accessor=A("subject"), linkify=True)
-    absent = tables.Column(verbose_name=_("Absent"))
-    tardiness = tables.Column(verbose_name=_("Tardiness"))
-    excused = tables.Column(verbose_name=_("Excuse"))
-    extra_marks = tables.Column(verbose_name=_("Extra marks"), accessor=A("extra_marks__all"))
-
-    def render_groups(self, value, record):
-        if isinstance(record.register_object, LessonPeriod):
-            return record.register_object.lesson.group_names
-        else:
-            return value
-
-    def render_subject(self, value, record):
-        if isinstance(record.register_object, Event):
-            return _("Event")
-        else:
-            return value
-
-    def render_absent(self, value):
-        return (
-            render_to_string(
-                "components/materialize-chips.html",
-                dict(content=_("Absent"), classes="red white-text"),
-            )
-            if value
-            else "–"
-        )
-
-    def render_excused(self, value, record):
-        if record.absent and value:
-            context = dict(content=_("Excused"), classes="green white-text")
-            badge = render_to_string("components/materialize-chips.html", context)
-            if record.excuse_type:
-                context = dict(content=record.excuse_type.name, classes="green white-text")
-                badge = render_to_string("components/materialize-chips.html", context)
-            return badge
-        return "–"
-
-    def render_tardiness(self, value):
-        if value:
-            content = _(f"{value}' tardiness")
-            context = dict(content=content, classes="orange white-text")
-            return render_to_string("components/materialize-chips.html", context)
-        else:
-            return "–"
-
-    def render_extra_marks(self, value):
-        if value:
-            badges = ""
-            for extra_mark in value:
-                content = extra_mark.name
-                badges += render_to_string(
-                    "components/materialize-chips.html", context=dict(content=content)
-                )
-            return mark_safe(badges)  # noqa
-        else:
-            return "–"
-
-    class Meta:
-        model = PersonalNote
-        fields = ()
-
-
-def _get_link(value, record):
-    return record["register_object"].get_alsijil_url(record.get("week"))
-
-
-class RegisterObjectTable(tables.Table):
-    """Table to show all register objects in an overview.
-
-    .. warning::
-        Works only with ``generate_list_of_all_register_objects``.
-    """
-
-    class Meta:
-        attrs = {"class": "highlight responsive-table"}
-
-    status = tables.Column(accessor="register_object")
-    date = tables.Column(order_by="date_sort", linkify=_get_link)
-    period = tables.Column(order_by="period_sort", linkify=_get_link)
-    groups = tables.Column(linkify=_get_link)
-    teachers = tables.Column(linkify=_get_link)
-    subject = tables.Column(linkify=_get_link)
-    topic = tables.Column(linkify=_get_link)
-    homework = tables.Column(linkify=_get_link)
-    group_note = tables.Column(linkify=_get_link)
-
-    def render_status(self, value, record):
-        context = {
-            "has_documentation": record.get("has_documentation", False),
-            "register_object": value,
-        }
-        if record.get("week"):
-            context["week"] = record["week"]
-        if record.get("substitution"):
-            context["substitution"] = record["substitution"]
-        return render_to_string("alsijil/partials/lesson_status.html", context)
-
-
-class RegisterObjectSelectTable(RegisterObjectTable):
-    """Table to show all register objects with multi-select support.
-
-    More information at ``RegisterObjectTable``
-    """
-
-    selected = SelectColumn()
-
-    class Meta(RegisterObjectTable.Meta):
-        sequence = ("selected", "...")
diff --git a/aleksis/apps/alsijil/tasks.py b/aleksis/apps/alsijil/tasks.py
index 7eaf8c01a5186ef08c12a5e4b19e9fc1e853d4d2..015f4905dfd4dcf37f4c5949c65e212d3b316dbb 100644
--- a/aleksis/apps/alsijil/tasks.py
+++ b/aleksis/apps/alsijil/tasks.py
@@ -1,187 +1,189 @@
-from copy import deepcopy
-from datetime import date, timedelta
+# from copy import deepcopy
+# from datetime import date, timedelta
 
-from django.db.models import Q
-from django.utils.translation import gettext as _
+# from django.db.models import Q
+# from django.utils.translation import gettext as _
 
-from calendarweek import CalendarWeek
-from celery.result import allow_join_result
-from celery.states import SUCCESS
+# from calendarweek import CalendarWeek
+# from celery.result import allow_join_result
+# from celery.states import SUCCESS
 
-from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
-from aleksis.core.models import Group, PDFFile
+# from aleksis.core.models import Group, PDFFile
 from aleksis.core.util.celery_progress import ProgressRecorder, recorded_task
-from aleksis.core.util.pdf import generate_pdf_from_template
 
-from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
+# from aleksis.core.util.pdf import generate_pdf_from_template
+
+# from .models import ExtraMark
 
 
 @recorded_task
 def generate_full_register_printout(group: int, file_object: int, recorder: ProgressRecorder):
     """Generate a full register printout as PDF for a group."""
-    context = {}
-
-    _number_of_steps = 8
-
-    recorder.set_progress(1, _number_of_steps, _("Load data ..."))
-
-    group = Group.objects.get(pk=group)
-    file_object = PDFFile.objects.get(pk=file_object)
-
-    groups_q = (
-        Q(lesson_period__lesson__groups=group)
-        | Q(lesson_period__lesson__groups__parent_groups=group)
-        | Q(extra_lesson__groups=group)
-        | Q(extra_lesson__groups__parent_groups=group)
-        | Q(event__groups=group)
-        | Q(event__groups__parent_groups=group)
-    )
-    personal_notes = (
-        PersonalNote.objects.prefetch_related(
-            "lesson_period__substitutions", "lesson_period__lesson__teachers"
-        )
-        .not_empty()
-        .filter(groups_q)
-        .filter(groups_of_person=group)
-    )
-    documentations = LessonDocumentation.objects.not_empty().filter(groups_q)
-
-    recorder.set_progress(2, _number_of_steps, _("Sort data ..."))
-
-    sorted_documentations = {"extra_lesson": {}, "event": {}, "lesson_period": {}}
-    sorted_personal_notes = {"extra_lesson": {}, "event": {}, "lesson_period": {}, "person": {}}
-    for documentation in documentations:
-        key = documentation.register_object.label_
-        sorted_documentations[key][documentation.register_object_key] = documentation
-
-    for note in personal_notes:
-        key = note.register_object.label_
-        sorted_personal_notes[key].setdefault(note.register_object_key, [])
-        sorted_personal_notes[key][note.register_object_key].append(note)
-        sorted_personal_notes["person"].setdefault(note.person.pk, [])
-        sorted_personal_notes["person"][note.person.pk].append(note)
-
-    recorder.set_progress(3, _number_of_steps, _("Load lesson data ..."))
-
-    # Get all lesson periods for the selected group
-    lesson_periods = LessonPeriod.objects.filter_group(group).distinct()
-    events = Event.objects.filter_group(group).distinct()
-    extra_lessons = ExtraLesson.objects.filter_group(group).distinct()
-    weeks = CalendarWeek.weeks_within(group.school_term.date_start, group.school_term.date_end)
-
-    register_objects_by_day = {}
-    for extra_lesson in extra_lessons:
-        day = extra_lesson.date
-        register_objects_by_day.setdefault(day, []).append(
-            (
-                extra_lesson,
-                sorted_documentations["extra_lesson"].get(extra_lesson.pk),
-                sorted_personal_notes["extra_lesson"].get(extra_lesson.pk, []),
-                None,
-            )
-        )
-
-    for event in events:
-        day_number = (event.date_end - event.date_start).days + 1
-        for i in range(day_number):
-            day = event.date_start + timedelta(days=i)
-            event_copy = deepcopy(event)
-            event_copy.annotate_day(day)
-
-            # Skip event days if it isn't inside the timetable schema
-            if not (event_copy.raw_period_from_on_day and event_copy.raw_period_to_on_day):
-                continue
-
-            register_objects_by_day.setdefault(day, []).append(
-                (
-                    event_copy,
-                    sorted_documentations["event"].get(event.pk),
-                    sorted_personal_notes["event"].get(event.pk, []),
-                    None,
-                )
-            )
-
-    recorder.set_progress(4, _number_of_steps, _("Sort lesson data ..."))
-
-    weeks = CalendarWeek.weeks_within(
-        group.school_term.date_start,
-        group.school_term.date_end,
-    )
-
-    for lesson_period in lesson_periods:
-        for week in weeks:
-            day = week[lesson_period.period.weekday]
-
-            if (
-                lesson_period.lesson.validity.date_start
-                <= day
-                <= lesson_period.lesson.validity.date_end
-            ):
-                filtered_documentation = sorted_documentations["lesson_period"].get(
-                    f"{lesson_period.pk}_{week.week}_{week.year}"
-                )
-                filtered_personal_notes = sorted_personal_notes["lesson_period"].get(
-                    f"{lesson_period.pk}_{week.week}_{week.year}", []
-                )
-
-                substitution = lesson_period.get_substitution(week)
-
-                register_objects_by_day.setdefault(day, []).append(
-                    (lesson_period, filtered_documentation, filtered_personal_notes, substitution)
-                )
-
-    recorder.set_progress(5, _number_of_steps, _("Load statistics ..."))
-
-    persons = group.members.prefetch_related(None).select_related(None)
-    persons = group.generate_person_list_with_class_register_statistics(persons)
-
-    prefetched_persons = []
-    for person in persons:
-        person.filtered_notes = sorted_personal_notes["person"].get(person.pk, [])
-        prefetched_persons.append(person)
-
-    context["school_term"] = group.school_term
-    context["persons"] = prefetched_persons
-    context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
-    context["excuse_types_not_absent"] = ExcuseType.objects.filter(count_as_absent=False)
-    context["extra_marks"] = ExtraMark.objects.all()
-    context["group"] = group
-    context["weeks"] = weeks
-    context["register_objects_by_day"] = register_objects_by_day
-    context["register_objects"] = list(lesson_periods) + list(events) + list(extra_lessons)
-    context["today"] = date.today()
-    context["lessons"] = (
-        group.lessons.all()
-        .select_related(None)
-        .prefetch_related(None)
-        .select_related("validity", "subject")
-        .prefetch_related("teachers", "lesson_periods")
-    )
-    context["child_groups"] = (
-        group.child_groups.all()
-        .select_related(None)
-        .prefetch_related(None)
-        .prefetch_related(
-            "lessons",
-            "lessons__validity",
-            "lessons__subject",
-            "lessons__teachers",
-            "lessons__lesson_periods",
-        )
-    )
-
-    recorder.set_progress(6, _number_of_steps, _("Generate template ..."))
-
-    file_object, result = generate_pdf_from_template(
-        "alsijil/print/full_register.html", context, file_object=file_object
-    )
-
-    recorder.set_progress(7, _number_of_steps, _("Generate PDF ..."))
-
-    with allow_join_result():
-        result.wait()
-        file_object.refresh_from_db()
-        if not result.status == SUCCESS and file_object.file:
-            raise Exception(_("PDF generation failed"))
-
-    recorder.set_progress(8, _number_of_steps)
+
+
+#     context = {}
+
+#     _number_of_steps = 8
+
+#     recorder.set_progress(1, _number_of_steps, _("Load data ..."))
+
+#     group = Group.objects.get(pk=group)
+#     file_object = PDFFile.objects.get(pk=file_object)
+
+#     groups_q = (
+#         Q(lesson_period__lesson__groups=group)
+#         | Q(lesson_period__lesson__groups__parent_groups=group)
+#         | Q(extra_lesson__groups=group)
+#         | Q(extra_lesson__groups__parent_groups=group)
+#         | Q(event__groups=group)
+#         | Q(event__groups__parent_groups=group)
+#     )
+#     personal_notes = (
+#         PersonalNote.objects.prefetch_related(
+#             "lesson_period__substitutions", "lesson_period__lesson__teachers"
+#         )
+#         .not_empty()
+#         .filter(groups_q)
+#         .filter(groups_of_person=group)
+#     )
+#     documentations = LessonDocumentation.objects.not_empty().filter(groups_q)
+
+#     recorder.set_progress(2, _number_of_steps, _("Sort data ..."))
+
+#     sorted_documentations = {"extra_lesson": {}, "event": {}, "lesson_period": {}}
+#     sorted_personal_notes = {"extra_lesson": {}, "event": {}, "lesson_period": {}, "person": {}}
+#     for documentation in documentations:
+#         key = documentation.register_object.label_
+#         sorted_documentations[key][documentation.register_object_key] = documentation
+
+#     for note in personal_notes:
+#         key = note.register_object.label_
+#         sorted_personal_notes[key].setdefault(note.register_object_key, [])
+#         sorted_personal_notes[key][note.register_object_key].append(note)
+#         sorted_personal_notes["person"].setdefault(note.person.pk, [])
+#         sorted_personal_notes["person"][note.person.pk].append(note)
+
+#     recorder.set_progress(3, _number_of_steps, _("Load lesson data ..."))
+
+#     # Get all lesson periods for the selected group
+#     lesson_periods = LessonPeriod.objects.filter_group(group).distinct()
+#     events = Event.objects.filter_group(group).distinct()
+#     extra_lessons = ExtraLesson.objects.filter_group(group).distinct()
+#     weeks = CalendarWeek.weeks_within(group.school_term.date_start, group.school_term.date_end)
+
+#     register_objects_by_day = {}
+#     for extra_lesson in extra_lessons:
+#         day = extra_lesson.date
+#         register_objects_by_day.setdefault(day, []).append(
+#             (
+#                 extra_lesson,
+#                 sorted_documentations["extra_lesson"].get(extra_lesson.pk),
+#                 sorted_personal_notes["extra_lesson"].get(extra_lesson.pk, []),
+#                 None,
+#             )
+#         )
+
+#     for event in events:
+#         day_number = (event.date_end - event.date_start).days + 1
+#         for i in range(day_number):
+#             day = event.date_start + timedelta(days=i)
+#             event_copy = deepcopy(event)
+#             event_copy.annotate_day(day)
+
+#             # Skip event days if it isn't inside the timetable schema
+#             if not (event_copy.raw_period_from_on_day and event_copy.raw_period_to_on_day):
+#                 continue
+
+#             register_objects_by_day.setdefault(day, []).append(
+#                 (
+#                     event_copy,
+#                     sorted_documentations["event"].get(event.pk),
+#                     sorted_personal_notes["event"].get(event.pk, []),
+#                     None,
+#                 )
+#             )
+
+#     recorder.set_progress(4, _number_of_steps, _("Sort lesson data ..."))
+
+#     weeks = CalendarWeek.weeks_within(
+#         group.school_term.date_start,
+#         group.school_term.date_end,
+#     )
+
+#     for lesson_period in lesson_periods:
+#         for week in weeks:
+#             day = week[lesson_period.period.weekday]
+
+#             if (
+#                 lesson_period.lesson.validity.date_start
+#                 <= day
+#                 <= lesson_period.lesson.validity.date_end
+#             ):
+#                 filtered_documentation = sorted_documentations["lesson_period"].get(
+#                     f"{lesson_period.pk}_{week.week}_{week.year}"
+#                 )
+#                 filtered_personal_notes = sorted_personal_notes["lesson_period"].get(
+#                     f"{lesson_period.pk}_{week.week}_{week.year}", []
+#                 )
+
+#                 substitution = lesson_period.get_substitution(week)
+
+#                 register_objects_by_day.setdefault(day, []).append(
+#                     (lesson_period, filtered_documentation, filtered_personal_notes, substitution)
+#                 )
+
+#     recorder.set_progress(5, _number_of_steps, _("Load statistics ..."))
+
+#     persons = group.members.prefetch_related(None).select_related(None)
+#     persons = group.generate_person_list_with_class_register_statistics(persons)
+
+#     prefetched_persons = []
+#     for person in persons:
+#         person.filtered_notes = sorted_personal_notes["person"].get(person.pk, [])
+#         prefetched_persons.append(person)
+
+#     context["school_term"] = group.school_term
+#     context["persons"] = prefetched_persons
+#     context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
+#     context["excuse_types_not_absent"] = ExcuseType.objects.filter(count_as_absent=False)
+#     context["extra_marks"] = ExtraMark.objects.all()
+#     context["group"] = group
+#     context["weeks"] = weeks
+#     context["register_objects_by_day"] = register_objects_by_day
+#     context["register_objects"] = list(lesson_periods) + list(events) + list(extra_lessons)
+#     context["today"] = date.today()
+#     context["lessons"] = (
+#         group.lessons.all()
+#         .select_related(None)
+#         .prefetch_related(None)
+#         .select_related("validity", "subject")
+#         .prefetch_related("teachers", "lesson_periods")
+#     )
+#     context["child_groups"] = (
+#         group.child_groups.all()
+#         .select_related(None)
+#         .prefetch_related(None)
+#         .prefetch_related(
+#             "lessons",
+#             "lessons__validity",
+#             "lessons__subject",
+#             "lessons__teachers",
+#             "lessons__lesson_periods",
+#         )
+#     )
+
+#     recorder.set_progress(6, _number_of_steps, _("Generate template ..."))
+
+#     file_object, result = generate_pdf_from_template(
+#         "alsijil/print/full_register.html", context, file_object=file_object
+#     )
+
+#     recorder.set_progress(7, _number_of_steps, _("Generate PDF ..."))
+
+#     with allow_join_result():
+#         result.wait()
+#         file_object.refresh_from_db()
+#         if not result.status == SUCCESS and file_object.file:
+#             raise Exception(_("PDF generation failed"))
+
+#     recorder.set_progress(8, _number_of_steps)
diff --git a/aleksis/apps/alsijil/templates/alsijil/absences/register.html b/aleksis/apps/alsijil/templates/alsijil/absences/register.html
deleted file mode 100644
index 590ad1e1f61d5ed09210101012aadaae9dda6f25..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/absences/register.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load material_form i18n static any_js %}
-
-{% block browser_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %}
-
-{% block extra_head %}
-  {{ form.media.css }}
-  {% include_css "select2-materialize" %}
-{% endblock %}
-
-{% block content %}
-  <form method="post">
-    {% csrf_token %}
-    {% form form=register_absence_form %}{% endform %}
-    {% include "core/partials/save_button.html" %}
-  </form>
-
-  <script>
-    $(document).ready(function () {
-      $("#id_date_start").change(function () {
-        $("#id_date_end").val($("#id_date_start").val());
-        initDatePicker("#id_date_end");
-      });
-    });
-  </script>
-
-  {% include_js "select2-materialize" %}
-  {{ form.media.js }}
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html b/aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html
deleted file mode 100644
index 2c427ef2fa701a4e1beb36b40fc5c1de09ca8e44..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html
+++ /dev/null
@@ -1,82 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load material_form i18n static %}
-
-{% block browser_title %}{% blocktrans %}Confirm: Register absence{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Confirm: Register absence{% endblocktrans %}{% endblock %}
-
-{% block content %}
-  <p class="flow-text">
-    {% blocktrans %}
-      Do you really want to register the following absence?
-    {% endblocktrans %}
-  </p>
-  <div class="card">
-    <div class="card-content">
-      <div class="card-title">
-        {{ person }}
-      </div>
-      <div class="collection">
-        <div class="collection-item">
-          <i class="material-icons iconify left" data-icon="mdi:calendar-range"></i>
-          {{ form_data.date_start }}, {{ form_data.from_period }}. – {{ form_data.date_end }}, {{ form_data.to_period }}.
-          {% if form_data.date_start != form_data.date_end %}
-            <figure class="alert warning">
-              <i class="material-icons iconify left" data-icon="mdi:alert-outline"></i>
-              {% blocktrans %}
-                As the length of this absence is longer than one day,
-                please double check the correctness of your entry.
-              {% endblocktrans %}
-            </figure>
-          {% endif %}
-        </div>
-        <div class="collection-item">
-          <i class="material-icons iconify left" data-icon="mdi:format-list-bulleted"></i>
-          {% blocktrans with count=affected_lessons %} {{ count }} affected lessons {% endblocktrans %}
-          {% if affected_lessons == 0 %}
-            <div class="alert error">
-              <div>
-                <i class="material-icons iconify left" data-icon="mdi:alert-octagon-outline"></i>
-                {% blocktrans %}
-                  There are no affected lessons. Registering this absence won't have any effect.
-                {% endblocktrans %}
-              </div>
-            </div>
-          {% endif %}
-        </div>
-        <div class="collection-item">
-          <i class="material-icons iconify left" data-icon="mdi:label-outline"></i>
-          {% if form_data.absent %}
-            <span class="chip red white-text">{% trans "Absent" %}</span>
-            {% if form_data.excused and form_data.excuse_type %}
-              <span class="chip green white-text">{{ form_data.excuse_type.name }}</span>
-            {% elif form_data.excused %}
-              <span class="chip green white-text">{% trans "Excused" %}</span>
-            {% endif %}
-          {% else %}
-            {% trans "Reset status to 'not absent'" %}
-          {% endif %}
-        </div>
-        {% if form_data.remarks %}
-          <div class="collection-item">
-            <i class="material-icons iconify left" data-icon="mdi:pencil-outline"></i>
-            {{ form_data.remarks }}
-          </div>
-        {% endif %}
-      </div>
-    </div>
-  </div>
-
-  <form method="post">
-    {% csrf_token %}
-    <div class="hide">
-      {% form form=form %}{% endform %}
-    </div>
-    <input type="hidden" name="confirmed" value="1">
-    {% include "core/partials/save_button.html" %}
-    <a class="btn red waves-effect waves-light" href="{% url "register_absence" person.pk %}">
-      <i class="material-icons iconify left" data-icon="mdi:close"></i>
-      {% trans "Cancel" %}
-    </a>
-  </form>
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/all_objects.html b/aleksis/apps/alsijil/templates/alsijil/class_register/all_objects.html
deleted file mode 100644
index b5e57e0ee276ab395ebd824327c7584a063dba63..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/all_objects.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load i18n rules static django_tables2 material_form %}
-
-{% block browser_title %}{% blocktrans %}All lessons{% endblocktrans %}{% endblock %}
-
-{% block page_title %}
-  {% blocktrans %}All lessons{% endblocktrans %}
-{% endblock %}
-
-{% block extra_head %}
-  {{ block.super }}
-  <link rel="stylesheet" href="{% static 'css/alsijil/alsijil.css' %}"/>
-{% endblock %}
-
-{% block content %}
-  {% include "alsijil/partials/objects_table.html" %}
-  <script src="{% static "js/multi_select.js" %}"></script>
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/groups.html b/aleksis/apps/alsijil/templates/alsijil/class_register/groups.html
deleted file mode 100644
index 43a6eeeb9349969b58c61111e6017b7d24e74f60..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/groups.html
+++ /dev/null
@@ -1,109 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load i18n static rules %}
-
-{% block browser_title %}{% blocktrans %}My groups{% endblocktrans %}{% endblock %}
-
-{% block page_title %}
-  {% blocktrans %}My groups{% endblocktrans %}
-{% endblock %}
-
-{% block extra_head %}
-  {{ block.super }}
-  <link rel="stylesheet" href="{% static 'css/alsijil/alsijil.css' %}"/>
-{% endblock %}
-
-{% block content %}
-  <table class="highlight responsive-table hide-on-med-and-down">
-    <thead>
-    <tr>
-      <th>{% trans "Name" %}</th>
-      <th>{% trans "Students" %}</th>
-      <th></th>
-    </tr>
-    </thead>
-    {% for group in groups %}
-      <tr>
-        <td>
-          {{ group }}
-        </td>
-        <td>{{ group.students_count }}</td>
-        <td>
-          <div class="right">
-            <a class="btn primary-color waves-effect waves-light" href="{% url "students_list" group.pk %}">
-              <i class="material-icons iconify left" data-icon="mdi:account-multiple-outline"></i>
-              {% trans "Students list" %}
-            </a>
-            <a class="btn secondary-color waves-effect waves-light" href="{% url "week_view" "group" group.pk %}">
-              <i class="material-icons iconify left" data-icon="mdi:view-week-outline"></i>
-              {% trans "Week view" %}
-            </a>
-            {% has_perm "alsijil.view_assigned_grouproles_rule" user group as can_view_assigned_group_roles %}
-            {% if can_view_assigned_group_roles %}
-              <a class="btn primary waves-effect waves-light" href="{% url 'assigned_group_roles' group.pk %}">
-                <i class="material-icons iconify left" data-icon="mdi:clipboard-account-outline"></i>
-                {% trans "Roles" %}
-              </a>
-            {% endif %}
-            <a class="btn primary waves-effect waves-light" href="{% url "full_register_group" group.pk %}"
-               target="_blank">
-              <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-              {% trans "Generate printout" %}
-            </a>
-          </div>
-        </td>
-      </tr>
-    {% empty %}
-      <tr>
-        <td class="flow-text" colspan="3">
-          {% blocktrans %}No groups available.{% endblocktrans %}
-        </td>
-      </tr>
-    {% endfor %}
-  </table>
-
-  <div class="hide-on-large-only">
-    <ul class="collection">
-      {% for group in groups %}
-        <li class="collection-item">
-          <span class="title">{{ group }}</span>
-          <p>
-            {{ group.students_count }} {% trans "students" %}
-          </p>
-          <p>
-            <a class="btn primary-color waves-effect waves-light" href="{% url "week_view" "group" group.pk %}">
-              <i class="material-icons iconify left" data-icon="mdi:account-multiple-outline"></i>
-              {% trans "Students list" %}
-            </a>
-          </p>
-          <p>
-            <a class="btn secondary-color waves-effect waves-light" href="{% url "week_view" "group" group.pk %}">
-              <i class="material-icons iconify left" data-icon="mdi:view-week-outline"></i>
-              {% trans "Week view" %}
-            </a>
-          </p>
-          {% has_perm "alsijil.view_assigned_grouproles_rule" user group as can_view_assigned_group_roles %}
-          {% if can_view_assigned_group_roles %}
-            <p>
-              <a class="btn primary waves-effect waves-light" href="{% url 'assigned_group_roles' group.pk %}">
-                <i class="material-icons iconify left" data-icon="mdi:clipboard-account-outline"></i>
-                {% trans "Roles" %}
-              </a>
-            </p>
-          {% endif %}
-          <p>
-            <a class="btn primary waves-effect waves-light" href="{% url "full_register_group" group.pk %}"
-               target="_blank">
-              <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-              {% trans "Generate printout" %}
-            </a>
-          </p>
-        </li>
-      {% empty %}
-          <li class="collection-item flow-text">
-            {% blocktrans %}No groups available.{% endblocktrans %}
-          </li>
-      {% endfor %}
-    </ul>
-  </div>
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html b/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html
deleted file mode 100644
index e4e3960c8a1325acdca09ddcff95d958657d0f62..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html
+++ /dev/null
@@ -1,125 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load week_helpers material_form_internal material_form i18n static rules time_helpers %}
-
-{% block browser_title %}{% blocktrans %}Lesson{% endblocktrans %}{% endblock %}
-{% block extra_head %}
-  {{ block.super }}
-  <link rel="stylesheet" href="{% static 'css/alsijil/lesson.css' %}"/>
-
-  {% if with_seating_plan %}
-    <link rel="stylesheet" href="{% static "css/stoelindeling/seating_plan.css" %}">
-  {% endif %}
-{% endblock %}
-
-{% block page_title %}
-  {% include "alsijil/partials/lesson/heading.html" %}
-{% endblock %}
-
-{% block content %}
-  {% has_perm "alsijil.view_lessondocumentation_rule" user register_object as can_view_lesson_documentation %}
-  {% has_perm "alsijil.edit_lessondocumentation_rule" user register_object as can_edit_lesson_documentation %}
-  {% has_perm "alsijil.edit_register_object_personalnote_rule" user register_object as can_edit_register_object_personalnote %}
-
-  <!-- Tab Buttons -->
-  <div class="col s12 margin-bottom">
-    <ul class="tabs tabs-icons tabs-fixed-width">
-      <li class="tab col">
-        <a href="#lesson-documentation">
-          <i class="material-icons iconify" data-icon="mdi:message-bulleted"></i>
-          {% trans "Period" %}
-        </a>
-      </li>
-      {% if register_object.label_ != "lesson_period" or not register_object.get_substitution.cancelled or not SITE_PREFERENCES.alsijil__block_personal_notes_for_cancelled %}
-        <li class="tab col">
-          <a href="#personal-notes">
-            <i class="material-icons iconify" data-icon="mdi:account-multiple-outline"></i>
-            {% trans "Persons" %}
-          </a>
-        </li>
-      {% endif %}
-      {% if with_seating_plan %}
-        <li class="tab col">
-          <a href="#seating-plan">
-            <i class="material-icons iconify" data-icon="mdi:seat-outline"></i>
-            {% trans "Seating plan" %}
-          </a>
-        </li>
-      {% endif %}
-      {% if prev_lesson %}
-        {% has_perm "alsijil.view_lessondocumentation_rule" user prev_lesson as can_view_prev_lesson_documentation %}
-        {% if prev_lesson.get_lesson_documentation and can_view_prev_lesson_documentation %}
-          <li class="tab col">
-            <a href="#previous-lesson">
-              <i class="material-icons iconify" data-icon="mdi:history"></i>
-              {% trans "Previous" %}
-            </a>
-          </li>
-        {% endif %}
-      {% endif %}
-      <li class="tab col">
-        <a href="#more">
-          <i class="material-icons iconify" data-icon="mdi:dots-horizontal"></i>
-          {% trans "More" %}
-        </a>
-      </li>
-    </ul>
-  </div>
-
-  <form method="post" class="row">
-    {% csrf_token %}
-
-    {% if not blocked_because_holidays %}
-      <div class="row">
-        <div class="col s12 no-padding" id="lesson-documentation">
-          {% include "alsijil/partials/lesson/tabs/documentation.html" %}
-        </div>
-
-        {% with prev_doc=prev_lesson.get_lesson_documentation %}
-          {% with absences=prev_lesson.get_absences tardinesses=prev_lesson.get_tardinesses extra_marks=prev_lesson.get_extra_marks %}
-            {% has_perm "alsijil.view_lessondocumentation_rule" user prev_lesson as can_view_prev_lesson_documentation %}
-            {% if prev_doc and can_view_prev_lesson_documentation %}
-              <div class="col s12 no-padding" id="previous-lesson">
-                {% include "alsijil/partials/lesson/tabs/previous_lesson.html" %}
-              </div>
-            {% endif %}
-          {% endwith %}
-        {% endwith %}
-
-        {% if register_object.label_ != "lesson_period" or not register_object.get_substitution.cancelled or not SITE_PREFERENCES.alsijil__block_personal_notes_for_cancelled %}
-          <div class="col s12 no-padding" id="personal-notes">
-            {% include "alsijil/partials/lesson/tabs/notes.html" %}
-          </div>
-        {% endif %}
-
-        {% if with_seating_plan %}
-          <div class="col s12 no-padding" id="seating-plan">
-            {% include "alsijil/partials/lesson/tabs/seating_plan.html" %}
-          </div>
-        {% endif %}
-
-        <div class="col s12 no-padding" id="more">
-          {% include "alsijil/partials/lesson/tabs/more.html" %}
-        </div>
-      </div>
-    {% else %}
-      <div class="row no-margin">
-        <div class="container">
-          <div class="card">
-            <div class="card-content center-align">
-              <p>
-                <i class="material-icons iconify medium orange-text" data-icon="mdi:alert-outline"></i>
-              </p>
-              <p class="card-title">
-                {% blocktrans %}
-                  This lesson overlaps with holidays and can't be edited.
-                {% endblocktrans %}
-              </p>
-              <span class="badge new blue no-float no-margin">{{ holiday }}</span>
-            </div>
-          </div>
-        </div>
-      </div>
-    {% endif %}
-  </form>
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/person.html b/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
deleted file mode 100644
index 78792a793cce1b93577d7bf6ca55da771a43a7df..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
+++ /dev/null
@@ -1,185 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load rules data_helpers week_helpers i18n material_form static django_tables2 %}
-
-{% block extra_head %}
-  <link rel="stylesheet" href="{% static "css/alsijil/person.css" %}">
-  <script src="{% static "js/multi_select.js" %}" type="text/javascript"></script>
-{% endblock %}
-
-{% block browser_title %}{% blocktrans %}Class register: person{% endblocktrans %}{% endblock %}
-
-
-{% block page_title %}
-  {% has_perm "alsijil.view_my_students_rule" user as has_students %}
-  {% if has_students %}
-    <a href="{% url "my_students" %}"
-       class="btn-flat primary-color-text waves-light waves-effect">
-      <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Back" %}
-    </a>
-  {% endif %}
-  <span id="heading">
-    {% blocktrans with person=person %}
-      Class register overview for {{ person }}
-    {% endblocktrans %}
-  </span>
-  {% has_perm "alsijil.register_absence_rule" user person as can_register_absence %}
-  {% if can_register_absence %}
-    <a class="btn primary-color waves-effect waves-light right" href="{% url "register_absence" person.pk %}">
-      <i class="material-icons iconify left" data-icon="mdi:message-draw"></i>
-      {% trans "Register absence" %}
-    </a>
-  {% endif %}
-{% endblock %}
-
-{% block content %}
-  <div class="row">
-
-  <!-- Tab Buttons -->
-  <div class="col s12">
-    <ul class="tabs">
-      {% if register_object_table %}
-        <li class="tab">
-          <a href="#lesson-documentations">{% trans "Lesson documentations" %}</a>
-        </li>
-      {% endif %}
-      <li class="tab">
-        <a href="#personal-notes">{% trans "Personal notes" %}</a>
-      </li>
-      {% if stats %}
-        <li class="tab"><a href="#statistics">{% trans "Statistics" %}</a></li>
-      {% endif %}
-    </ul>
-  </div>
-
-  <!-- Lesson Documentation Tab -->
-  {% if register_object_table %}
-    <div class="col s12" id="lesson-documentations">
-      {% include "alsijil/partials/objects_table.html" with table=register_object_table filter_form=filter_form %}
-    </div>
-  {% endif %}
-
-  <!-- Personal Note Tab -->
-  <div class="col s12" id="personal-notes">
-    <div class="col s12" id="overview">
-      <h2>{% trans "Relevant personal notes" %}</h2>
-      <form class="modal" id="filter-modal">
-        <figure class="modal-content">
-          <figcaption>{% trans "Filter personal notes" %}</figcaption>
-          {% form form=personal_note_filter_form %}{% endform %}
-        </figure>
-        <div class="modal-footer">
-          <button type="button" class="btn-flat secondary-color-text waves-effect waves-ripple" id="remove-filters">
-            <i class="material-icons iconify left" data-icon="mdi:close"></i>{% trans "Clear all filters" %}
-          </button>
-          <button type="button" class="modal-close btn-flat red-text waves-effect waves-ripple waves-red">
-            <i class="material-icons iconify left" data-icon="mdi:close-circle-outline"></i>{% trans "Close" %}
-          </button>
-          <button type="submit" class="modal-close btn-flat primary-color-text waves-effect waves-ripple waves-light">
-            <i class="material-icons iconify left" data-icon="mdi:filter-outline"></i>{% trans "Filter" %}
-          </button>
-        </div>
-      </form>
-      {% has_perm "alsijil.edit_person_overview_personalnote_rule" user person as can_mark_all_as_excused %}
-      <div class="row">
-        <div class="col s12 m3 l5 push-m9 push-l7">
-          <button
-              class="modal-trigger btn primary-color waves-effect waves-light
-              {% if can_mark_all_as_excused %} medium-high-right {% endif %}"
-              data-target="filter-modal"
-              type="button">
-            {% trans "Filter results" %} ({{ num_filters }})
-            <i class="material-icons iconify right" data-icon="mdi:filter-outline"></i>
-          </button>
-        </div>
-        <form action="" method="post" class="">
-          {% csrf_token %}
-          <div class="col s12 m9 l7 pull-m3 pull-l5 row">
-            {% if can_mark_all_as_excused %}
-              <div class="col s12 m9">
-                {% form form=action_form %}{% endform %}
-              </div>
-              <div class="col s12 m3">
-                <button type="submit" class="btn waves-effect waves-light medium-high full-width-s">
-                  Run <i class="material-icons iconify right" data-icon="mdi:send-outline"></i>
-                </button>
-              </div>
-            {% endif %}
-          </div>
-          <div class="col s12 overflow-x-scroll">
-            {% render_table personal_notes_table %}
-          </div>
-        </form>
-      </div>
-    </div>
-  </div>
-
-  <!-- Statistics Tab -->
-  {% if stats %}
-    <div class="col s12" id="statistics">
-      <h2>{% trans "Statistics on absences, tardiness and remarks" %}</h2>
-      <ul class="collapsible">
-        {% for school_term, stat in stats %}
-          <li {% if forloop.first %}class="active"{% endif %}>
-            <div class="collapsible-header">
-              <i class="material-icons iconify" data-icon="mdi:calendar-range"></i>{{ school_term }}</div>
-            <div class="collapsible-body">
-              <table>
-                <tr>
-                  <th colspan="3">{% trans 'Absences' %}</th>
-                  <td>{{ stat.absences_count }}</td>
-                </tr>
-                <tr>
-                  <td rowspan="{{ excuse_types.count|add:3 }}" class="hide-on-small-only">{% trans "thereof" %}</td>
-                  <td rowspan="{{ excuse_types.count|add:3 }}" class="hide-on-med-and-up"></td>
-                  <th colspan="2">{% trans 'Excused' %}</th>
-                  <td>{{ stat.excused }}</td>
-                </tr>
-                <tr>
-                  <td rowspan="{{ excuse_types.count|add:1 }}" class="hide-on-small-only">{% trans "thereof" %}</td>
-                  <td rowspan="{{ excuse_types.count|add:1 }}" class="hide-on-med-and-up"></td>
-                  <th colspan="2" class="truncate">{% trans 'Without Excuse Type' %}</th>
-                  <td>{{ stat.excused_no_excuse_type }}</td>
-                </tr>
-                {% for excuse_type in excuse_types %}
-                  <tr>
-                    <th>{{ excuse_type.name }}</th>
-                    <td>{{ stat|get_dict:excuse_type.count_label }}</td>
-                  </tr>
-                {% endfor %}
-                <tr>
-                  <th colspan="2">{% trans 'Unexcused' %}</th>
-                  <td>{{ stat.unexcused }}</td>
-                </tr>
-                {% for excuse_type in excuse_types_not_absent %}
-                  <tr>
-                    <th colspan="3">{{ excuse_type.name }}</th>
-                    <td>{{ stat|get_dict:excuse_type.count_label }}</td>
-                  </tr>
-                  {% endfor %}
-                <tr>
-                  <th colspan="3">{% trans 'Tardiness' %}</th>
-                  <td>{{ stat.tardiness }}'/{{ stat.tardiness_count }} &times;</td>
-                </tr>
-                {% for extra_mark in extra_marks %}
-                  <tr>
-                    <th colspan="3">{{ extra_mark.name }}</th>
-                    <td>{{ stat|get_dict:extra_mark.count_label }}</td>
-                  </tr>
-                {% endfor %}
-              </table>
-            </div>
-          </li>
-        {% endfor %}
-      </ul>
-    </div>
-  {% endif %}
-  <script type="text/javascript">
-    $("#remove-filters").click(function () {
-      $("#filter-modal").trigger("reset");
-      $("#filter-modal input, #filter-modal select").each(function () {
-        $(this).val("");
-      })
-    })
-  </script>
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/persons.html b/aleksis/apps/alsijil/templates/alsijil/class_register/persons.html
deleted file mode 100644
index 6873ddc84a1cdbad62ff43b09d1d50ece163a01a..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/persons.html
+++ /dev/null
@@ -1,64 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load i18n week_helpers data_helpers static time_helpers %}
-
-{% block browser_title %}{% blocktrans %}My students{% endblocktrans %}{% endblock %}
-
-
-{% block page_title %}
-  {% blocktrans %}My students{% endblocktrans %}
-{% endblock %}
-
-{% block extra_head %}
-  {{ block.super }}
-  <link rel="stylesheet" href="{% static 'css/alsijil/alsijil.css' %}"/>
-{% endblock %}
-
-
-{% block content %}
-  <ul class="collapsible">
-    {% for group, persons in groups %}
-      <li {% if forloop.first %}class="active"{% endif %}>
-        <div class="collapsible-header">
-          <div class="hundred-percent">
-            <span class="right show-on-active hide-on-small-and-down">
-              <a class="btn primary-color waves-effect waves-light" href="{% url "week_view" "group" group.pk %}">
-                <i class="material-icons iconify left" data-icon="mdi:view-week-outline"></i>
-                {% trans "Week view" %}
-              </a>
-              <a class="btn waves-effect waves-light" href="{% url "full_register_group" group.pk %}" target="_blank">
-                <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-                {% trans "Generate printout" %}
-              </a>
-            </span>
-
-            <h2>{{ group.name }}
-              <span class="chip">{{ group.school_term }}</span>
-            </h2>
-
-            <p class="show-on-active hide-on-med-and-up">
-              <a class="btn primary-color waves-effect waves-light hundred-percent"
-                 href="{% url "week_view" "group" group.pk %}">
-                <i class="material-icons iconify left" data-icon="mdi:view-week-outline"></i>
-                {% trans "Week view" %}
-              </a>
-            </p>
-            <p class="show-on-active hide-on-med-and-up">
-              <a class="btn waves-effect waves-light hundred-percent" href="{% url "full_register_group" group.pk %}"
-                 target="_blank">
-                <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-                {% trans "Generate printout" %}
-              </a>
-            </p>
-          </div>
-        </div>
-
-        <div class="collapsible-body">
-          {% include "alsijil/partials/persons_with_stats.html" with persons=persons %}
-        </div>
-      </li>
-    {% endfor %}
-  </ul>
-
-  {% include "alsijil/partials/legend.html" %}
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/students_list.html b/aleksis/apps/alsijil/templates/alsijil/class_register/students_list.html
deleted file mode 100644
index 72bb8071f1b05f7e911bf75776a1740a9a5d5ae9..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/students_list.html
+++ /dev/null
@@ -1,49 +0,0 @@
-{# -*- engine:django -*- #}
-{% extends "core/base.html" %}
-{% load static time_helpers data_helpers week_helpers i18n %}
-
-{% block browser_title %}{% blocktrans with group=group %}Students list: {{ group }}{% endblocktrans %}{% endblock %}
-
-{% block page_title %}
-  <a href="{% url "my_groups" %}"
-     class="btn-flat primary-color-text waves-light waves-effect">
-    <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Back" %}
-  </a>
-  {% blocktrans with group=group %}Students list: {{ group }}{% endblocktrans %}
-  <span class="right show-on-active hide-on-small-and-down">
-    <a class="btn primary-color waves-effect waves-light" href="{% url "week_view" "group" group.pk %}">
-      <i class="material-icons iconify left" data-icon="mdi:view-week-outline"></i>
-      {% trans "Week view" %}
-    </a>
-    <a class="btn waves-effect waves-light" href="{% url "full_register_group" group.pk %}" target="_blank">
-      <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-      {% trans "Generate printout" %}
-    </a>
-  </span>
-{% endblock %}
-
-{% block extra_head %}
-  {{ block.super }}
-  <link rel="stylesheet" href="{% static 'css/alsijil/alsijil.css' %}"/>
-{% endblock %}
-
-{% block content %}
-  <p class="show-on-active hide-on-med-and-up">
-    <a class="btn primary-color waves-effect waves-light hundred-percent"
-       href="{% url "week_view" "group" group.pk %}">
-       <i class="material-icons iconify left" data-icon="mdi:view-week-outline"></i>
-      {% trans "Week view" %}
-    </a>
-  </p>
-  <p class="show-on-active hide-on-med-and-up">
-    <a class="btn waves-effect waves-light hundred-percent" href="{% url "full_register_group" group.pk %}"
-       target="_blank">
-      <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-      {% trans "Generate printout" %}
-    </a>
-  </p>
-
-  {% include "alsijil/partials/persons_with_stats.html" with persons=persons %}
-
-  {% include "alsijil/partials/legend.html" %}
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html b/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html
deleted file mode 100644
index 081f38c5321359ca92241c21ba1f9b1fc71aa926..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html
+++ /dev/null
@@ -1,443 +0,0 @@
-{# -*- engine:django -*- #}
-
-{% extends "core/base.html" %}
-{% load material_form i18n week_helpers static data_helpers rules time_helpers %}
-
-{% block browser_title %}{% blocktrans %}Week view{% endblocktrans %}{% endblock %}
-
-{% block extra_head %}
-  {{ block.super }}
-  <link rel="stylesheet" href="{% static 'css/alsijil/alsijil.css' %}"/>
-  <link rel="stylesheet" href="{% static 'css/alsijil/week_view.css' %}"/>
-{% endblock %}
-
-{% block content %}
-  <script type="text/javascript" src="{% static "js/helper.js" %}"></script>
-  {{ week_select|json_script:"week_select" }}
-  <script type="text/javascript" src="{% static "js/chronos/week_select.js" %}"></script>
-  <div class="row">
-    <div id="toggle-row" class="col s12 m8 l10 {% if lesson_periods %}pre-hidden{% endif %}">
-      <form method="post" action="">
-        {% csrf_token %}
-        {% form form=select_form %}{% endform %}
-        <button type="submit" class="btn waves-effect waves-light primary-color">
-          <i class="material-icons iconify left" data-icon="mdi:check"></i>
-          {% blocktrans %}Select{% endblocktrans %}
-        </button>
-      </form>
-    </div>
-    <div class="col s12 m4 l2 right">
-      <button type="button" class="btn waves-effect waves-light hundred-percent" id="toggle-button">
-        <i class="material-icons iconify left" data-icon="mdi:filter-outline"></i> {% trans "Toggle filters" %}
-      </button>
-    </div>
-  </div>
-
-
-  <div class="row no-margin">
-    <h4 class="col s12 m6">{% blocktrans with el=el week=week.week %}CW {{ week }}:
-      {{ instance }}{% endblocktrans %} </h4>
-    {% include "chronos/partials/week_select.html" with wanted_week=week %}
-  </div>
-
-  {% if group %}
-    <p class="hide-on-med-and-down">
-      <a class="btn primary-color waves-effect waves-light" href="{% url "students_list" group.pk %}">
-        <i class="material-icons iconify left" data-icon="mdi:account-multiple-outline"></i>
-        {% trans "Students list" %}
-      </a>
-      <a class="btn waves-effect waves-light" href="{% url "full_register_group" group.pk %}" target="_blank">
-        <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-        {% trans "Generate printout" %}
-      </a>
-    </p>
-
-    <p class="hide-on-med-and-up">
-      <a class="btn primary-color waves-effect waves-light hundred-percent"
-         href="{% url "students_list" group.pk %}">
-        <i class="material-icons iconify left" data-icon="mdi:account-multiple-outline"></i>
-        {% trans "Students list" %}
-      </a>
-    </p>
-    <p class="hide-on-med-and-up">
-      <a class="btn waves-effect waves-light hundred-percent" href="{% url "full_register_group" group.pk %}"
-         target="_blank">
-        <i class="material-icons iconify left" data-icon="mdi:printer-outline"></i>
-        {% trans "Generate printout" %}
-      </a>
-    </p>
-  {% endif %}
-
-  {% if lesson_periods %}
-  <div class="col s12 margin-bottom">
-    <ul class="tabs tabs-icons tabs-fixed-width">
-      <li class="tab col">
-        <a class="active" href="#week-overview">
-          <i class="material-icons iconify" data-icon="mdi:message-bulleted"></i>
-          {% trans "Lesson documentations" %}
-        </a>
-      </li>
-      <li class="tab col">
-        <a href="#personal-notes">
-          <i class="material-icons iconify" data-icon="mdi:account-multiple-outline"></i>
-          {% trans "Persons" %}
-        </a>
-      </li>
-      {% if group_roles %}
-        <li class="tab col">
-          <a href="#group-roles">
-            <i class="material-icons iconify" data-icon="mdi:clipboard-account-outline"></i>
-            {% trans "Group roles" %}
-          </a>
-        </li>
-      {% endif %}
-    </ul>
-  </div>
-  {% endif %}
-
-  {% if lesson_periods %}
-    <div class="row">
-      <div class="col s12" id="week-overview">
-        {% for weekday, objects in regrouped_objects.items %}
-          {% with weekdays|get_dict:objects.0.weekday as advanced_weekday %}
-            {% if advanced_weekday.holiday and not SITE_PREFERENCES.alsijil__allow_entries_in_holidays %}
-              <div class="card">
-                <div class="card-content">
-                    <span class="card-title">
-                      {{ advanced_weekday.name }}, {{ advanced_weekday.date }} <span
-                      class="badge new blue no-float">{{ advanced_weekday.holiday }}</span>
-                    </span>
-                </div>
-              </div>
-            {% else %}
-              <div class="card show-on-extra-large">
-                <div class="card-content">
-                    <span class="card-title">
-                      {{ advanced_weekday.name }}, {{ advanced_weekday.date }}
-                    </span>
-                  <table class="striped datatable">
-                    <thead>
-                    <tr>
-                      <th></th>
-                      <th>{% blocktrans %}Period{% endblocktrans %}</th>
-                      {% if not group %}
-                        <th>{% blocktrans %}Groups{% endblocktrans %}</th>
-                      {% endif %}
-                      <th>{% blocktrans %}Subject{% endblocktrans %}</th>
-                      <th>{% blocktrans %}Teachers{% endblocktrans %}</th>
-                      <th>{% blocktrans %}Lesson topic{% endblocktrans %}</th>
-                      <th>{% blocktrans %}Homework{% endblocktrans %}</th>
-                      <th>{% blocktrans %}Group note{% endblocktrans %}</th>
-                    </tr>
-                    </thead>
-                    <tbody>
-                    {% for register_object in objects %}
-                      {% has_perm "alsijil.view_lessondocumentation_rule" user register_object as can_view_lesson_documentation %}
-                      {% if can_view_lesson_documentation %}
-                        <tr>
-                          <td class="center-align">
-                            {% include "alsijil/partials/lesson_status.html" with register_object=register_object %}
-                          </td>
-                          <td class="tr-link">
-                            <a class="tr-link"
-                               href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                              {% if register_object.period %}
-                                {{ register_object.period.period }}.
-                              {% else %}
-                                {{ register_object.period_from_on_day }}.–
-                                {{ register_object.period_to_on_day }}.
-                              {% endif %}
-                            </a>
-                          </td>
-                          {% if not group %}
-                            <td>
-                              <a class="tr-link"
-                                 href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                                {% if register_object.lesson %}
-                                  {{ register_object.lesson.group_names }}
-                                {% else %}
-                                  {{ register_object.group_names }}
-                                {% endif %}
-                              </a>
-                            </td>
-                          {% endif %}
-                          <td>
-                            <a class="tr-link"
-                               href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                              {% if register_object.get_subject %}
-                                {{ register_object.get_subject.name }}
-                              {% elif register_object.subject %}
-                                {{ register_object.subject }}
-                              {% else %}
-                                {% trans "Event" %} ({{ register_object.title }})
-                              {% endif %}
-                            </a>
-                          </td>
-                          <td>
-                            <a class="tr-link"
-                               href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                              {{ register_object.teacher_names }}
-                            </a>
-                          </td>
-                          <td>
-                            <a class="tr-link"
-                               href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                              {% firstof register_object.get_lesson_documentation.topic "–" %}
-                            </a>
-                          </td>
-                          <td>
-                            <a class="tr-link"
-                               href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                              {% firstof register_object.get_lesson_documentation.homework "–" %}
-                            </a>
-                          </td>
-                          <td>
-                            <a class="tr-link"
-                               href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                              {% firstof register_object.get_lesson_documentation.group_note "–" %}
-                            </a>
-                          </td>
-                        </tr>
-                      {% endif %}
-                    {% endfor %}
-                    </tbody>
-                  </table>
-                </div>
-              </div>
-              <ul class="collapsible hide-on-extra-large-only hide-on-small-only">
-                <li class="">
-                  <div class="collapsible-header flow-text">
-                    {{ advanced_weekday.name }}, {{ advanced_weekday.date }} <i
-                    class="material-icons iconify collapsible-icon-right" data-icon="mdi:unfold-more-horizontal"></i>
-                  </div>
-                  <div class="collapsible-body">
-                    <div class="collection">
-                      {% for register_object in objects %}
-                        {% has_perm "alsijil.view_lessondocumentation_rule" user register_object as can_view_lesson_documentation %}
-                        {% if can_view_lesson_documentation %}
-                          <a class="collection-item avatar"
-                             href="{{ register_object.alsijil_url }}?back={{ back_url }}">
-                            {% include "alsijil/partials/lesson_status.html" with register_object=register_object css_class="materialize-circle" color_suffix=" " %}
-                            <table>
-                              <tr>
-                                <th>{% trans "Subject" %}</th>
-                                <td>
-                                  {% if register_object.period %}
-                                    {{ register_object.period.period }}.
-                                  {% else %}
-                                    {{ register_object.period_from_on_day }}.–
-                                    {{ register_object.period_to_on_day }}.
-                                  {% endif %}
-                                  {% if register_object.get_subject %}
-                                    {{ register_object.get_subject.name }}
-                                  {% elif register_object.subject %}
-                                    {{ register_object.subject }}
-                                  {% else %}
-                                    {% trans "Event" %}
-                                  {% endif %}
-                                </td>
-                              </tr>
-                              {% if not group %}
-                                <tr>
-                                  <th>{% trans "Groups" %}</th>
-                                  <td>
-                                    {% if register_object.lesson %}
-                                      {{ register_object.lesson.group_names }}
-                                    {% else %}
-                                      {{ register_object.group_names }}
-                                    {% endif %}
-                                  </td>
-                                </tr>
-                              {% endif %}
-                              <tr>
-                                <th>{% trans "Teachers" %}</th>
-                                <td>
-                                  {{ register_object.teacher_names }}
-                                </td>
-                              </tr>
-                              <tr>
-                                <th>{% trans "Lesson topic" %}</th>
-                                <td>{% firstof register_object.get_lesson_documentation.topic "–" %}</td>
-                              </tr>
-                              {% with register_object.get_lesson_documentation as lesson_documentation %}
-                                {% if lesson_documentation.homework %}
-                                  <tr>
-                                    <th>{% trans "Homework" %}</th>
-                                    <td>{% firstof register_object.get_lesson_documentation.homework "–" %}</td>
-                                  </tr>
-                                {% endif %}
-                                {% if lesson_documentation.group_note %}
-                                  <tr>
-                                    <th>{% trans "Group note" %}</th>
-                                    <td>{% firstof register_object.get_lesson_documentation.group_note "–" %}</td>
-                                  </tr>
-                                {% endif %}
-                              {% endwith %}
-                            </table>
-                          </a>
-                        {% endif %}
-                      {% endfor %}
-                    </div>
-                  </div>
-                </li>
-              </ul>
-              <div class="hide-on-med-and-up">
-                <h3>{{ advanced_weekday.name }}</h3>
-                <p class="subtitle">
-                  <span>{{ advanced_weekday.date }}</span>
-                  <button class="btn-superflat right waves-effect unfold-trigger">
-                    {% trans "Unfold" %}
-                    <i class="material-icons iconify" data-icon="mdi:unfold-less-horizontal"></i>
-                  </button>
-                </p>
-                <div class="horizontal-scroll-container">
-                  {% for register_object in objects %}
-                    <div class="card horizontal-scroll-card">
-                      <div class="card-content">
-                        <span class="card-title">
-                          <span class="period">
-                            {% if register_object.period %}
-                              {{ register_object.period.period }}.
-                            {% else %}
-                              {{ register_object.period_from_on_day }}.–{{ register_object.period_to_on_day }}.
-                            {% endif %}
-                          </span>
-                          <span class="subject">
-                            {% if register_object.get_subject %}
-                              {{ register_object.get_subject.name }}
-                            {% elif register_object.subject %}
-                              {{ register_object.subject }}
-                            {% else %}
-                              {% trans "Event" %}
-                            {% endif %}
-                          </span>
-                          <span class="lesson-icon">
-                            {% include "alsijil/partials/lesson_status.html" with register_object=register_object %}
-                          </span>
-                        </span>
-                        <dl>
-                          <div class="one-line">
-                            {% if not group %}
-                              <dt>{% trans "Groups" %}</dt>
-                              <dd>
-                                {% if register_object.lesson %}
-                                  {{ register_object.lesson.group_names }}
-                                {% else %}
-                                  {{ register_object.group_names }}
-                                {% endif %}
-                              </dd>
-                            {% endif %}
-
-                            <dt>{% trans "Teachers" %}</dt>
-                            <dd>
-                              {{ register_object.teacher_names }}
-                            </dd>
-                          </div>
-
-                          <dt>{% trans "Lesson topic" %}</dt>
-                          <dd>{% firstof register_object.get_lesson_documentation.topic "–" %}</dd>
-
-                          {% with register_object.get_lesson_documentation as lesson_documentation %}
-                            {% if lesson_documentation.homework %}
-                              <dt>{% trans "Homework" %}</dt>
-                              <dd>{% firstof register_object.get_lesson_documentation.homework "–" %}</dd>
-                            {% endif %}
-                            {% if lesson_documentation.group_note %}
-                              <dt>{% trans "Group note" %}</dt>
-                              <dd>{% firstof register_object.get_lesson_documentation.group_note "–" %}</dd>
-                            {% endif %}
-                          {% endwith %}
-                        </dl>
-                      </div>
-                      <div class="card-action">
-                        <a href="{{ register_object.alsijil_url }}?back={{ back_url }}"
-                           class="">
-                          {% trans "Visit lesson overview" %}
-                        </a>
-                      </div>
-                    </div>
-                  {% endfor %}
-                </div>
-              </div>
-            {% endif %}
-          {% endwith %}
-        {% endfor %}
-      </div>
-      <div class="col s12" id="personal-notes">
-        <div class="card">
-          <div class="card-content">
-              <span class="card-title">
-                {% blocktrans %}Personal notes{% endblocktrans %}
-              </span>
-            {% for person in persons %}
-              <h5 class="card-title">
-                <a href="{% url "overview_person" person.person.pk %}">{{ person.person.full_name }}</a>
-                {% has_perm "alsijil.register_absence_rule" user person.person as can_register_absence %}
-                {% if can_register_absence %}
-                  <a class="btn primary-color waves-effect waves-light right"
-                     href="{% url "register_absence" person.person.pk %}">
-                    <i class="material-icons iconify left" data-icon="mdi:message-draw"></i>
-                    {% trans "Register absence" %}
-                  </a>
-                {% endif %}
-              </h5>
-              {% if group_roles %}
-                <p>
-                  {% for assignment in person.group_roles %}
-                    {% include "alsijil/group_role/chip.html" with role=assignment.role small=assignment.date_range %}
-                  {% endfor %}
-                </p>
-              {% endif %}
-              <p class="card-text">
-                {% trans "Absent" %}: {{ person.person.absences_count }}
-                ({{ person.person.unexcused_count }} {% trans "unexcused" %})
-              </p>
-              <p class="card-text">
-                {% trans "Summed up tardiness" %}: {% firstof person.person.tardiness_sum|to_time|time:"H\h i\m" "–" %}
-              </p>
-              <p class="card-text">
-                {% trans "Count of tardiness" %}: {{ person.person.tardiness_count }} &times;
-              </p>
-              {% for extra_mark in extra_marks %}
-                <p class="card-text">
-                  {{ extra_mark.name }}: {{ person.person|get_dict:extra_mark.count_label }}
-                </p>
-              {% endfor %}
-              {% for note in person.personal_notes %}
-                <blockquote>
-                  {{ note.remarks }}
-                  <em class="right">
-                    <a href="{{ note.register_object.alsijil_url }}">
-                      {{ note.date_formatted }}, {{ note.register_object.get_subject.name }}
-                    </a>
-                  </em>
-                </blockquote>
-              {% endfor %}
-            {% endfor %}
-          </div>
-        </div>
-      </div>
-      {% if group_roles %}
-        <div class="col s12" id="group-roles">
-          {% include "alsijil/group_role/partials/assigned_roles.html" with roles=group_roles group=group back_url=back_url %}
-        </div>
-      {% endif %}
-    </div>
-  {% else %}
-    <div class="card">
-      <div class="card-content">
-        <span class="card-title">
-          <i class="material-icons iconify red-text left" data-icon="mdi:alert-outline"></i>
-          {% blocktrans %}No lessons available{% endblocktrans %}
-        </span>
-        <p>
-          {% blocktrans %}
-            There are no lessons for the selected group or teacher in this week.
-          {% endblocktrans %}
-        </p>
-      </div>
-    </div>
-  {% endif %}
-
-  <script src="{% static 'js/alsijil/week_view.js' %}" type="text/javascript"></script>
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/excuse_type/create.html b/aleksis/apps/alsijil/templates/alsijil/excuse_type/create.html
deleted file mode 100644
index 6fc6faefb2543cecf32b02e7dcb7ea2a40f3d73b..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/excuse_type/create.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{# -*- engine:django -*- #}
-
-{% extends "core/base.html" %}
-{% load material_form i18n %}
-
-{% block browser_title %}{% blocktrans %}Create excuse type{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Create excuse type{% endblocktrans %}{% endblock %}
-
-{% block content %}
-  {% include "alsijil/excuse_type/warning.html" %}
-
-  <form method="post">
-    {% csrf_token %}
-    {% form form=form %}{% endform %}
-    {% include "core/partials/save_button.html" %}
-  </form>
-
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/excuse_type/edit.html b/aleksis/apps/alsijil/templates/alsijil/excuse_type/edit.html
deleted file mode 100644
index 78396ed66264cc19abdac2085d1cc89ff931bb38..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/excuse_type/edit.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{# -*- engine:django -*- #}
-
-{% extends "core/base.html" %}
-{% load material_form i18n %}
-
-{% block browser_title %}{% blocktrans %}Edit excuse type{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Edit excuse type{% endblocktrans %}{% endblock %}
-
-{% block content %}
-
-  <form method="post">
-    {% csrf_token %}
-    {% form form=form %}{% endform %}
-    {% include "core/partials/save_button.html" %}
-  </form>
-
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html b/aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html
deleted file mode 100644
index e6235a32f382aa9cf80ae3d8ad0ad1704e25e2e9..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{# -*- engine:django -*- #}
-
-{% extends "core/base.html" %}
-
-{% load i18n rules %}
-{% load render_table from django_tables2 %}
-
-{% block browser_title %}{% blocktrans %}Excuse types{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Excuse types{% endblocktrans %}{% endblock %}
-
-{% block content %}
-  {% include "alsijil/excuse_type/warning.html" %}
-
-  {% has_perm "alsijil.add_excusetype_rule" user as add_excusetype %}
-  {% if add_excusetype %}
-    <a class="btn green waves-effect waves-light" href="{% url 'create_excuse_type' %}">
-      <i class="material-icons iconify left"data-icon="mdi:plus"></i>
-      {% trans "Create excuse type" %}
-    </a>
-  {% endif %}
-
-  {% render_table table %}
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/excuse_type/warning.html b/aleksis/apps/alsijil/templates/alsijil/excuse_type/warning.html
deleted file mode 100644
index 811b90b33381c578469552154092333b2b38624d..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/excuse_type/warning.html
+++ /dev/null
@@ -1,8 +0,0 @@
-{% load i18n %}
-<figure class="alert warning">
-  <i class="material-icons iconify left" data-icon="mdi:alert-outline"></i>
-  {% blocktrans %}
-    This function should only be used to define alternatives to the default excuse which also will be counted extra.
-    Don't use this to create a default excuse or if you don't divide between different types of excuse.
-  {% endblocktrans %}
-</figure>
diff --git a/aleksis/apps/alsijil/templates/alsijil/notifications/check.html b/aleksis/apps/alsijil/templates/alsijil/notifications/check.html
deleted file mode 100644
index d76a1a0a5abfc6fb8b7722d5a4cf16ff927f069a..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/notifications/check.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}{% trans "Please check if the following class register entries are complete and correct:" %}
-{% for entry in items %}
-- {{ entry.register_object }} ({{ entry.date }})
-{% endfor %}
\ No newline at end of file
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/heading.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/heading.html
deleted file mode 100644
index 132e97f05acd0216d59f89a12cab08b96e123cc5..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/heading.html
+++ /dev/null
@@ -1,110 +0,0 @@
-{% load i18n %}
-
-{% if next_lesson_person or prev_lesson_person or back_to_week_url %}
-  <div class="row margin-bottom alsijil-nav-header">
-    <div class="col s12 no-padding">
-      {# Back to week view #}
-      {% if back_to_week_url %}
-        <a href="{{ back_to_week_url }}"
-           class="btn secondary-color waves-light waves-effect margin-bottom {% if prev_lesson_person or next_lesson_person %}hide-on-extra-large-only{% endif %}">
-          <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Week view" %}
-        </a>
-      {% endif %}
-
-      {% if prev_lesson_person or next_lesson_person %}
-        <div class="col s12 no-padding center alsijil-nav">
-          {% if back_to_week_url %}
-            <a href="{{ back_to_week_url }}"
-               class="btn-flat secondary-color-text waves-light waves-effect left hide-on-med-and-down hide-on-large-only show-on-extra-large">
-              <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Week view" %}
-            </a>
-          {% endif %}
-
-          {# Previous lesson #}
-          <a class="btn-flat waves-effect waves-light left primary-color-text {% if not prev_lesson_person %}disabled{% endif %}"
-             title="{% trans "My previous lesson" %}"
-              {% if prev_lesson_person %}
-             href="{% url "lesson_period" prev_lesson_person.week.year prev_lesson_person.week.week prev_lesson_person.id %}"
-              {% endif %}
-          >
-            <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i>
-            <span class="hide-on-small-only">{% trans "My previous lesson" %}</span>
-            <span class="hide-on-med-and-up">{% trans "Previous" %}</span>
-          </a>
-          {# Next lesson #}
-          <a class="btn-flat waves-effect waves-light right primary-color-text {% if not next_lesson_person %}disabled{% endif %}"
-             title="{% trans "My next lesson" %}"
-              {% if next_lesson_person %}
-             href="{% url "lesson_period" next_lesson_person.week.year next_lesson_person.week.week next_lesson_person.id %}"
-              {% endif %}
-          >
-            <i class="material-icons iconify right" data-icon="mdi:chevron-right"></i>
-            <span class="hide-on-small-only">{% trans "My next lesson" %}</span>
-            <span class="hide-on-med-and-up">{% trans "Next" %}</span>
-          </a>
-          <span class="truncate">{{ request.user.person }}</span>
-        </div>
-      {% endif %}
-    </div>
-  </div>
-{% endif %}
-
-<h1>
-  <span class="right hide-on-small-only">
-    {% include "alsijil/partials/lesson_status.html" with register_object=register_object css_class="medium" %}
-  </span>
-
-  <a class="btn-flat waves-effect waves-light primary-color-text left alsijil-header-nav-button hide-on-med-and-up {% if not prev_lesson %}disabled{% endif %}"
-      {% if prev_lesson %}
-     href="{% url "lesson_period" prev_lesson.week.year prev_lesson.week.week prev_lesson.id %}"
-      {% endif %}
-  >
-    <i class="material-icons iconify center" data-icon="mdi:chevron-left"></i>
-  </a>
-  <a class="btn-flat waves-effect waves-light primary-color-text right alsijil-header-nav-button hide-on-med-and-up {% if not next_lesson %}disabled{% endif %}"
-      {% if next_lesson %}
-     href="{% url "lesson_period" next_lesson.week.year next_lesson.week.week next_lesson.id %}"
-      {% endif %}
-  >
-    <i class="material-icons iconify center" data-icon="mdi:chevron-right"></i>
-  </a>
-
-  <span class="alsijil-time-head">
-    {% if register_object.label_ == "event" %}
-      {% if register_object.date_start == register_object.date_end %}
-        {% if register_object.period_from.period == register_object.period_to.period %}
-          {{ register_object.date_start|date:"SHORT_DATE_FORMAT" }},
-          {% blocktrans with period=register_object.period_from.period %}{{ period }}. period{% endblocktrans %}
-        {% else %}
-          {{ register_object.date_start|date:"SHORT_DATE_FORMAT" }},
-          {% blocktrans with period_from=register_object.period_from.period  period_to=register_object.period_to.period %}
-            {{ period_from }}.–{{ period_to }}.  period
-          {% endblocktrans %}
-        {% endif %}
-      {% else %}
-        {{ register_object.date_start|date:"SHORT_DATE_FORMAT" }},
-        {{ register_object.period_from.period }}.–{{ register_object.date_end|date:"SHORT_DATE_FORMAT" }},
-        {{ register_object.period_to.period }}.
-      {% endif %}
-    {% else %}
-      {{ day|date:"SHORT_DATE_FORMAT" }},
-      {% blocktrans with period=register_object.period.period %}{{ period }}. period{% endblocktrans %}
-    {% endif %}
-  </span>
-
-  <span class="alsijil-object-head">
-    {{ register_object.group_names }},
-
-    {% if register_object.label_ == "event" %}
-      {% trans "Event" %} ({{ register_object.title }}),
-    {% else %}
-      {{ register_object.get_subject.short_name }},
-    {% endif %}
-
-    {{ register_object.teacher_short_names }}
-  </span>
-</h1>
-
-<div class="hide-on-med-and-up margin-bottom">
-  {% include "alsijil/partials/lesson_status.html" with register_object=register_object chip=1 css_class="hundred-percent center" %}
-</div>
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/prev_next.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/prev_next.html
deleted file mode 100644
index a12cf71e843ada4d1caeec54a82c8ca1876cb7ec..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/prev_next.html
+++ /dev/null
@@ -1,38 +0,0 @@
-{% load i18n %}
-
-<div class="row no-margin hide-on-small-only">
-  <div class="col s12 no-padding">
-    {% if not blocked_because_holidays and with_save %}
-      {% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
-        <button type="submit" class="btn waves-effect waves-light green margin-bottom">
-          <i class="material-icons iconify left" data-icon="mdi:content-save-outline"></i>
-          {% trans "Save" %}
-        </button>
-      {% endif %}
-    {% endif %}
-
-    <a class="btn waves-effect waves-light primary margin-bottom {% if not prev_lesson %}disabled{% endif %}"
-        {% if prev_lesson %}
-       href="{% url "lesson_period" prev_lesson.week.year prev_lesson.week.week prev_lesson.id %}"
-        {% endif %}
-    >
-      <i class="material-icons iconify left" data-icon="mdi:arrow-left"></i>
-      {% blocktrans with subject=register_object.get_subject.short_name %}
-        Previous {{ subject }} lesson
-      {% endblocktrans %}
-    </a>
-
-    <a class="btn right waves-effect waves-light primary margin-bottom {% if not next_lesson %}disabled{% endif %}"
-        {% if next_lesson %}
-       href="{% url "lesson_period" next_lesson.week.year next_lesson.week.week next_lesson.id %}"
-        {% endif %}
-    >
-      <i class="material-icons iconify right" data-icon="mdi:arrow-right"></i>
-      {% blocktrans with subject=register_object.get_subject.short_name %}
-        Next {{ subject }} lesson
-      {% endblocktrans %}
-    </a>
-  </div>
-</div>
-
-
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html
deleted file mode 100644
index 22b396f457a13d4d04824836745fd49fdf7eac72..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html
+++ /dev/null
@@ -1,65 +0,0 @@
-{% load i18n material_form_internal material_form %}
-
-{% include "alsijil/partials/lesson/prev_next.html" with with_save=0 %}
-
-<div class="hide-on-med-and-up margin-bottom">
-  {% if not blocked_because_holidays %}
-    {% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
-      {% include "core/partials/save_button.html" %}
-    {% endif %}
-  {% endif %}
-</div>
-
-<div class="card">
-  <div class="card-content">
-    <span class="card-title">
-      {% blocktrans %}Lesson documentation{% endblocktrans %}
-    </span>
-
-    {% if can_edit_lesson_documentation %}
-      {% form form=lesson_documentation_form %}{% endform %}
-    {% elif can_view_lesson_documentation %}
-      <table>
-        <tr>
-          <th>
-            {% trans "Lesson topic" %}
-          </th>
-          <td>
-            {{ lesson_documentation.topic }}
-          </td>
-        </tr>
-        <tr>
-          <th>
-            {% trans "Homework" %}
-          </th>
-          <td>
-            {{ lesson_documentation.homework }}
-          </td>
-        </tr>
-        <tr>
-          <th>
-            {% trans "Group note" %}
-          </th>
-          <td>
-            {{ lesson_documentation.group_note }}
-          </td>
-        </tr>
-      </table>
-    {% endif %}
-  </div>
-  <div class="card-action-light hide-on-small-only">
-    {% if not blocked_because_holidays %}
-      {% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
-        {% include "core/partials/save_button.html" %}
-      {% endif %}
-    {% endif %}
-  </div>
-</div>
-
-<div class="hide-on-med-and-up">
-  {% if not blocked_because_holidays %}
-    {% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
-      {% include "core/partials/save_button.html" %}
-    {% endif %}
-  {% endif %}
-</div>
\ No newline at end of file
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/more.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/more.html
deleted file mode 100644
index ffc7706488757871e43de4e71da9e73802273a3f..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/more.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% load i18n %}
-
-{% if group_roles %}
-  {% include "alsijil/group_role/partials/assigned_roles.html" with roles=group_roles group=register_object.get_groups.first back_url=back_url %}
-{% endif %}
-
-{% if can_view_lesson_documentation %}
-  <div class="card">
-    <div class="card-content">
-      <span class="card-title">
-        {% blocktrans %}Change history{% endblocktrans %}
-      </span>
-      {% include 'core/partials/crud_events.html' with obj=lesson_documentation %}
-    </div>
-  </div>
-{% endif %}
\ No newline at end of file
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html
deleted file mode 100644
index 1b013252a8f316979cdf5bdcc87ef1c7463b6e36..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html
+++ /dev/null
@@ -1,142 +0,0 @@
-{% load i18n material_form_internal material_form time_helpers %}
-
-{% include "alsijil/partials/lesson/prev_next.html" with with_save=1 %}
-
-{% if not blocked_because_holidays %}
-  {% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
-    <button type="submit"
-            class="btn waves-effect waves-light green margin-bottom hundred-percent hide-on-med-and-up">
-      <i class="material-icons iconify left" data-icon="mdi:content-save-outline"></i> {% trans "Save" %}
-    </button>
-  {% endif %}
-{% endif %}
-
-{% if can_edit_register_object_personalnote %}
-  {% form form=personal_note_formset.management_form %}{% endform %}
-{% endif %}
-
-<div class="card no-mobile-card">
-  <div class="card-content">
-    <span class="card-title">
-      {% blocktrans %}Personal notes{% endblocktrans %}
-    </span>
-
-    <table class="striped alsijil-table horizontal-on-small">
-      <thead>
-      <tr>
-        <th>{% blocktrans %}Person{% endblocktrans %}</th>
-        <th>{% blocktrans %}Absent{% endblocktrans %}</th>
-        <th>{% blocktrans %}Tardiness{% endblocktrans %}</th>
-        <th>{% blocktrans %}Excused{% endblocktrans %}</th>
-        <th>{% blocktrans %}Excuse type{% endblocktrans %}</th>
-        <th>{% blocktrans %}Extra marks{% endblocktrans %}</th>
-        <th>{% blocktrans %}Remarks{% endblocktrans %}</th>
-      </tr>
-      </thead>
-      <tbody>
-      {% for form in personal_note_formset %}
-        {% if can_edit_register_object_personalnote %}
-          <tr>
-            {{ form.id }}
-            <td class="person-name">{{ form.person_name }}{{ form.person_name.value }}
-              <p>
-                {% for assignment in form.instance.person.group_roles.all %}
-                  {% include "alsijil/group_role/chip.html" with role=assignment.role %}
-                {% endfor %}
-              </p>
-            </td>
-            <td class="center-align">
-              <label>
-                {{ form.absent }}
-                <span><span class="hide-on-large-only">{{ form.absent.label }}</span></span>
-              </label>
-            </td>
-            <td>
-              <div class="input-field">
-                {{ form.tardiness }}
-                <label for="{{ form.absent.id_for_label }}">
-                  {% trans "Tardiness (in m)" %}
-                </label>
-              </div>
-            </td>
-            <td class="center-align">
-              <label>
-                {{ form.excused }}
-                <span><span class="hide-on-large-only">{{ form.excused.label }}</span></span>
-              </label>
-            </td>
-            <td>
-              <div class="input-field">
-                {{ form.excuse_type }}
-                <label for="{{ form.excuse_type.id_for_label }}">
-                  {% trans "Excuse type" %}
-                </label>
-              </div>
-            </td>
-            <td>
-              {% for group, items in form.extra_marks|select_options %}
-                {% for choice, value, selected in items %}
-                  <label class="{% if selected %} active{% endif %} alsijil-check-box">
-                    <input type="checkbox"
-                           {% if value == None or value == '' %}disabled{% else %}value="{{ value }}"{% endif %}
-                        {% if selected %} checked="checked"{% endif %}
-                           name="{{ form.extra_marks.html_name }}">
-                    <span>{{ choice }}</span>
-                  </label>
-                {% endfor %}
-              {% endfor %}
-            </td>
-            <td>
-              <div class="input-field">
-                {{ form.remarks }}
-                <label for="{{ form.remarks.id_for_label }}">
-                  {% trans "Remarks" %}
-                </label>
-              </div>
-            </td>
-          </tr>
-        {% else %}
-          <tr>
-            <td>{{ form.person_name.value }}
-              <p>
-                {% for assignment in form.instance.person.group_roles.all %}
-                  {% include "alsijil/group_role/chip.html" with role=assignment.role %}
-                {% endfor %}
-              </p>
-            </td>
-            <td>
-              <i class="material-icons iconify center" data-icon="mdi:{{ form.absent.value|yesno:"check,close" }}"></i>
-            </td>
-            <td>
-              <i class="material-icons iconify center" data-icon="mdi:{{ form.tardiness.value|yesno:"check,close" }}"></i>
-              <span class="alsijil-tardiness-text">
-                {% if form.tardiness.value %}{{ form.tardiness.value|to_time|time:"i\m" }}{% endif %}
-              </span>
-            </td>
-            <td>
-              <i class="material-icons iconify center" data-icon="mdi:{{ form.excused.value|yesno:"check,close" }}"></i>
-              </td>
-            <td>{% firstof form.instance.excuse_type "–" %}</td>
-            <td>
-              {% for extra_mark in form.instance.extra_marks.all %}
-                {{ extra_mark }}{% if not forloop.last %},{% endif %}
-              {% empty %}
-                –
-              {% endfor %}
-            </td>
-            <td>{% firstof form.remarks.value "–" %}</td>
-          </tr>
-        {% endif %}
-      {% endfor %}
-      </tbody>
-    </table>
-  </div>
-</div>
-{% if not blocked_because_holidays %}
-  {% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
-    <button type="submit"
-            class="btn waves-effect waves-light green margin-bottom hundred-percent hide-on-med-and-up">
-      <i class="material-icons iconify left" data-icon="mdi:content-save-outline"></i> {% trans "Save" %}
-    </button>
-  {% endif %}
-{% endif %}
\ No newline at end of file
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/previous_lesson.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/previous_lesson.html
deleted file mode 100644
index 8457576b786f579032d5c02052c3ea02a940b6ce..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/previous_lesson.html
+++ /dev/null
@@ -1,62 +0,0 @@
-{% load i18n rules %}
-
-<div class="card">
-  <div class="card-content">
-    <span class="card-title">
-      {% blocktrans %}Overview: Previous lesson{% endblocktrans %} ({{ prev_doc.date_formatted }},
-      {% blocktrans with period=prev_lesson.period.period %}{{ period }}. period{% endblocktrans %})
-    </span>
-
-    <table>
-      {% if prev_doc.topic %}
-        <tr>
-          <th class="collection-item">{% trans "Lesson topic of previous lesson:" %}</th>
-          <td>{{ prev_doc.topic }}</td>
-        </tr>
-      {% endif %}
-
-      {% if prev_doc.homework %}
-        <tr>
-          <th class="collection-item">{% trans "Homework for this lesson:" %}</th>
-          <td>{{ prev_doc.homework }}</td>
-        </tr>
-      {% endif %}
-
-      {% if prev_doc.group_note %}
-        <tr>
-          <th class="collection-item">{% trans "Group notes for previous lesson:" %}</th>
-          <td>{{ prev_doc.group_note }}</td>
-        </tr>
-      {% endif %}
-
-      {% if absences %}
-        <tr>
-          <th>{% trans "Absent persons:" %}</th>
-          <td>{% include "alsijil/partials/absences.html" with notes=absences %}</td>
-        </tr>
-      {% endif %}
-
-      {% if tardinesses %}
-        <tr>
-          <th>{% trans "Late persons:" %}</th>
-          <td>{% include "alsijil/partials/tardinesses.html" with notes=tardinesses %}</td>
-        </tr>
-      {% endif %}
-
-      {% for extra_mark, notes in extra_marks.items %}
-        <tr>
-          <th>{{ extra_mark.name }}</th>
-          <td>
-            {% for note in notes %}
-              {% has_perm "alsijil.view_personalnote_rule" user note as can_view_personalnote %}
-              {% if can_view_personalnote %}
-                <span>{{ note.person }}{% if not forloop.last %},{% endif %}</span>
-              {% endif %}
-            {% endfor %}
-          </td>
-        </tr>
-      {% endfor %}
-
-    </table>
-  </div>
-</div>
\ No newline at end of file
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html
deleted file mode 100644
index de5e9f44f947ac49a951d30b6de58a02e844f044..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html
+++ /dev/null
@@ -1,89 +0,0 @@
-{% load i18n material_form_internal material_form time_helpers rules %}
-
-
-{% if seating_plan %}
-  <div class="card no-mobile-card">
-    <div class="card-content">
-      <div class="card-title margin-bottom">
-        {% blocktrans with group=seating_plan.group room=seating_plan.room %}Seating plan for {{ group }} in
-          {{ room }}{% endblocktrans %}
-      </div>
-      {% if seating_plan_parent %}
-        <figure class="alert primary">
-          <i class="material-icons iconify left" data-icon="information-outline"></i>
-          {% blocktrans with child_group=first_group %}
-            This seating plan is taken from the parent group of {{ child_group }}.
-            If you want, you can take it over for your group and then customize it.
-          {% endblocktrans %}
-        </figure>
-      {% endif %}
-
-      <div class="row margin-bottom no-padding">
-        <div class="col s12 no-padding">
-          {% has_perm "stoelindeling.edit_seatingplan_rule" user seating_plan as can_edit %}
-          {% has_perm "stoelindeling.copy_seatingplan_for_group_rule" user first_group as can_copy %}
-
-          {% if can_edit %}
-            <a class="btn orange waves-effect waves-light"
-               href="{% url "edit_seating_plan" seating_plan.pk %}?next={{ back_url }}#seating-plan">
-              <i class="material-icons iconify left" data-icon="mdi:pencil-outline"></i>
-              {% trans "Edit seating plan" %}
-            </a>
-          {% endif %}
-          {% if can_copy and seating_plan_parent %}
-            <a class="btn orange waves-effect waves-light"
-               href="{% url "copy_seating_plan" seating_plan.pk %}?next={{ back_url }}#seating-plan">
-              <i class="material-icons iconify left" data-icon="mdi:content-copy"></i>
-              {% trans "Copy plan and edit" %}
-            </a>
-          {% endif %}
-        </div>
-      </div>
-
-      <div class="row">
-        <div class="col s12">
-          {% include "stoelindeling/seating_plan/render.html" %}
-        </div>
-      </div>
-    </div>
-  </div>
-{% else %}
-  <div class="container">
-    <div class="card">
-      <div class="card-content">
-        <div class="card-title">
-          <i class="material-icons iconify left small orange-text" data-icon="mdi:alert-outline"></i>
-          {% trans "There is no seating plan for this lesson." %}
-        </div>
-        {% has_perm "stoelindeling.create_seatingplan_rule" user first_group as can_add %}
-        {% if can_add %}
-          <div class="row margin-bottom">
-            <div class="col s12">
-              <a class="btn waves-effect waves-light" href="{% url "create_seating_plan" %}?group={{ first_group.pk }}&subject={{ register_object.get_subject.pk }}&room={{ register_object.get_room.pk }}&next={{ back_url }}#seating-plan">
-                <i class="material-icons iconify left" data-icon="mdi:plus"></i>
-                {% blocktrans with group=first_group.name subject=register_object.get_subject.name room=register_object.get_room.name %}
-                  Create a new seating plan for {{ group }} ({{ subject }}) in {{ room }}
-                {% endblocktrans %}
-              </a>
-            </div>
-          </div>
-        {% endif %}
-        {% for parent_group in first_group.parent_groups.all %}
-          {% has_perm "stoelindeling.create_seatingplan_rule" user parent_group as can_add %}
-          {% if can_add %}
-            <div class="row">
-              <div class="col s12">
-                <a class="btn waves-effect waves-light" href="{% url "create_seating_plan" %}?group={{ parent_group.pk }}&subject={{ register_object.get_subject.pk }}&room={{ register_object.get_room.pk }}&next={{ back_url }}#seating-plan">
-                  <i class="material-icons iconify left" data-icon="mdi:plus"></i>
-                  {% blocktrans with group=parent_group.name room=register_object.get_room.name %}
-                    Create a new seating plan for {{ group }} in {{ room }}
-                  {% endblocktrans %}
-                </a>
-              </div>
-            </div>
-          {% endif %}
-        {% endfor %}
-      </div>
-    </div>
-  </div>
-{% endif %}
diff --git a/aleksis/apps/alsijil/tests/test_actions.py b/aleksis/apps/alsijil/tests/test_actions.py
deleted file mode 100644
index 8dd499ac953583637a3ade36c835aa0e5d6aae24..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/tests/test_actions.py
+++ /dev/null
@@ -1,96 +0,0 @@
-from datetime import date, time
-
-import pytest
-
-from aleksis.apps.alsijil.actions import (
-    delete_personal_note,
-    mark_as_excuse_type_generator,
-    mark_as_excused,
-    mark_as_unexcused,
-)
-from aleksis.apps.alsijil.models import ExcuseType, PersonalNote
-from aleksis.apps.chronos.models import Event, TimePeriod
-from aleksis.core.models import Person
-
-pytestmark = pytest.mark.django_db
-
-
-def _generate_event(day: date):
-    period_from = TimePeriod.objects.create(
-        weekday=0, period=1, time_start=time(10, 00), time_end=time(11, 00)
-    )
-    period_to = TimePeriod.objects.create(
-        weekday=0, period=2, time_start=time(11, 00), time_end=time(12, 00)
-    )
-
-    event = Event.objects.create(
-        date_start=day, date_end=day, period_from=period_from, period_to=period_to
-    )
-    return event
-
-
-def _prepare_notes():
-    """Create some minimal personal notes."""
-    person, __ = Person.objects.get_or_create(first_name="Jane", last_name="Doe")
-
-    excuse_type, __ = ExcuseType.objects.get_or_create(short_name="Foo", name="Fooooooooooooo")
-    notes = [
-        PersonalNote(
-            person=person,
-            event=_generate_event(date(2021, 10, 1)),
-            absent=True,
-            remarks="This is baz.",
-        ),
-        PersonalNote(person=person, event=_generate_event(date(2021, 11, 1)), absent=True),
-        PersonalNote(
-            person=person, event=_generate_event(date(2022, 10, 1)), absent=True, excused=True
-        ),
-        PersonalNote(
-            person=person,
-            event=_generate_event(date(2021, 3, 1)),
-            absent=True,
-            excused=True,
-            excuse_type=excuse_type,
-        ),
-        PersonalNote(person=person, event=_generate_event(date(2021, 10, 4)), tardiness=10),
-        PersonalNote(
-            person=person, event=_generate_event(date(2032, 10, 11)), remarks="Good work!"
-        ),
-        PersonalNote(person=person, event=_generate_event(date(2032, 10, 11))),
-    ]
-    PersonalNote.objects.bulk_create(notes)
-    return notes
-
-
-def test_mark_as_excused_action():
-    notes = _prepare_notes()
-    assert PersonalNote.objects.filter(excused=True).count() == 2
-    mark_as_excused(None, None, PersonalNote.objects.all())
-    assert PersonalNote.objects.filter(excused=True).count() == 4
-    assert PersonalNote.objects.filter(excuse_type=None, excused=True).count() == 4
-
-
-def test_mark_as_unexcused_action():
-    notes = _prepare_notes()
-    assert PersonalNote.objects.filter(excused=True).count() == 2
-    mark_as_unexcused(None, None, PersonalNote.objects.all())
-    assert PersonalNote.objects.filter(excused=True).count() == 0
-    assert PersonalNote.objects.filter(excuse_type=None, excused=True).count() == 0
-
-
-def test_mark_as_excuse_type_generator_action():
-    excuse_type, __ = ExcuseType.objects.get_or_create(short_name="Foo", name="Fooooooooooooo")
-    notes = _prepare_notes()
-    assert PersonalNote.objects.filter(excused=True).count() == 2
-    assert PersonalNote.objects.filter(excused=True, excuse_type=excuse_type).count() == 1
-    mark_as_foo = mark_as_excuse_type_generator(excuse_type=excuse_type)
-    mark_as_foo(None, None, PersonalNote.objects.all())
-    assert PersonalNote.objects.filter(excused=True).count() == 4
-    assert PersonalNote.objects.filter(excuse_type=excuse_type, excused=True).count() == 4
-
-
-def test_delete_personal_note_action():
-    notes = _prepare_notes()
-    assert PersonalNote.objects.not_empty().count() == 6
-    delete_personal_note(None, None, PersonalNote.objects.all())
-    assert PersonalNote.objects.not_empty().count() == 0
diff --git a/aleksis/apps/alsijil/util/alsijil_helpers.py b/aleksis/apps/alsijil/util/alsijil_helpers.py
index 118b70af8e1a71f9d5e56a8d251c5a319cae0e02..4c93eb1fc46e9fda08afa58a2dbb8d8dbf71e150 100644
--- a/aleksis/apps/alsijil/util/alsijil_helpers.py
+++ b/aleksis/apps/alsijil/util/alsijil_helpers.py
@@ -1,407 +1,4 @@
-from datetime import date
-from operator import itemgetter
-from typing import Any, Dict, Iterable, List, Optional, Sequence, Union
-
-from django.db.models.expressions import Exists, OuterRef
-from django.db.models.query import Prefetch, QuerySet
-from django.db.models.query_utils import Q
-from django.http import HttpRequest
-from django.utils.formats import date_format
-from django.utils.translation import gettext as _
-
-from calendarweek import CalendarWeek
-
-from aleksis.apps.alsijil.forms import FilterRegisterObjectForm
-from aleksis.apps.alsijil.models import LessonDocumentation
-from aleksis.apps.chronos.models import Event, ExtraLesson, Holiday, LessonPeriod
-from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
 from aleksis.apps.kolego.models import AbsenceReasonTag
-from aleksis.core.models import Group
-from aleksis.core.util.core_helpers import get_site_preferences
-
-
-def get_register_object_by_pk(
-    request: HttpRequest,
-    model: Optional[str] = None,
-    year: Optional[int] = None,
-    week: Optional[int] = None,
-    id_: Optional[int] = None,
-) -> Optional[Union[LessonPeriod, Event, ExtraLesson]]:
-    """Get register object either by given object_id or by time and current person."""
-    wanted_week = CalendarWeek(year=year, week=week)
-    if id_ and model == "lesson":
-        register_object = LessonPeriod.objects.annotate_week(wanted_week).get(pk=id_)
-    elif id_ and model == "event":
-        register_object = Event.objects.get(pk=id_)
-    elif id_ and model == "extra_lesson":
-        register_object = ExtraLesson.objects.get(pk=id_)
-    elif hasattr(request, "user") and hasattr(request.user, "person"):
-        if request.user.person.lessons_as_teacher.exists():
-            register_object = (
-                LessonPeriod.objects.at_time().filter_teacher(request.user.person).first()
-            )
-        else:
-            register_object = (
-                LessonPeriod.objects.at_time().filter_participant(request.user.person).first()
-            )
-    else:
-        register_object = None
-    return register_object
-
-
-def get_timetable_instance_by_pk(
-    request: HttpRequest,
-    year: Optional[int] = None,
-    week: Optional[int] = None,
-    type_: Optional[str] = None,
-    id_: Optional[int] = None,
-):
-    """Get timetable object (teacher, room or group) by given type and id or the current person."""
-    if type_ and id_:
-        return get_el_by_pk(request, type_, id_)
-    elif hasattr(request, "user") and hasattr(request.user, "person"):
-        return request.user.person
-
-
-def annotate_documentations(
-    klass: Union[Event, LessonPeriod, ExtraLesson], wanted_week: CalendarWeek, pks: List[int]
-) -> QuerySet:
-    """Return an annotated queryset of all provided register objects."""
-    if isinstance(klass, LessonPeriod):
-        prefetch = Prefetch(
-            "documentations",
-            queryset=LessonDocumentation.objects.filter(
-                week=wanted_week.week, year=wanted_week.year
-            ),
-        )
-    else:
-        prefetch = Prefetch("documentations")
-    instances = klass.objects.prefetch_related(prefetch).filter(pk__in=pks)
-
-    if klass == LessonPeriod:
-        instances = instances.annotate_week(wanted_week)
-    elif klass in (LessonPeriod, ExtraLesson):
-        instances = instances.order_by("period__weekday", "period__period")
-    else:
-        instances = instances.order_by("period_from__weekday", "period_from__period")
-
-    instances = instances.annotate(
-        has_documentation=Exists(
-            LessonDocumentation.objects.filter(
-                ~Q(topic__exact=""),
-                Q(week=wanted_week.week, year=wanted_week.year) | Q(week=None, year=None),
-            ).filter(**{klass.label_: OuterRef("pk")})
-        )
-    )
-
-    return instances
-
-
-def register_objects_sorter(register_object: Union[LessonPeriod, Event, ExtraLesson]) -> int:
-    """Sort key for sorted/sort for sorting a list of class register objects.
-
-    This will sort the objects by the start period.
-    """
-    if hasattr(register_object, "period"):
-        return register_object.period.period
-    elif isinstance(register_object, Event):
-        return register_object.period_from_on_day
-    else:
-        return 0
-
-
-def _filter_register_objects_by_dict(
-    filter_dict: Dict[str, Any],
-    register_objects: QuerySet[Union[LessonPeriod, Event, ExtraLesson]],
-    label_: str,
-) -> QuerySet[Union[LessonPeriod, Event, ExtraLesson]]:
-    """Filter register objects by a dictionary generated through ``FilterRegisterObjectForm``."""
-    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 = []
-    lesson_periods = list(lesson_periods)
-    date_start = lesson_periods[0].lesson.validity.date_start
-    date_end = lesson_periods[-1].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 = []
-    inherit_privileges_preference = get_site_preferences()[
-        "alsijil__inherit_privileges_from_parent_group"
-    ]
-    for lesson_period in lesson_periods:
-        parent_group_owned_by_person = inherit_privileges_preference and (
-            Group.objects.filter(
-                child_groups__in=Group.objects.filter(lessons__lesson_periods=lesson_period),
-                owners=filter_dict.get("person"),
-            ).exists()
-        )
-        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,
-                # substitution teacher or, when the corresponding
-                # preference is switched on, owner of a parent group
-                # of this lesson period
-                if filter_dict.get("person") and (
-                    filter_dict.get("person") not in lesson_period.lesson.teachers.all()
-                    and not sub
-                    and not parent_group_owned_by_person
-                ):
-                    continue
-
-                teachers = lesson_period.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.date)
-            day_sort = register_object.date
-            period = f"{register_object.period.period}."
-            period_sort = register_object.period.period
-        else:
-            register_object.annotate_day(register_object.date_end)
-            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()
-    filter_dict["school_term"] = filter_dict.get("school_term", initial_filter_data["school_term"])
-
-    # If there is not school year at all, there are definitely no data.
-    if not filter_dict["school_term"]:
-        return []
-
-    filter_dict["date_start"] = filter_dict.get("date_start", initial_filter_data["date_start"])
-    filter_dict["date_end"] = filter_dict.get("date_end", initial_filter_data["date_end"])
-    filter_dict["filter_date"] = bool(filter_dict.get("date_start")) and bool(
-        filter_dict.get("date_end")
-    )
-
-    # Get all holidays in the selected school term to sort all data in holidays out
-    holidays = Holiday.objects.within_dates(
-        filter_dict["school_term"].date_start, filter_dict["school_term"].date_end
-    )
-    holiday_days = holidays.get_all_days()
-
-    lesson_periods = _filter_register_objects_by_dict(
-        filter_dict,
-        LessonPeriod.objects.order_by("lesson__validity__date_start"),
-        LessonPeriod.label_,
-    )
-    events = _filter_register_objects_by_dict(
-        filter_dict, Event.objects.exclude_holidays(holidays), Event.label_
-    )
-    extra_lessons = _filter_register_objects_by_dict(
-        filter_dict, ExtraLesson.objects.exclude_holidays(holidays), ExtraLesson.label_
-    )
-
-    # Prefetch documentations for all register objects and substitutions for all lesson periods
-    # in order to prevent extra queries
-    documentations = LessonDocumentation.objects.not_empty().filter(
-        Q(event__in=events)
-        | Q(extra_lesson__in=extra_lessons)
-        | Q(lesson_period__in=lesson_periods)
-    )
-
-    if lesson_periods:
-        register_objects = _generate_dicts_for_lesson_periods(
-            filter_dict, lesson_periods, documentations, holiday_days
-        )
-        register_objects += _generate_dicts_for_events_and_extra_lessons(
-            filter_dict, list(events) + list(extra_lessons), documentations
-        )
-
-        # Sort table entries by date and period and configure table
-        register_objects = sorted(register_objects, key=itemgetter("date_sort", "period_sort"))
-        return register_objects
-    return []
 
 
 def get_absence_reason_tag():
diff --git a/aleksis/apps/alsijil/util/predicates.py b/aleksis/apps/alsijil/util/predicates.py
index 5966a7459bc8a32f7e7468f20de2e7da0044441e..2a27bca7e2260a13d12199027d3501add3494b60 100644
--- a/aleksis/apps/alsijil/util/predicates.py
+++ b/aleksis/apps/alsijil/util/predicates.py
@@ -1,4 +1,4 @@
-from typing import Any, Union
+from typing import Union
 
 from django.contrib.auth.models import User
 from django.db.models import Q
@@ -6,77 +6,13 @@ from django.utils.timezone import localdate, now
 
 from rules import predicate
 
-from aleksis.apps.chronos.models import Event, ExtraLesson, LessonEvent, LessonPeriod
+from aleksis.apps.chronos.models import LessonEvent
 from aleksis.apps.cursus.models import Course
 from aleksis.core.models import Group, Person
 from aleksis.core.util.core_helpers import get_site_preferences
 from aleksis.core.util.predicates import check_object_permission
 
-from ..models import Documentation, NewPersonalNote, PersonalNote
-
-
-@predicate
-def is_none(user: User, obj: Any) -> bool:
-    """Predicate that checks if the provided object is None-like."""
-    return not bool(obj)
-
-
-@predicate
-def is_lesson_teacher(user: User, obj: Union[LessonPeriod, Event, ExtraLesson]) -> bool:
-    """Predicate for teachers of a lesson.
-
-    Checks whether the person linked to the user is a teacher in the register object.
-    If the register object is a lesson period and has a substitution linked,
-    this will **only** check if the person is one of the substitution teachers.
-    """
-    if obj:
-        return user.person in obj.get_teachers().all()
-    return False
-
-
-@predicate
-def is_lesson_original_teacher(user: User, obj: Union[LessonPeriod, Event, ExtraLesson]) -> bool:
-    """Predicate for teachers of a lesson.
-
-    Checks whether the person linked to the user is a teacher in the register object.
-    If the register object is a lesson period and has a substitution linked,
-    this will **also** check if the person is one of the substitution teachers.
-    """
-    if obj:
-        if isinstance(obj, LessonPeriod) and user.person in obj.lesson.teachers.all():
-            return True
-        return user.person in obj.get_teachers().all()
-    return False
-
-
-@predicate
-def is_lesson_participant(user: User, obj: LessonPeriod) -> bool:
-    """Predicate for participants of a lesson.
-
-    Checks whether the person linked to the user is a member in
-    the groups linked to the given LessonPeriod.
-    """
-    if hasattr(obj, "lesson") or hasattr(obj, "groups"):
-        for group in obj.get_groups().all():
-            if user.person in list(group.members.all()):
-                return True
-    return False
-
-
-@predicate
-def is_lesson_parent_group_owner(user: User, obj: LessonPeriod) -> bool:
-    """
-    Predicate for parent group owners of a lesson.
-
-    Checks whether the person linked to the user is the owner of
-    any parent groups of any groups of the given LessonPeriods lesson.
-    """
-    if hasattr(obj, "lesson") or hasattr(obj, "groups"):
-        for group in obj.get_groups().all():
-            for parent_group in group.parent_groups.all():
-                if user.person in list(parent_group.owners.all()):
-                    return True
-    return False
+from ..models import Documentation, NewPersonalNote
 
 
 @predicate
@@ -130,19 +66,6 @@ def use_prefetched(obj, attr):
     return getattr(obj, attr).all()
 
 
-@predicate
-def is_person_primary_group_owner(user: User, obj: Person) -> bool:
-    """
-    Predicate for group owners of the person's primary group.
-
-    Checks whether the person linked to the user is
-    the owner of the primary group of the given person.
-    """
-    if obj.primary_group:
-        return user.person in use_prefetched(obj.primary_group, "owners")
-    return False
-
-
 def has_person_group_object_perm(perm: str):
     """Predicate builder for permissions on a set of member groups.
 
@@ -171,55 +94,6 @@ def is_group_member(user: User, obj: Union[Group, Person]) -> bool:
     return False
 
 
-def has_lesson_group_object_perm(perm: str):
-    """Predicate builder for permissions on lesson groups.
-
-    Checks whether a user has a permission on any group of a LessonPeriod.
-    """
-    name = f"has_lesson_group_object_perm:{perm}"
-
-    @predicate(name)
-    def fn(user: User, obj: LessonPeriod) -> bool:
-        if hasattr(obj, "lesson"):
-            groups = obj.lesson.groups.all()
-            for group in groups:
-                if check_object_permission(user, perm, group, checker_obj=obj):
-                    return True
-        return False
-
-    return fn
-
-
-def has_personal_note_group_perm(perm: str):
-    """Predicate builder for permissions on personal notes.
-
-    Checks whether a user has a permission on any group of a person of a PersonalNote.
-    """
-    name = f"has_personal_note_person_or_group_perm:{perm}"
-
-    @predicate(name)
-    def fn(user: User, obj: PersonalNote) -> bool:
-        if hasattr(obj, "person"):
-            groups = obj.person.member_of.all()
-            for group in groups:
-                if check_object_permission(user, perm, group, checker_obj=obj):
-                    return True
-        return False
-
-    return fn
-
-
-@predicate
-def is_own_personal_note(user: User, obj: PersonalNote) -> bool:
-    """Predicate for users referred to in a personal note.
-
-    Checks whether the user referred to in a PersonalNote is the active user.
-    """
-    if hasattr(obj, "person") and obj.person is user.person:
-        return True
-    return False
-
-
 @predicate
 def is_parent_group_owner(user: User, obj: Group) -> bool:
     """Predicate which checks whether the user is the owner of any parent group of the group."""
@@ -230,66 +104,6 @@ def is_parent_group_owner(user: User, obj: Group) -> bool:
     return False
 
 
-@predicate
-def is_personal_note_lesson_teacher(user: User, obj: PersonalNote) -> bool:
-    """Predicate for teachers of a register object linked to a personal note.
-
-    Checks whether the person linked to the user is a teacher
-    in the register object linked to the personal note.
-    If the register object is a lesson period and has a substitution linked,
-    this will **only** check if the person is one of the substitution teachers.
-    """
-    if hasattr(obj, "register_object"):
-        return user.person in obj.register_object.get_teachers().all()
-    return False
-
-
-@predicate
-def is_personal_note_lesson_original_teacher(user: User, obj: PersonalNote) -> bool:
-    """Predicate for teachers of a register object linked to a personal note.
-
-    Checks whether the person linked to the user is a teacher
-    in the register object linked to the personal note.
-    If the register object is a lesson period and has a substitution linked,
-    this will **also** check if the person is one of the substitution teachers.
-    """
-    if hasattr(obj, "register_object"):
-        if (
-            isinstance(obj.register_object, LessonPeriod)
-            and user.person in obj.lesson_period.lesson.teachers.all()
-        ):
-            return True
-
-        return user.person in obj.register_object.get_teachers().all()
-    return False
-
-
-@predicate
-def is_personal_note_lesson_parent_group_owner(user: User, obj: PersonalNote) -> bool:
-    """
-    Predicate for parent group owners of a lesson referred to in the lesson of a personal note.
-
-    Checks whether the person linked to the user is the owner of
-    any parent groups of any groups of the given LessonPeriod lesson of the given PersonalNote.
-    If so, also checks whether the person linked to the personal note actually is a member of this
-    parent group.
-    """
-    if hasattr(obj, "register_object"):
-        for group in obj.register_object.get_groups().all():
-            for parent_group in group.parent_groups.all():
-                if user.person in use_prefetched(
-                    parent_group, "owners"
-                ) and obj.person in use_prefetched(parent_group, "members"):
-                    return True
-    return False
-
-
-@predicate
-def is_teacher(user: User, obj: Person) -> bool:
-    """Predicate which checks if the provided object is a teacher."""
-    return user.person.is_teacher
-
-
 @predicate
 def is_group_role_assignment_group_owner(user: User, obj: Union[Group, Person]) -> bool:
     """Predicate for group owners of a group role assignment.
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index 0395a8bfbc54fcab81dfea092f431045d15ae134..75f0e1b06d6dfe14380047fe8a6d1221109bcf21 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -1,36 +1,20 @@
-from contextlib import nullcontext
-from copy import deepcopy
-from datetime import datetime, timedelta
-from typing import Any, Dict, Optional
+from typing import Any, Dict
 
-from django.apps import apps
-from django.core.exceptions import PermissionDenied
-from django.db.models import Count, Exists, FilteredRelation, OuterRef, Prefetch, Q, Sum
-from django.db.models.expressions import Case, When
-from django.db.models.functions import Extract
-from django.http import Http404, HttpRequest, HttpResponse, HttpResponseNotFound
-from django.shortcuts import get_object_or_404, redirect, render
+from django.db.models import Q
+from django.http import HttpRequest, HttpResponse
+from django.shortcuts import get_object_or_404, redirect
 from django.urls import reverse, reverse_lazy
 from django.utils import timezone
 from django.utils.decorators import method_decorator
 from django.utils.http import url_has_allowed_host_and_scheme
 from django.utils.translation import gettext as _
-from django.views import View
 from django.views.decorators.cache import never_cache
 from django.views.generic import DetailView
 
-import reversion
-from calendarweek import CalendarWeek
-from django_tables2 import RequestConfig, SingleTableView
-from guardian.core import ObjectPermissionChecker
-from guardian.shortcuts import get_objects_for_user
+from django_tables2 import SingleTableView
 from reversion.views import RevisionMixin
 from rules.contrib.views import PermissionRequiredMixin, permission_required
 
-from aleksis.apps.chronos.managers import TimetableType
-from aleksis.apps.chronos.models import Event, ExtraLesson, Holiday, LessonPeriod, TimePeriod
-from aleksis.apps.chronos.util.build import build_weekdays
-from aleksis.apps.chronos.util.date import get_weeks_for_year, week_weekday_to_date
 from aleksis.core.decorators import pwa_cache
 from aleksis.core.mixins import (
     AdvancedCreateView,
@@ -38,594 +22,23 @@ from aleksis.core.mixins import (
     AdvancedEditView,
     SuccessNextMixin,
 )
-from aleksis.core.models import Group, PDFFile, Person, SchoolTerm
+from aleksis.core.models import Group, PDFFile
 from aleksis.core.util import messages
 from aleksis.core.util.celery_progress import render_progress_page
-from aleksis.core.util.core_helpers import get_site_preferences, has_person, objectgetter_optional
-from aleksis.core.util.predicates import check_global_permission
+from aleksis.core.util.core_helpers import has_person, objectgetter_optional
 
-from .filters import PersonalNoteFilter
 from .forms import (
     AssignGroupRoleForm,
-    ExcuseTypeForm,
-    FilterRegisterObjectForm,
     GroupRoleAssignmentEditForm,
     GroupRoleForm,
-    LessonDocumentationForm,
-    PersonalNoteFormSet,
-    PersonOverviewForm,
-    RegisterAbsenceForm,
-    RegisterObjectActionForm,
-    SelectForm,
 )
-from .models import ExcuseType, ExtraMark, GroupRole, GroupRoleAssignment, PersonalNote
+from .models import GroupRole, GroupRoleAssignment
 from .tables import (
-    ExcuseTypeTable,
     GroupRoleTable,
-    PersonalNoteTable,
-    RegisterObjectSelectTable,
-    RegisterObjectTable,
 )
 from .tasks import generate_full_register_printout
-from .util.alsijil_helpers import (
-    annotate_documentations,
-    generate_list_of_all_register_objects,
-    get_register_object_by_pk,
-    get_timetable_instance_by_pk,
-    register_objects_sorter,
-)
-
-
-@pwa_cache
-@permission_required("alsijil.view_register_object_rule", fn=get_register_object_by_pk)  # FIXME
-def register_object(
-    request: HttpRequest,
-    model: Optional[str] = None,
-    year: Optional[int] = None,
-    week: Optional[int] = None,
-    id_: Optional[int] = None,
-) -> HttpResponse:
-    context = {}
-
-    register_object = get_register_object_by_pk(request, model, year, week, id_)
-
-    if id_ and model == "lesson":
-        wanted_week = CalendarWeek(year=year, week=week)
-    elif id_ and model == "extra_lesson":
-        wanted_week = register_object.calendar_week
-    elif hasattr(request, "user") and hasattr(request.user, "person"):
-        wanted_week = CalendarWeek()
-    else:
-        wanted_week = None
-
-    if not all((year, week, id_)):
-        if register_object and model == "lesson":
-            return redirect(
-                "lesson_period",
-                wanted_week.year,
-                wanted_week.week,
-                register_object.pk,
-            )
-        elif not register_object:
-            raise Http404(
-                _(
-                    "You either selected an invalid lesson or "
-                    "there is currently no lesson in progress."
-                )
-            )
-
-    date_of_lesson = (
-        week_weekday_to_date(wanted_week, register_object.period.weekday)
-        if not isinstance(register_object, Event)
-        else register_object.date_start
-    )
-    start_time = (
-        register_object.period.time_start
-        if not isinstance(register_object, Event)
-        else register_object.period_from.time_start
-    )
-
-    if isinstance(register_object, Event):
-        register_object.annotate_day(date_of_lesson)
-    if isinstance(register_object, LessonPeriod) and (
-        date_of_lesson < register_object.lesson.validity.date_start
-        or date_of_lesson > register_object.lesson.validity.date_end
-    ):
-        return HttpResponseNotFound()
-
-    if (
-        datetime.combine(date_of_lesson, start_time) > datetime.now()
-        and not (
-            get_site_preferences()["alsijil__open_periods_same_day"]
-            and date_of_lesson <= datetime.now().date()
-        )
-        and not request.user.is_superuser
-    ):
-        raise PermissionDenied(
-            _("You are not allowed to create a lesson documentation for a lesson in the future.")
-        )
-
-    holiday = Holiday.on_day(date_of_lesson)
-    blocked_because_holidays = (
-        holiday is not None and not get_site_preferences()["alsijil__allow_entries_in_holidays"]
-    )
-    context["blocked_because_holidays"] = blocked_because_holidays
-    context["holiday"] = holiday
-
-    next_lesson = (
-        request.user.person.next_lesson(register_object, date_of_lesson)
-        if isinstance(register_object, LessonPeriod)
-        else None
-    )
-    prev_lesson = (
-        request.user.person.previous_lesson(register_object, date_of_lesson)
-        if isinstance(register_object, LessonPeriod)
-        else None
-    )
-    back_url = reverse(
-        "lesson_period", args=[wanted_week.year, wanted_week.week, register_object.pk]
-    )
-    context["back_url"] = back_url
-
-    context["register_object"] = register_object
-    context["week"] = wanted_week
-    context["day"] = date_of_lesson
-    context["next_lesson_person"] = next_lesson
-    context["prev_lesson_person"] = prev_lesson
-    context["prev_lesson"] = (
-        register_object.prev if isinstance(register_object, LessonPeriod) else None
-    )
-    context["next_lesson"] = (
-        register_object.next if isinstance(register_object, LessonPeriod) else None
-    )
-
-    if not blocked_because_holidays:
-        groups = register_object.get_groups().all()
-        if groups:
-            first_group = groups.first()
-            context["first_group"] = first_group
-
-        # Group roles
-        show_group_roles = request.user.person.preferences[
-            "alsijil__group_roles_in_lesson_view"
-        ] and request.user.has_perm(
-            "alsijil.view_assigned_grouproles_for_register_object_rule", register_object
-        )
-        if show_group_roles:
-            group_roles = GroupRole.objects.with_assignments(date_of_lesson, groups)
-            context["group_roles"] = group_roles
-
-        with_seating_plan = (
-            apps.is_installed("aleksis.apps.stoelindeling")
-            and groups
-            and request.user.has_perm("stoelindeling.view_seatingplan_for_group_rule", first_group)
-        )
-        context["with_seating_plan"] = with_seating_plan
-
-        if with_seating_plan:
-            seating_plan = register_object.seating_plan
-            context["seating_plan"] = register_object.seating_plan
-            if seating_plan and seating_plan.group != first_group:
-                context["seating_plan_parent"] = True
-
-        # Create or get lesson documentation object; can be empty when first opening lesson
-        lesson_documentation = register_object.get_or_create_lesson_documentation(wanted_week)
-        context["has_documentation"] = bool(lesson_documentation.topic)
-
-        lesson_documentation_form = LessonDocumentationForm(
-            request.POST or None,
-            instance=lesson_documentation,
-            prefix="lesson_documentation",
-        )
-
-        # Prefetch object permissions for all related groups of the register object
-        # because the object permissions are checked for all groups of the register object
-        # That has to be set as an attribute of the register object,
-        # so that the permission system can use the prefetched data.
-        checker = ObjectPermissionChecker(request.user)
-        checker.prefetch_perms(register_object.get_groups().all())
-        register_object.set_object_permission_checker(checker)
-
-        # Create a formset that holds all personal notes for all persons in this lesson
-        if not request.user.has_perm(
-            "alsijil.view_register_object_personalnote_rule", register_object
-        ):
-            persons = Person.objects.filter(
-                Q(pk=request.user.person.pk) | Q(member_of__in=request.user.person.owner_of.all())
-            ).distinct()
-        else:
-            persons = Person.objects.all()
-
-        persons_qs = register_object.get_personal_notes(persons, wanted_week).distinct()
-
-        # Annotate group roles
-        if show_group_roles:
-            persons_qs = persons_qs.prefetch_related(
-                Prefetch(
-                    "person__group_roles",
-                    queryset=GroupRoleAssignment.objects.on_day(date_of_lesson).for_groups(groups),
-                ),
-            )
-
-        personal_note_formset = PersonalNoteFormSet(
-            request.POST or None, queryset=persons_qs, prefix="personal_notes"
-        )
-
-        if request.method == "POST":
-            if lesson_documentation_form.is_valid() and request.user.has_perm(
-                "alsijil.edit_lessondocumentation_rule", register_object
-            ):
-                with reversion.create_revision():
-                    reversion.set_user(request.user)
-                    lesson_documentation_form.save()
-
-                messages.success(request, _("The lesson documentation has been saved."))
-
-            substitution = (
-                register_object.get_substitution()
-                if isinstance(register_object, LessonPeriod)
-                else None
-            )
-            if (
-                not getattr(substitution, "cancelled", False)
-                or not get_site_preferences()["alsijil__block_personal_notes_for_cancelled"]
-            ):
-                if personal_note_formset.is_valid() and request.user.has_perm(
-                    "alsijil.edit_register_object_personalnote_rule", register_object
-                ):
-                    with reversion.create_revision():
-                        reversion.set_user(request.user)
-                        instances = personal_note_formset.save()
-
-                    if (not isinstance(register_object, Event)) and get_site_preferences()[
-                        "alsijil__carry_over_personal_notes"
-                    ]:
-                        # Iterate over personal notes
-                        # and carry changed absences to following lessons
-                        with reversion.create_revision():
-                            reversion.set_user(request.user)
-                            for instance in instances:
-                                instance.person.mark_absent(
-                                    wanted_week[register_object.period.weekday],
-                                    register_object.period.period + 1,
-                                    instance.absent,
-                                    instance.excused,
-                                    instance.excuse_type,
-                                )
-
-                messages.success(request, _("The personal notes have been saved."))
-
-                # Regenerate form here to ensure that programmatically
-                # changed data will be shown correctly
-                personal_note_formset = PersonalNoteFormSet(
-                    None, queryset=persons_qs, prefix="personal_notes"
-                )
-
-        back_url = request.GET.get("back", "")
-        back_url_is_safe = url_has_allowed_host_and_scheme(
-            url=back_url,
-            allowed_hosts={request.get_host()},
-            require_https=request.is_secure(),
-        )
-        if back_url_is_safe:
-            context["back_to_week_url"] = back_url
-        elif register_object.get_groups().all():
-            context["back_to_week_url"] = reverse(
-                "week_view_by_week",
-                args=[
-                    lesson_documentation.calendar_week.year,
-                    lesson_documentation.calendar_week.week,
-                    "group",
-                    register_object.get_groups().all()[0].pk,
-                ],
-            )
-        context["lesson_documentation"] = lesson_documentation
-        context["lesson_documentation_form"] = lesson_documentation_form
-        context["personal_note_formset"] = personal_note_formset
-
-    return render(request, "alsijil/class_register/lesson.html", context)
-
-
-@pwa_cache
-@permission_required("alsijil.view_week_rule", fn=get_timetable_instance_by_pk)
-def week_view(
-    request: HttpRequest,
-    year: Optional[int] = None,
-    week: Optional[int] = None,
-    type_: Optional[str] = None,
-    id_: Optional[int] = None,
-) -> HttpResponse:
-    context = {}
-
-    wanted_week = CalendarWeek(year=year, week=week) if year and week else CalendarWeek()
 
-    instance = get_timetable_instance_by_pk(request, year, week, type_, id_)
 
-    lesson_periods = LessonPeriod.objects.in_week(wanted_week).prefetch_related(
-        "lesson__groups__members",
-        "lesson__groups__parent_groups",
-        "lesson__groups__parent_groups__owners",
-    )
-    events = Event.objects.in_week(wanted_week)
-    extra_lessons = ExtraLesson.objects.in_week(wanted_week)
-
-    query_exists = True
-    if type_ and id_:
-        if isinstance(instance, HttpResponseNotFound):
-            return HttpResponseNotFound()
-
-        type_ = TimetableType.from_string(type_)
-
-        lesson_periods = lesson_periods.filter_from_type(type_, instance)
-        events = events.filter_from_type(type_, instance)
-        extra_lessons = extra_lessons.filter_from_type(type_, instance)
-
-    elif hasattr(request, "user") and hasattr(request.user, "person"):
-        if request.user.person.lessons_as_teacher.exists():
-            inherit_privileges_preference = get_site_preferences()[
-                "alsijil__inherit_privileges_from_parent_group"
-            ]
-            lesson_periods = (
-                lesson_periods.filter_teacher(request.user.person).union(
-                    lesson_periods.filter_groups(request.user.person.owner_of.all())
-                )
-                if inherit_privileges_preference
-                else lesson_periods.filter_teacher(request.user.person)
-            )
-            events = (
-                events.filter_teacher(request.user.person).union(
-                    events.filter_groups(request.user.person.owner_of.all())
-                )
-                if inherit_privileges_preference
-                else events.filter_teacher(request.user.person)
-            )
-            extra_lessons = (
-                extra_lessons.filter_teacher(request.user.person).union(
-                    extra_lessons.filter_groups(request.user.person.owner_of.all())
-                )
-                if inherit_privileges_preference
-                else extra_lessons.filter_teacher(request.user.person)
-            )
-
-            type_ = TimetableType.TEACHER
-        else:
-            lesson_periods = lesson_periods.filter_participant(request.user.person)
-            events = events.filter_participant(request.user.person)
-            extra_lessons = extra_lessons.filter_participant(request.user.person)
-
-    else:
-        query_exists = False
-        lesson_periods = None
-        events = None
-        extra_lessons = None
-
-    # Add a form to filter the view
-    if type_:
-        initial = {type_.value: instance}
-        back_url = reverse(
-            "week_view_by_week", args=[wanted_week.year, wanted_week.week, type_.value, instance.pk]
-        )
-    else:
-        initial = {}
-        back_url = reverse("week_view_by_week", args=[wanted_week.year, wanted_week.week])
-    context["back_url"] = back_url
-    select_form = SelectForm(request, request.POST or None, initial=initial)
-
-    if request.method == "POST" and select_form.is_valid():
-        if "type_" not in select_form.cleaned_data:
-            return redirect("week_view_by_week", wanted_week.year, wanted_week.week)
-        else:
-            return redirect(
-                "week_view_by_week",
-                wanted_week.year,
-                wanted_week.week,
-                select_form.cleaned_data["type_"].value,
-                select_form.cleaned_data["instance"].pk,
-            )
-
-    group = instance if type_ == TimetableType.GROUP else None
-
-    # Group roles
-    show_group_roles = (
-        group
-        and request.user.person.preferences["alsijil__group_roles_in_week_view"]
-        and request.user.has_perm("alsijil.view_assigned_grouproles_rule", group)
-    )
-    if show_group_roles:
-        group_roles = GroupRole.objects.with_assignments(wanted_week, [group])
-        context["group_roles"] = group_roles
-        group_roles_persons = GroupRoleAssignment.objects.in_week(wanted_week).for_group(group)
-
-    extra_marks = ExtraMark.objects.all()
-
-    if query_exists:
-        lesson_periods_pk = list(lesson_periods.values_list("pk", flat=True))
-        lesson_periods = annotate_documentations(LessonPeriod, wanted_week, lesson_periods_pk)
-
-        events_pk = [event.pk for event in events]
-        events = annotate_documentations(Event, wanted_week, events_pk)
-
-        extra_lessons_pk = list(extra_lessons.values_list("pk", flat=True))
-        extra_lessons = annotate_documentations(ExtraLesson, wanted_week, extra_lessons_pk)
-        groups = Group.objects.filter(
-            Q(lessons__lesson_periods__in=lesson_periods_pk)
-            | Q(events__in=events_pk)
-            | Q(extra_lessons__in=extra_lessons_pk)
-        )
-    else:
-        lesson_periods_pk = []
-        events_pk = []
-        extra_lessons_pk = []
-
-    if lesson_periods_pk or events_pk or extra_lessons_pk:
-        # Aggregate all personal notes for this group and week
-        persons_qs = Person.objects.all()
-
-        if not request.user.has_perm("alsijil.view_week_personalnote_rule", instance):
-            persons_qs = persons_qs.filter(pk=request.user.person.pk)
-        elif group:
-            persons_qs = (
-                persons_qs.filter(member_of=group)
-                .filter(member_of__in=request.user.person.owner_of.all())
-                .distinct()
-            )
-        else:
-            persons_qs = (
-                persons_qs.filter(member_of__in=groups)
-                .filter(member_of__in=request.user.person.owner_of.all())
-                .distinct()
-            )
-
-        # Prefetch object permissions for persons and groups the persons are members of
-        # because the object permissions are checked for both persons and groups
-        checker = ObjectPermissionChecker(request.user)
-        checker.prefetch_perms(persons_qs.prefetch_related(None))
-        checker.prefetch_perms(groups)
-
-        prefetched_personal_notes = list(
-            PersonalNote.objects.filter(  #
-                Q(event__in=events_pk)
-                | Q(
-                    week=wanted_week.week,
-                    year=wanted_week.year,
-                    lesson_period__in=lesson_periods_pk,
-                )
-                | Q(extra_lesson__in=extra_lessons_pk)
-            ).filter(~Q(remarks=""))
-        )
-        persons_qs = (
-            persons_qs.select_related("primary_group")
-            .prefetch_related(
-                Prefetch(
-                    "primary_group__owners",
-                    queryset=Person.objects.filter(pk=request.user.person.pk),
-                    to_attr="owners_prefetched",
-                ),
-                Prefetch("member_of", queryset=groups, to_attr="member_of_prefetched"),
-            )
-            .annotate(
-                filtered_personal_notes=FilteredRelation(
-                    "personal_notes",
-                    condition=(
-                        Q(personal_notes__event__in=events_pk)
-                        | Q(
-                            personal_notes__week=wanted_week.week,
-                            personal_notes__year=wanted_week.year,
-                            personal_notes__lesson_period__in=lesson_periods_pk,
-                        )
-                        | Q(personal_notes__extra_lesson__in=extra_lessons_pk)
-                    ),
-                )
-            )
-        )
-
-        persons_qs = persons_qs.annotate(
-            absences_count=Count(
-                "filtered_personal_notes",
-                filter=Q(filtered_personal_notes__absent=True),
-            ),
-            unexcused_count=Count(
-                "filtered_personal_notes",
-                filter=Q(
-                    filtered_personal_notes__absent=True, filtered_personal_notes__excused=False
-                ),
-            ),
-            tardiness_sum=Sum("filtered_personal_notes__tardiness"),
-            tardiness_count=Count(
-                "filtered_personal_notes",
-                filter=Q(filtered_personal_notes__tardiness__gt=0),
-            ),
-        )
-
-        for extra_mark in extra_marks:
-            persons_qs = persons_qs.annotate(
-                **{
-                    extra_mark.count_label: Count(
-                        "filtered_personal_notes",
-                        filter=Q(filtered_personal_notes__extra_marks=extra_mark),
-                    )
-                }
-            )
-
-        persons = []
-        for person in persons_qs:
-            personal_notes = []
-            for note in filter(lambda note: note.person_id == person.pk, prefetched_personal_notes):
-                if note.lesson_period:
-                    note.lesson_period.annotate_week(wanted_week)
-                personal_notes.append(note)
-            person.set_object_permission_checker(checker)
-            person_dict = {"person": person, "personal_notes": personal_notes}
-            if show_group_roles:
-                person_dict["group_roles"] = filter(
-                    lambda role: role.person_id == person.pk, group_roles_persons
-                )
-            persons.append(person_dict)
-    else:
-        persons = None
-
-    context["extra_marks"] = extra_marks
-    context["week"] = wanted_week
-    context["weeks"] = get_weeks_for_year(year=wanted_week.year)
-
-    context["lesson_periods"] = lesson_periods
-    context["events"] = events
-    context["extra_lessons"] = extra_lessons
-
-    context["persons"] = persons
-    context["group"] = group
-    context["select_form"] = select_form
-    context["instance"] = instance
-    context["weekdays"] = build_weekdays(TimePeriod.WEEKDAY_CHOICES, wanted_week)
-
-    regrouped_objects = {}
-
-    for register_object in list(lesson_periods) + list(extra_lessons):
-        register_object.weekday = register_object.period.weekday
-        regrouped_objects.setdefault(register_object.period.weekday, [])
-        regrouped_objects[register_object.period.weekday].append(register_object)
-
-    for event in events:
-        weekday_from = event.get_start_weekday(wanted_week)
-        weekday_to = event.get_end_weekday(wanted_week)
-
-        for weekday in range(weekday_from, weekday_to + 1):
-            # Make a copy in order to keep the annotation only on this weekday
-            event_copy = deepcopy(event)
-            event_copy.annotate_day(wanted_week[weekday])
-            event_copy.weekday = weekday
-
-            regrouped_objects.setdefault(weekday, [])
-            regrouped_objects[weekday].append(event_copy)
-
-    # Sort register objects
-    for weekday in regrouped_objects:
-        to_sort = regrouped_objects[weekday]
-        regrouped_objects[weekday] = sorted(to_sort, key=register_objects_sorter)
-    context["regrouped_objects"] = regrouped_objects
-
-    week_prev = wanted_week - 1
-    week_next = wanted_week + 1
-    args_prev = [week_prev.year, week_prev.week]
-    args_next = [week_next.year, week_next.week]
-    args_dest = []
-    if type_ and id_:
-        args_prev += [type_.value, id_]
-        args_next += [type_.value, id_]
-        args_dest += [type_.value, id_]
-
-    context["week_select"] = {
-        "year": wanted_week.year,
-        "dest": reverse("week_view_placeholders", args=args_dest),
-    }
-
-    context["url_prev"] = reverse("week_view_by_week", args=args_prev)
-    context["url_next"] = reverse("week_view_by_week", args=args_next)
-
-    return render(request, "alsijil/class_register/week_view.html", context)
-
-
-@pwa_cache
 @permission_required(
     "alsijil.view_full_register_rule", fn=objectgetter_optional(Group, None, False)
 )
@@ -665,436 +78,6 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
     )
 
 
-@pwa_cache
-@permission_required("alsijil.view_my_students_rule")
-def my_students(request: HttpRequest) -> HttpResponse:
-    context = {}
-    relevant_groups = (
-        request.user.person.get_owner_groups_with_lessons()
-        .annotate(has_parents=Exists(Group.objects.filter(child_groups=OuterRef("pk"))))
-        .filter(members__isnull=False)
-        .order_by("has_parents", "name")
-        .prefetch_related("members")
-        .distinct()
-    )
-
-    # Prefetch object permissions for persons and groups the persons are members of
-    # because the object permissions are checked for both persons and groups
-    all_persons = Person.objects.filter(member_of__in=relevant_groups)
-    checker = ObjectPermissionChecker(request.user)
-    checker.prefetch_perms(relevant_groups)
-    checker.prefetch_perms(all_persons)
-
-    new_groups = []
-    for group in relevant_groups:
-        persons = group.generate_person_list_with_class_register_statistics(
-            group.members.prefetch_related(
-                "primary_group__owners",
-                Prefetch("member_of", queryset=relevant_groups, to_attr="member_of_prefetched"),
-            )
-        ).distinct()
-        persons_for_group = []
-        for person in persons:
-            person.set_object_permission_checker(checker)
-            persons_for_group.append(person)
-        new_groups.append((group, persons_for_group))
-
-    context["groups"] = new_groups
-    context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
-    context["excuse_types_not_absent"] = ExcuseType.objects.filter(count_as_absent=False)
-    context["extra_marks"] = ExtraMark.objects.all()
-    return render(request, "alsijil/class_register/persons.html", context)
-
-
-@pwa_cache
-@permission_required(
-    "alsijil.view_my_groups_rule",
-)
-def my_groups(request: HttpRequest) -> HttpResponse:
-    context = {}
-    context["groups"] = request.user.person.get_owner_groups_with_lessons().annotate(
-        students_count=Count("members", distinct=True)
-    )
-    return render(request, "alsijil/class_register/groups.html", context)
-
-
-@method_decorator(pwa_cache, "dispatch")
-class StudentsList(PermissionRequiredMixin, DetailView):
-    model = Group
-    template_name = "alsijil/class_register/students_list.html"
-    permission_required = "alsijil.view_students_list_rule"
-
-    def get_context_data(self, **kwargs):
-        context = super().get_context_data(**kwargs)
-        context["group"] = self.object
-        context["persons"] = (
-            self.object.generate_person_list_with_class_register_statistics()
-            .filter(member_of__in=self.request.user.person.owner_of.all())
-            .distinct()
-        )
-        context["extra_marks"] = ExtraMark.objects.all()
-        context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
-        context["excuse_types_not_absent"] = ExcuseType.objects.filter(count_as_absent=False)
-        return context
-
-
-@pwa_cache
-@permission_required(
-    "alsijil.view_person_overview_rule",
-    fn=objectgetter_optional(
-        Person.objects.prefetch_related("member_of__owners"), "request.user.person", True
-    ),
-)
-def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse:
-    context = {}
-    person = objectgetter_optional(
-        Person.objects.prefetch_related("member_of__owners"),
-        default="request.user.person",
-        default_eval=True,
-    )(request, id_)
-    context["person"] = person
-
-    person_personal_notes = (
-        person.personal_notes.all()
-        .prefetch_related(
-            "lesson_period__lesson__groups",
-            "lesson_period__lesson__teachers",
-            "lesson_period__substitutions",
-        )
-        .annotate_date_range()
-    )
-
-    # Prefetch object permissions for groups the person is a member of
-    # because the object permissions are checked for all groups the person is a member of
-    # That has to be set as an attribute of the register object,
-    # so that the permission system can use the prefetched data.
-    checker = ObjectPermissionChecker(request.user)
-    checker.prefetch_perms(Group.objects.filter(members=person))
-    person.set_object_permission_checker(checker)
-
-    if request.user.has_perm("alsijil.view_person_overview_personalnote_rule", person):
-        allowed_personal_notes = person_personal_notes.all()
-    else:
-        allowed_personal_notes = person_personal_notes.filter(
-            Q(lesson_period__lesson__groups__owners=request.user.person)
-            | Q(extra_lesson__groups__owners=request.user.person)
-            | Q(event__groups__owners=request.user.person)
-        )
-
-    unexcused_absences = allowed_personal_notes.filter(absent=True, excused=False)
-    context["unexcused_absences"] = unexcused_absences
-
-    personal_notes = (
-        allowed_personal_notes.not_empty()
-        .filter(Q(absent=True) | Q(tardiness__gt=0) | ~Q(remarks="") | Q(extra_marks__isnull=False))
-        .annotate(
-            school_term_start=Case(
-                When(event__isnull=False, then="event__school_term__date_start"),
-                When(extra_lesson__isnull=False, then="extra_lesson__school_term__date_start"),
-                When(
-                    lesson_period__isnull=False,
-                    then="lesson_period__lesson__validity__school_term__date_start",
-                ),
-            ),
-            order_year=Case(
-                When(event__isnull=False, then=Extract("event__date_start", "year")),
-                When(extra_lesson__isnull=False, then="extra_lesson__year"),
-                When(lesson_period__isnull=False, then="year"),
-            ),
-            order_week=Case(
-                When(event__isnull=False, then=Extract("event__date_start", "week")),
-                When(extra_lesson__isnull=False, then="extra_lesson__week"),
-                When(lesson_period__isnull=False, then="week"),
-            ),
-            order_weekday=Case(
-                When(event__isnull=False, then="event__period_from__weekday"),
-                When(extra_lesson__isnull=False, then="extra_lesson__period__weekday"),
-                When(lesson_period__isnull=False, then="lesson_period__period__weekday"),
-            ),
-            order_period=Case(
-                When(event__isnull=False, then="event__period_from__period"),
-                When(extra_lesson__isnull=False, then="extra_lesson__period__period"),
-                When(lesson_period__isnull=False, then="lesson_period__period__period"),
-            ),
-            order_groups=Case(
-                When(event__isnull=False, then="event__groups"),
-                When(extra_lesson__isnull=False, then="extra_lesson__groups"),
-                When(lesson_period__isnull=False, then="lesson_period__lesson__groups"),
-            ),
-            order_teachers=Case(
-                When(event__isnull=False, then="event__teachers"),
-                When(extra_lesson__isnull=False, then="extra_lesson__teachers"),
-                When(lesson_period__isnull=False, then="lesson_period__lesson__teachers"),
-            ),
-        )
-        .order_by(
-            "-school_term_start",
-            "-order_year",
-            "-order_week",
-            "-order_weekday",
-            "order_period",
-        )
-        .annotate_date_range()
-        .annotate_subject()
-    )
-
-    personal_note_filter_object = PersonalNoteFilter(request.GET, queryset=personal_notes)
-    filtered_personal_notes = personal_note_filter_object.qs
-    context["personal_note_filter_form"] = personal_note_filter_object.form
-
-    used_filters = list(personal_note_filter_object.data.values())
-    context["num_filters"] = (
-        len(used_filters) - used_filters.count("") - used_filters.count("unknown")
-    )
-
-    personal_notes_list = []
-    for note in personal_notes:
-        note.set_object_permission_checker(checker)
-        personal_notes_list.append(note)
-    context["personal_notes"] = personal_notes_list
-    context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
-    context["excuse_types_not_absent"] = ExcuseType.objects.filter(count_as_absent=False)
-
-    form = PersonOverviewForm(request, request.POST or None, queryset=allowed_personal_notes)
-    if (
-        request.method == "POST"
-        and request.user.has_perm("alsijil.edit_person_overview_personalnote_rule", person)
-        and form.is_valid()
-    ):
-        with reversion.create_revision():
-            reversion.set_user(request.user)
-            form.execute()
-        person.refresh_from_db()
-    context["action_form"] = form
-
-    table = PersonalNoteTable(filtered_personal_notes)
-    RequestConfig(request, paginate={"per_page": 20}).configure(table)
-    context["personal_notes_table"] = table
-
-    extra_marks = ExtraMark.objects.all()
-    excuse_types = ExcuseType.objects.all()
-    if request.user.has_perm("alsijil.view_person_statistics_personalnote_rule", person):
-        school_terms = SchoolTerm.objects.all().order_by("-date_start")
-        stats = []
-        for school_term in school_terms:
-            stat = {}
-            personal_notes = PersonalNote.objects.filter(
-                person=person,
-            ).filter(
-                Q(lesson_period__lesson__validity__school_term=school_term)
-                | Q(extra_lesson__school_term=school_term)
-                | Q(event__school_term=school_term)
-            )
-
-            if not personal_notes.exists():
-                continue
-
-            stat.update(
-                personal_notes.filter(absent=True)
-                .exclude(excuse_type__count_as_absent=False)
-                .aggregate(absences_count=Count("absent"))
-            )
-            stat.update(
-                personal_notes.filter(absent=True, excused=True)
-                .exclude(excuse_type__count_as_absent=False)
-                .aggregate(excused=Count("absent"))
-            )
-            stat.update(
-                personal_notes.filter(absent=True, excused=True, excuse_type__isnull=True)
-                .exclude(excuse_type__count_as_absent=False)
-                .aggregate(excused_no_excuse_type=Count("absent"))
-            )
-            stat.update(
-                personal_notes.filter(absent=True, excused=False).aggregate(
-                    unexcused=Count("absent")
-                )
-            )
-            stat.update(personal_notes.aggregate(tardiness=Sum("tardiness")))
-            stat.update(
-                personal_notes.filter(~Q(tardiness=0)).aggregate(tardiness_count=Count("tardiness"))
-            )
-
-            for extra_mark in extra_marks:
-                stat.update(
-                    personal_notes.filter(extra_marks=extra_mark).aggregate(
-                        **{extra_mark.count_label: Count("pk")}
-                    )
-                )
-
-            for excuse_type in excuse_types:
-                stat.update(
-                    personal_notes.filter(absent=True, excuse_type=excuse_type).aggregate(
-                        **{excuse_type.count_label: Count("absent")}
-                    )
-                )
-
-            stats.append((school_term, stat))
-        context["stats"] = stats
-
-    context["extra_marks"] = extra_marks
-
-    # Build filter with own form and logic as django-filter can't work with different models
-    if request.user.person.preferences["alsijil__default_lesson_documentation_filter"]:
-        default_documentation = False
-    else:
-        default_documentation = None
-
-    filter_form = FilterRegisterObjectForm(
-        request, request.GET or None, for_person=True, default_documentation=default_documentation
-    )
-    filter_dict = (
-        filter_form.cleaned_data
-        if filter_form.is_valid()
-        else {"has_documentation": default_documentation}
-    )
-    filter_dict["person"] = person
-    context["filter_form"] = filter_form
-
-    if person.is_teacher:
-        register_objects = generate_list_of_all_register_objects(filter_dict)
-        table = RegisterObjectTable(register_objects)
-        items_per_page = request.user.person.preferences[
-            "alsijil__register_objects_table_items_per_page"
-        ]
-        RequestConfig(request, paginate={"per_page": items_per_page}).configure(table)
-        context["register_object_table"] = table
-    return render(request, "alsijil/class_register/person.html", context)
-
-
-@never_cache
-@permission_required("alsijil.register_absence_rule", fn=objectgetter_optional(Person))
-def register_absence(request: HttpRequest, id_: int = None) -> HttpResponse:
-    context = {}
-
-    person = get_object_or_404(Person, pk=id_) if id_ else None
-
-    register_absence_form = RegisterAbsenceForm(
-        request, request.POST or None, initial={"person": person}
-    )
-
-    if (
-        request.method == "POST"
-        and register_absence_form.is_valid()
-        and request.user.has_perm("alsijil.register_absence_rule", person)
-    ):
-        confirmed = request.POST.get("confirmed", "0") == "1"
-
-        # Get data from form
-        person = register_absence_form.cleaned_data["person"]
-        start_date = register_absence_form.cleaned_data["date_start"]
-        end_date = register_absence_form.cleaned_data["date_end"]
-        from_period = register_absence_form.cleaned_data["from_period"]
-        to_period = register_absence_form.cleaned_data["to_period"]
-        absent = register_absence_form.cleaned_data["absent"]
-        excused = register_absence_form.cleaned_data["excused"]
-        excuse_type = register_absence_form.cleaned_data["excuse_type"]
-        remarks = register_absence_form.cleaned_data["remarks"]
-
-        # Mark person as absent
-        affected_count = 0
-        delta = end_date - start_date
-        for i in range(delta.days + 1):
-            from_period_on_day = from_period if i == 0 else TimePeriod.period_min
-            to_period_on_day = to_period if i == delta.days else TimePeriod.period_max
-            day = start_date + timedelta(days=i)
-
-            # Skip holidays if activated
-            if not get_site_preferences()["alsijil__allow_entries_in_holidays"]:
-                holiday = Holiday.on_day(day)
-                if holiday:
-                    continue
-
-            with reversion.create_revision() if confirmed else nullcontext():
-                affected_count += person.mark_absent(
-                    day,
-                    from_period_on_day,
-                    absent,
-                    excused,
-                    excuse_type,
-                    remarks,
-                    to_period_on_day,
-                    dry_run=not confirmed,
-                )
-
-        if not confirmed:
-            # Show confirmation page
-            context = {}
-            context["affected_lessons"] = affected_count
-            context["person"] = person
-            context["form_data"] = register_absence_form.cleaned_data
-            context["form"] = register_absence_form
-            return render(request, "alsijil/absences/register_confirm.html", context)
-        else:
-            messages.success(request, _("The absence has been saved."))
-            return redirect("overview_person", person.pk)
-
-    context["person"] = person
-    context["register_absence_form"] = register_absence_form
-
-    return render(request, "alsijil/absences/register.html", context)
-
-
-@method_decorator(never_cache, name="dispatch")
-class DeletePersonalNoteView(PermissionRequiredMixin, DetailView):
-    model = PersonalNote
-    template_name = "core/pages/delete.html"
-    permission_required = "alsijil.edit_personalnote_rule"
-
-    def post(self, request, *args, **kwargs):
-        note = self.get_object()
-        with reversion.create_revision():
-            reversion.set_user(request.user)
-            note.reset_values()
-            note.save()
-        messages.success(request, _("The personal note has been deleted."))
-        return redirect("overview_person", note.person.pk)
-
-
-@method_decorator(pwa_cache, "dispatch")
-class ExcuseTypeListView(PermissionRequiredMixin, SingleTableView):
-    """Table of all excuse types."""
-
-    model = ExcuseType
-    table_class = ExcuseTypeTable
-    permission_required = "alsijil.view_excusetypes_rule"
-    template_name = "alsijil/excuse_type/list.html"
-
-
-@method_decorator(never_cache, name="dispatch")
-class ExcuseTypeCreateView(PermissionRequiredMixin, AdvancedCreateView):
-    """Create view for excuse types."""
-
-    model = ExcuseType
-    form_class = ExcuseTypeForm
-    permission_required = "alsijil.add_excusetype_rule"
-    template_name = "alsijil/excuse_type/create.html"
-    success_url = reverse_lazy("excuse_types")
-    success_message = _("The excuse type has been created.")
-
-
-@method_decorator(never_cache, name="dispatch")
-class ExcuseTypeEditView(PermissionRequiredMixin, AdvancedEditView):
-    """Edit view for excuse types."""
-
-    model = ExcuseType
-    form_class = ExcuseTypeForm
-    permission_required = "alsijil.edit_excusetype_rule"
-    template_name = "alsijil/excuse_type/edit.html"
-    success_url = reverse_lazy("excuse_types")
-    success_message = _("The excuse type has been saved.")
-
-
-@method_decorator(never_cache, "dispatch")
-class ExcuseTypeDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDeleteView):
-    """Delete view for excuse types."""
-
-    model = ExcuseType
-    permission_required = "alsijil.delete_excusetype_rule"
-    template_name = "core/pages/delete.html"
-    success_url = reverse_lazy("excuse_types")
-    success_message = _("The excuse type has been deleted.")
-
-
 @method_decorator(pwa_cache, "dispatch")
 class GroupRoleListView(PermissionRequiredMixin, SingleTableView):
     """Table of all group roles."""
@@ -1261,52 +244,3 @@ class GroupRoleAssignmentDeleteView(
     def get_success_url(self) -> str:
         pk = self.object.groups.first().pk
         return reverse("assigned_group_roles", args=[pk])
-
-
-@method_decorator(pwa_cache, "dispatch")
-class AllRegisterObjectsView(PermissionRequiredMixin, View):
-    """Provide overview of all register objects for coordinators."""
-
-    permission_required = "alsijil.view_register_objects_list_rule"
-
-    def get_context_data(self, request):
-        context = {}
-        # Filter selectable groups by permissions
-        groups = Group.objects.all()
-        if not check_global_permission(request.user, "alsijil.view_full_register"):
-            allowed_groups = get_objects_for_user(
-                self.request.user, "core.view_full_register_group", Group
-            ).values_list("pk", flat=True)
-            groups = groups.filter(Q(parent_groups__in=allowed_groups) | Q(pk__in=allowed_groups))
-
-        # Build filter with own form and logic as django-filter can't work with different models
-        filter_form = FilterRegisterObjectForm(
-            request, request.GET or None, for_person=False, groups=groups
-        )
-        filter_dict = filter_form.cleaned_data if filter_form.is_valid() else {}
-        filter_dict["groups"] = groups
-        context["filter_form"] = filter_form
-
-        register_objects = generate_list_of_all_register_objects(filter_dict)
-
-        self.action_form = RegisterObjectActionForm(request, register_objects, request.POST or None)
-        context["action_form"] = self.action_form
-
-        if register_objects:
-            self.table = RegisterObjectSelectTable(register_objects)
-            items_per_page = request.user.person.preferences[
-                "alsijil__register_objects_table_items_per_page"
-            ]
-            RequestConfig(request, paginate={"per_page": items_per_page}).configure(self.table)
-            context["table"] = self.table
-        return context
-
-    def get(self, request: HttpRequest) -> HttpResponse:
-        context = self.get_context_data(request)
-        return render(request, "alsijil/class_register/all_objects.html", context)
-
-    def post(self, request: HttpRequest):
-        context = self.get_context_data(request)
-        if self.action_form.is_valid():
-            self.action_form.execute()
-        return render(request, "alsijil/class_register/all_objects.html", context)
diff --git a/pyproject.toml b/pyproject.toml
index 3bbbbc8096e676c9522ab1308502dae104dc50c4..2f82bc3ed9291cd3d7a9b04d725ff6b67216942b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "AlekSIS-App-Alsijil"
-version = "4.0.0.dev5"
+version = "4.0.0.dev8"
 packages = [
     { include = "aleksis" }
 ]
@@ -50,8 +50,7 @@ priority = "supplemental"
 [tool.poetry.dependencies]
 python = "^3.10"
 aleksis-core = "^4.0.0.dev11"
-aleksis-app-chronos = "^4.0.0.dev5"
-aleksis-app-stoelindeling = { version = "^3.0.dev1", optional = true }
+aleksis-app-chronos = "^4.0.0.dev7"
 aleksis-app-kolego = "^0.1.0.dev3"
 
 [tool.poetry.extras]