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/data_checks.py b/aleksis/apps/alsijil/data_checks.py
index 87e703a0cb4fb17cff6be360cbea52895a6fa775..b9b88a1556c13980cd51f30c432b9933a7039638 100644
--- a/aleksis/apps/alsijil/data_checks.py
+++ b/aleksis/apps/alsijil/data_checks.py
@@ -159,23 +159,3 @@ class PersonalNoteOnHolidaysDataCheck(DataCheck):
         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/managers.py b/aleksis/apps/alsijil/managers.py
index dc29fae25c189c7c2250279a6e8d86206f52e520..0eabc94d78bb261fa1b597bead2624ce4200d120 100644
--- a/aleksis/apps/alsijil/managers.py
+++ b/aleksis/apps/alsijil/managers.py
@@ -1,12 +1,9 @@
 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
 
@@ -17,125 +14,6 @@ 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
 
diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py
index abbedd709dd3604172b831c2c931a2ea5f198b5a..b3186f6103de2d0f6781d03d9562e468124e80f3 100644
--- a/aleksis/apps/alsijil/model_extensions.py
+++ b/aleksis/apps/alsijil/model_extensions.py
@@ -1,188 +1,6 @@
-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 aleksis.apps.alsijil.managers import PersonalNoteQuerySet
-from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
 from aleksis.core.models import Group, Person
-from aleksis.core.util.core_helpers import get_site_preferences
-
-from .models import 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)
 
 # Dynamically add extra permissions to Group and Person models in core
 # Note: requires migrate afterwards
@@ -208,288 +26,3 @@ Group.add_permission(
 )
 Group.add_permission("assign_grouprole", _("Can assign a group role for this group"))
 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
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 5303a40e57e3bf7ac6bbeba98da2f6e206be6007..8f0be3f18661dab3dfffea268c3498345ad435a2 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,29 @@ 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.models import LessonEvent
 from aleksis.apps.chronos.util.format import format_m2m
 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.
 
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..fc4b79fd24871e6092ff7a1eb4376c0710adc662 100644
--- a/aleksis/apps/alsijil/tasks.py
+++ b/aleksis/apps/alsijil/tasks.py
@@ -13,7 +13,7 @@ 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 .models import ExtraMark
 
 
 @recorded_task
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..fd49ca4a2f044064e6739d8f53ed27e8eb0e2539 100644
--- a/aleksis/apps/alsijil/util/predicates.py
+++ b/aleksis/apps/alsijil/util/predicates.py
@@ -12,7 +12,7 @@ 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
+from ..models import Documentation, NewPersonalNote
 
 
 @predicate
@@ -198,7 +198,7 @@ def has_personal_note_group_perm(perm: str):
     name = f"has_personal_note_person_or_group_perm:{perm}"
 
     @predicate(name)
-    def fn(user: User, obj: PersonalNote) -> bool:
+    def fn(user: User, obj) -> bool:
         if hasattr(obj, "person"):
             groups = obj.person.member_of.all()
             for group in groups:
@@ -210,7 +210,7 @@ def has_personal_note_group_perm(perm: str):
 
 
 @predicate
-def is_own_personal_note(user: User, obj: PersonalNote) -> bool:
+def is_own_personal_note(user: User, obj) -> bool:
     """Predicate for users referred to in a personal note.
 
     Checks whether the user referred to in a PersonalNote is the active user.
@@ -231,7 +231,7 @@ def is_parent_group_owner(user: User, obj: Group) -> bool:
 
 
 @predicate
-def is_personal_note_lesson_teacher(user: User, obj: PersonalNote) -> bool:
+def is_personal_note_lesson_teacher(user: User, obj) -> bool:
     """Predicate for teachers of a register object linked to a personal note.
 
     Checks whether the person linked to the user is a teacher
@@ -245,7 +245,7 @@ def is_personal_note_lesson_teacher(user: User, obj: PersonalNote) -> bool:
 
 
 @predicate
-def is_personal_note_lesson_original_teacher(user: User, obj: PersonalNote) -> bool:
+def is_personal_note_lesson_original_teacher(user: User, ob) -> bool:
     """Predicate for teachers of a register object linked to a personal note.
 
     Checks whether the person linked to the user is a teacher
@@ -265,7 +265,7 @@ def is_personal_note_lesson_original_teacher(user: User, obj: PersonalNote) -> b
 
 
 @predicate
-def is_personal_note_lesson_parent_group_owner(user: User, obj: PersonalNote) -> bool:
+def is_personal_note_lesson_parent_group_owner(user: User, obj) -> bool:
     """
     Predicate for parent group owners of a lesson referred to in the lesson of a personal note.
 
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)