diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue
index 857cdac09725c6e7b86885ccae5513e644e563bf..8f048056c30adbaf24b8ed14393d2eb61dccdff5 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue
@@ -6,15 +6,13 @@ import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.v
 </script>
 
 <template>
-  <mobile-fullscreen-dialog
-    v-model="dialog"
-  >
+  <mobile-fullscreen-dialog v-model="dialog">
     <template #activator>
       <secondary-action-button
         i18n-key="alsijil.coursebook.print.button"
         icon-text="$print"
         :loading="loading"
-        @click="dialog=true"
+        @click="dialog = true"
         :disabled="dialog"
       />
     </template>
@@ -50,7 +48,9 @@ import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.v
         />
         <v-checkbox
           v-model="includeTeachersAndSubjectsTable"
-          :label="$t('alsijil.coursebook.print.include_teachers_and_subjects_table')"
+          :label="
+            $t('alsijil.coursebook.print.include_teachers_and_subjects_table')
+          "
         />
         <v-checkbox
           v-model="includePersonOverviews"
@@ -64,9 +64,7 @@ import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.v
     </template>
     <template #actions>
       <!-- TODO: Should cancel reset state? -->
-      <cancel-button
-        @click="dialog=false"
-      />
+      <cancel-button @click="dialog = false" />
       <primary-action-button
         i18n-key="alsijil.coursebook.print.button"
         icon-text="$print"
@@ -132,7 +130,7 @@ export default {
   methods: {
     setGroupSelection(groups) {
       this.$emit("input", groups);
-      this.currentGroupSelection=groups;
+      this.currentGroupSelection = groups;
     },
     print() {
       this.$router.push({
@@ -140,7 +138,7 @@ export default {
         params: {
           groupIds: this.selectedGroups,
         },
-        query: { 
+        query: {
           cover: this.includeCover,
           abbreviations: this.includeAbbreviations,
           members_table: this.includeMembersTable,
diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py
index 4ad59b387532598ac392795a143e2acf90717fdb..efb06db7a0b298c21fa81663a2865a976b65cf77 100644
--- a/aleksis/apps/alsijil/model_extensions.py
+++ b/aleksis/apps/alsijil/model_extensions.py
@@ -93,11 +93,10 @@ def annotate_person_statistics(
     return persons
 
 
-def  annotate_person_statistics_from_documentations(
-        persons: QuerySet[Person],
-        docs: QuerySet[Documentation]
+def annotate_person_statistics_from_documentations(
+    persons: QuerySet[Person], docs: QuerySet[Documentation]
 ) -> QuerySet[Person]:
-    """Annotate a queryset of persons with class register statistics from queryset of documentations."""
+    """Annotate a queryset of persons with class register statistics from documentations."""
     docs = list(docs.values_list("pk", flat=True))
     return annotate_person_statistics(
         persons,
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 91fe27f044fb99587b77da89aaad954e96ddd29e..e622c5c3d47cadfe556e6a63387919b122661153 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -1,5 +1,5 @@
 from datetime import datetime
-from typing import Optional, List
+from typing import List, Optional
 
 from django.contrib.auth.models import User
 from django.core.exceptions import PermissionDenied
diff --git a/aleksis/apps/alsijil/tasks.py b/aleksis/apps/alsijil/tasks.py
index 8b59c67e9508d3c1a01e4744af4ca119a121703c..365d407499fcdcc4a72c0ec8c583f0e37312bbdb 100644
--- a/aleksis/apps/alsijil/tasks.py
+++ b/aleksis/apps/alsijil/tasks.py
@@ -1,57 +1,59 @@
-from typing import Optional, List
+from datetime import date
+from typing import List, Optional
 
-from copy import deepcopy
-from datetime import date, timedelta
-
-from django.db.models import Q, Prefetch
+from django.db.models import Prefetch, Q
 from django.utils.translation import gettext as _
 
-from calendarweek import CalendarWeek
 from celery.result import allow_join_result
 from celery.states import SUCCESS
 
+from aleksis.apps.cursus.models import Course
+from aleksis.apps.kolego.models.absence import AbsenceReason
 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 aleksis.apps.cursus.models import Course
-from aleksis.apps.kolego.models.absence import AbsenceReason
-
-from .models import ExtraMark, NewPersonalNote, Documentation, ParticipationStatus
 from .model_extensions import annotate_person_statistics_from_documentations
+from .models import Documentation, ExtraMark, NewPersonalNote, ParticipationStatus
 
 
 @recorded_task
 def generate_full_register_printout(
-        groups: List[int],
-        file_object: int,
-        recorder: ProgressRecorder,
-        include_cover: Optional[bool] = True,
-        include_abbreviations: Optional[bool] = True,
-        include_members_table: Optional[bool] = True,
-        include_teachers_and_subjects_table: Optional[bool] = True,
-        include_person_overviews: Optional[bool] = True,
-        include_coursebook: Optional[bool] = True
+    groups: List[int],
+    file_object: int,
+    recorder: ProgressRecorder,
+    include_cover: Optional[bool] = True,
+    include_abbreviations: Optional[bool] = True,
+    include_members_table: Optional[bool] = True,
+    include_teachers_and_subjects_table: Optional[bool] = True,
+    include_person_overviews: Optional[bool] = True,
+    include_coursebook: Optional[bool] = True,
 ):
     """Generate a configurable register printout as PDF for a group."""
-    def prefetch_notable_participations(select_related=[], prefetch_related=[]):
+
+    def prefetch_notable_participations(select_related=None, prefetch_related=None):
+        if not select_related:
+            select_related = []
+        if not prefetch_related:
+            prefetch_related = []
         return Prefetch(
             "participations",
             to_attr="notable_participations",
-            queryset=ParticipationStatus.objects
-            .filter(Q(absence_reason__tags__short_name="class_register")
-                    | Q(tardiness__isnull=False)
-                    )
+            queryset=ParticipationStatus.objects.filter(
+                Q(absence_reason__tags__short_name="class_register") | Q(tardiness__isnull=False)
+            )
             .select_related("absence_reason", *select_related)
             .prefetch_related(*prefetch_related),
         )
 
-    def prefetch_personal_notes(name, select_related=[], prefetch_related=[]):
+    def prefetch_personal_notes(name, select_related=None, prefetch_related=None):
+        if not select_related:
+            select_related = []
+        if not prefetch_related:
+            prefetch_related = []
         return Prefetch(
             name,
-            queryset=NewPersonalNote.objects
-            .filter(Q(note__gt="")
-                    | Q(extra_mark__isnull=False))
+            queryset=NewPersonalNote.objects.filter(Q(note__gt="") | Q(extra_mark__isnull=False))
             .select_related("extra_mark", *select_related)
             .prefetch_related(*prefetch_related),
         )
@@ -70,15 +72,19 @@ def generate_full_register_printout(
     _number_of_steps = 5 + len(groups)
 
     recorder.set_progress(1, _number_of_steps, _("Loading data ..."))
-    
+
     groups = Group.objects.filter(pk__in=groups).order_by("name")
 
     if include_cover:
         groups = groups.select_related("school_term")
 
     if include_abbreviations or include_members_table:
-        context["absence_reasons"] = AbsenceReason.objects.filter(tags__short_name="class_register", count_as_absent=True)
-        context["absence_reasons_not_counted"] = AbsenceReason.objects.filter(tags__short_name="class_register", count_as_absent=False)
+        context["absence_reasons"] = AbsenceReason.objects.filter(
+            tags__short_name="class_register", count_as_absent=True
+        )
+        context["absence_reasons_not_counted"] = AbsenceReason.objects.filter(
+            tags__short_name="class_register", count_as_absent=False
+        )
         context["extra_marks"] = ExtraMark.objects.all()
 
     if include_members_table or include_person_overviews:
@@ -96,45 +102,39 @@ def generate_full_register_printout(
     recorder.set_progress(2, _number_of_steps, _("Loading groups ..."))
 
     for i, group in enumerate(groups, start=1):
-
-        recorder.set_progress(2 + i, _number_of_steps, _(f"Loading group {group.short_name or group.name} ..."))
+        recorder.set_progress(
+            2 + i, _number_of_steps, _(f"Loading group {group.short_name or group.name} ...")
+        )
 
         if include_members_table or include_person_overviews or include_coursebook:
             documentations = Documentation.objects.filter(
                 Q(datetime_start__date__gte=group.school_term.date_start)
                 & Q(datetime_end__date__lte=group.school_term.date_end)
-                & Q(pk__in=Documentation.objects.filter(course__groups=group)
+                & Q(
+                    pk__in=Documentation.objects.filter(course__groups=group)
                     .values_list("pk", flat=True)
                     .union(
-                        Documentation.objects.filter(course__groups__parent_groups=group).values_list(
-                            "pk", flat=True
-                        )
-                    )
+                        Documentation.objects.filter(
+                            course__groups__parent_groups=group
+                        ).values_list("pk", flat=True)
                     )
+                )
             )
 
         if include_members_table or include_person_overviews:
-            group.members_with_stats = annotate_person_statistics_from_documentations(group.members.all(), documentations)
+            group.members_with_stats = annotate_person_statistics_from_documentations(
+                group.members.all(), documentations
+            )
 
         if include_person_overviews:
             doc_query_set = documentations.select_related("subject").prefetch_related("teachers")
             group.members_with_stats = group.members_with_stats.prefetch_related(
                 prefetch_notable_participations(
-                    prefetch_related=[
-                        Prefetch(
-                            "related_documentation",
-                            queryset=doc_query_set
-                        )
-                    ]
+                    prefetch_related=[Prefetch("related_documentation", queryset=doc_query_set)]
                 ),
                 prefetch_personal_notes(
                     "new_personal_notes",
-                    prefetch_related=[
-                        Prefetch(
-                            "documentation",
-                            queryset=doc_query_set
-                        )
-                    ]
+                    prefetch_related=[Prefetch("documentation", queryset=doc_query_set)],
                 ),
             )
 
@@ -142,20 +142,20 @@ def generate_full_register_printout(
             group.as_list = [group]
 
         if include_coursebook:
-            group.documentations_by_day = {}
-            documentations = documentations.order_by("datetime_start").prefetch_related(
+            group.documentations_by_day = documentations.order_by(
+                "datetime_start"
+            ).prefetch_related(
                 prefetch_notable_participations(select_related=["person"]),
-                prefetch_personal_notes("personal_notes", select_related=["person"])
+                prefetch_personal_notes("personal_notes", select_related=["person"]),
             )
-            for doc in documentations:
-                group.documentations_by_day.setdefault(doc.datetime_start.date(), []).append(doc)
-
     context["groups"] = groups
 
     recorder.set_progress(3 + len(groups), _number_of_steps, _("Generating template ..."))
 
     file_object, result = generate_pdf_from_template(
-        "alsijil/print/register_for_group.html", context, file_object=PDFFile.objects.get(pk=file_object)
+        "alsijil/print/register_for_group.html",
+        context,
+        file_object=PDFFile.objects.get(pk=file_object),
     )
 
     recorder.set_progress(4 + len(groups), _number_of_steps, _("Generating PDF ..."))
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/absences.html b/aleksis/apps/alsijil/templates/alsijil/partials/absences.html
deleted file mode 100644
index d1faaadd474111453bc07e608ddb63aee11efedc..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/absences.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% load i18n rules %}
-{% for note in notes %}
-  {% has_perm "alsijil.view_personalnote_rule" user note as can_view_personalnote %}
-  {% if can_view_personalnote %}
-    <span class="{% if note.excused %}green-text{% else %}red-text{% endif %}">{{ note.person }}
-      {% if note.excused %}{% if note.excuse_type %}({{ note.excuse_type.short_name }}){% else %}{% trans "(e)" %}{% endif %}{% else %}{% trans "(u)" %}{% endif %}{% if not forloop.last %},{% endif %}
-    </span>
-  {% endif %}
-{% endfor %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/legend.html b/aleksis/apps/alsijil/templates/alsijil/partials/legend.html
deleted file mode 100644
index bf0c82d792b57943b36502f0b94dff3db193a70d..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/legend.html
+++ /dev/null
@@ -1,71 +0,0 @@
-{% load i18n %}
-<div class="card">
-  <div class="card-content">
-    <div class="card-title">{% trans "Legend" %}</div>
-    <div class="row">
-      <div class="col s12 m12 l4">
-        <h6>{% trans "General" %}</h6>
-        <ul class="collection">
-          <li class="collection-item chip-height">
-            <strong>{% trans "(a)" %}</strong> {% trans "Absences" %}
-            <span class="chip secondary-color white-text right">0</span>
-          </li>
-          <li class="collection-item chip-height">
-            <strong>{% trans "(u)" %}</strong> {% trans "Unexcused absences" %}
-            <span class="chip red white-text right">0</span>
-          </li>
-          <li class="collection-item chip-height">
-            <strong>{% trans "Sum (e)" %}</strong> {% trans "Sum of excused absences" %}
-            <span class="chip green white-text right">0</span>
-          </li>
-          <li class="collection-item chip-height">
-            <strong>{% trans "(e)" %}</strong> {% trans "Regular excused absences" %}
-            <span class="chip grey white-text right">0</span>
-          </li>
-        </ul>
-      </div>
-
-      {% if excuse_types %}
-        <div class="col s12 m12 l4">
-          <h6>{% trans "Excuse types" %}</h6>
-
-          <ul class="collection">
-            {% for excuse_type in excuse_types %}
-              <li class="collection-item chip-height">
-                <strong>({{ excuse_type.short_name }})</strong> {{ excuse_type.name }}
-                <span class="chip grey white-text right">0</span>
-              </li>
-            {% endfor %}
-          </ul>
-          {% if excuse_types_not_absent %}
-            <h6>{% trans "Excuse types (not counted as absent)" %}</h6>
-
-            <ul class="collection">
-              {% for excuse_type in excuse_types_not_absent %}
-                <li class="collection-item chip-height">
-                  <strong>({{ excuse_type.short_name }})</strong> {{ excuse_type.name }}
-                  <span class="chip grey white-text right">0</span>
-                </li>
-              {% endfor %}
-            </ul>
-          {% endif %}
-        </div>
-      {% endif %}
-
-      {% if extra_marks %}
-        <div class="col s12 m12 l4">
-          <h6>{% trans "Extra marks" %}</h6>
-
-          <ul class="collection">
-            {% for extra_mark in extra_marks %}
-              <li class="collection-item chip-height">
-                <strong>{{ extra_mark.short_name }}</strong> {{ extra_mark.name }}
-                <span class="chip grey white-text right">0</span>
-              </li>
-            {% endfor %}
-          </ul>
-        </div>
-      {% endif %}
-    </div>
-  </div>
-</div>
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html
deleted file mode 100644
index acd8d283d6ecc9fe7ed84234600444e1424a0e49..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{% load i18n week_helpers %}
-
-{% now_datetime as now_dt %}
-
-{% if has_documentation or register_object.has_documentation %}
-  {% include "alsijil/partials/lesson_status_icon.html" with text=_("Data complete") icon="mdi:check-circle-outline" color="green" %}
-{% elif not register_object.period %}
-  {% if week %}
-    {% period_to_time_start week register_object.raw_period_from_on_day as time_start %}
-    {% period_to_time_end week register_object.raw_period_to_on_day as time_end %}
-  {% else %}
-    {% period_to_time_start register_object.date_start register_object.period_from as time_start %}
-    {% period_to_time_end register_object.date_end register_object.period_to as time_end %}
-  {% endif %}
-
-  {% if now_dt > time_end %}
-    {% include "alsijil/partials/lesson_status_icon.html" with text=_("Missing data") icon="mdi:alert-outline" color="red" %}
-  {% elif now_dt > time_start and now_dt < time_end %}
-    {% include "alsijil/partials/lesson_status_icon.html" with text=_("Pending") icon="mdi:dots-horizontal" color="orange" %}
-  {% else %}
-    {% include "alsijil/partials/lesson_status_icon.html" with text=_("Event") icon="mdi:calendar" color="purple" %}
-  {% endif %}
-{% else %}
-  {% period_to_time_start week register_object.period as time_start %}
-  {% period_to_time_end week register_object.period as time_end %}
-
-  {% if substitution.cancelled or register_object.get_substitution.cancelled %}
-    {% include "alsijil/partials/lesson_status_icon.html" with text=_("Lesson cancelled") icon="mdi:close" color="red" %}
-  {% elif now_dt > time_end %}
-    {% include "alsijil/partials/lesson_status_icon.html" with text=_("Missing data") icon="mdi:alert-outline" color="red" %}
-  {% elif now_dt > time_start and now_dt < time_end %}
-    {% include "alsijil/partials/lesson_status_icon.html" with text=_("Pending") icon="mdi:dots-horizontal" color="orange" %}
-  {% elif substitution or register_object.get_substitution %}
-    {% include "alsijil/partials/lesson_status_icon.html" with text=_("Substitution") icon="mdi:update" color="orange" %}
-  {% endif %}
-{% endif %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status_icon.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status_icon.html
deleted file mode 100644
index 2c016c685bb89e8edca9e75408fd11109f655e0b..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status_icon.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% if chip %}
-  <span class="{% if chip %}chip{% endif %} {{ color }} white-text {{ css_class }}">
-    <i class="material-icons iconify left" data-icon="{{ icon }}"></i>
-    {{ text }}
-  </span>
-{% else %}
-  <i class="material-icons iconify {{ color }}{% firstof color_suffix "-text" %} tooltipped {{ css_class }}"
-     data-icon="{{ icon }}"
-     data-position="bottom"
-     data-tooltip="{{ text }}" title="{{ text }}">
-  </i>
-{% endif %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/objects_table.html b/aleksis/apps/alsijil/templates/alsijil/partials/objects_table.html
deleted file mode 100644
index e2053816e4a682b5de56596f0cf998e2b4e0a371..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/objects_table.html
+++ /dev/null
@@ -1,43 +0,0 @@
-{% load i18n material_form django_tables2 %}
-<div class="card">
-  <div class="card-content">
-    <div class="card-title">{% trans "Lesson filter" %}</div>
-    <form action="" method="get">
-      {% form form=filter_form %}{% endform %}
-      <button type="submit" class="btn waves-effect waves-light">
-        <i class="material-icons iconify left" data-icon="mdi:refresh"></i>
-        {% trans "Update filters" %}
-      </button>
-    </form>
-  </div>
-</div>
-
-{% if table %}
-  <div class="card">
-    <div class="card-content">
-      <form action="" method="post">
-        {% csrf_token %}
-        <div class="row">
-          <div class="col s12 {% if action_form %}m4 l4 xl6{% endif %}">
-            <div class="card-title">{% trans "Lesson table" %}</div>
-          </div>
-          {% if action_form %}
-            <div class="col s12 m8 l8 xl6">
-              <div class="col s12 m8">
-                {% form form=action_form %}{% endform %}
-              </div>
-              <div class="col s12 m4">
-                <button type="submit" class="btn waves-effect waves-primary">
-                  {% trans "Execute" %}
-                  <i class="material-icons iconify right" data-icon="mdi:send-outline"></i>
-                </button>
-              </div>
-            </div>
-          {% endif %}
-        </div>
-        {% render_table table %}
-
-      </form>
-    </div>
-  </div>
-{% endif %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/person_overview.html b/aleksis/apps/alsijil/templates/alsijil/partials/person_overview.html
index 3418b1c40e4bfabf9f11c5cc9074efbe23fb73c8..fa2fb386a1e69e4734f448606fc24eebe8ac9d84 100644
--- a/aleksis/apps/alsijil/templates/alsijil/partials/person_overview.html
+++ b/aleksis/apps/alsijil/templates/alsijil/partials/person_overview.html
@@ -1,15 +1,15 @@
 {% load static i18n data_helpers %}
 
-<h4>{% blocktrans %}Personal Overview: {{ person.last_name }}, {{ person.first_name }}{% endblocktrans %}</h4>
+<h4>{% blocktrans with full_name=person.full_name %}Personal Overview: {{ person.full_name }}{% endblocktrans %}</h4>
 
 <h5>{% blocktrans %}Contact Details{% endblocktrans %}</h5>
 <table class="person-info">
   <tr>
     <td rowspan="6" class="person-img">
       {% if person.photo %}
-        <img src="{{ person.photo.url }}" alt="{{ person.first_name }} {{ person.last_name }}"/>
+        <img src="{{ person.photo.url }}" alt="{{ person.full_name }}"/>
       {% else %}
-        <img src="{% static 'img/fallback.png' %}" alt="{{ person.first_name }} {{ person.last_name }}"/>
+        <img src="{% static 'img/fallback.png' %}" alt="{{ person.full_name }}"/>
       {% endif %}
     </td>
     <td><i class="material-icons iconify" data-icon="mdi:account-outline"></i></td>
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html b/aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html
deleted file mode 100644
index 69ce9c6c9a011744eb2b3bed80228fe3147ff01b..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html
+++ /dev/null
@@ -1,147 +0,0 @@
-{% load data_helpers time_helpers i18n rules %}
-
-{% if not persons %}
-  <figure class="alert primary">
-    <i class="material-icons iconify left" data-icon="mdi:alert-outline"></i>
-    {% blocktrans %}No students available.{% endblocktrans %}
-  </figure>
-{% else %}
-  <table class="highlight responsive-table">
-  <thead>
-  <tr class="hide-on-med-and-down">
-    <th rowspan="2">{% trans "Name" %}</th>
-    <th rowspan="2">{% trans "Primary group" %}</th>
-    <th colspan="{{ excuse_types.count|add:4 }}">{% trans "Absences" %}</th>
-    {% if excuse_types_not_absent %}
-      <th colspan="{{ excuse_types_not_absent.count }}">{% trans "Uncounted Absences" %}</th>
-    {% endif %}
-    {% if extra_marks %}
-      <th colspan="{{ extra_marks.count }}">{% trans "Extra marks" %}</th>
-    {% endif %}
-    <th rowspan="2">{% trans "Tardiness" %}</th>
-    <th rowspan="2"></th>
-  </tr>
-  <tr class="hide-on-large-only">
-    <th class="truncate">{% trans "Name" %}</th>
-    <th class="truncate">{% trans "Primary group" %}</th>
-    <th class="truncate chip-height">{% trans "Absences" %}</th>
-    <th class="chip-height">{% trans "Sum (e)" %}</th>
-    <th class="chip-height">{% trans "(e)" %}</th>
-    {% for excuse_type in excuse_types %}
-      <th class="chip-height">
-        ({{ excuse_type.short_name }})
-      </th>
-    {% endfor %}
-    <th class="chip-height">{% trans "(u)" %}</th>
-    {% for excuse_type in excuse_types_not_absent %}
-      <th class="chip-height">
-        ({{ excuse_type.short_name }})
-      </th>
-    {% endfor %}
-    {% for extra_mark in extra_marks %}
-      <th class="chip-height">
-        {{ extra_mark.short_name }}
-      </th>
-    {% endfor %}
-    <th class="truncate chip-height">{% trans "Tardiness" %}</th>
-    <th rowspan="2"></th>
-  </tr>
-  <tr class="hide-on-med-and-down">
-    <th>{% trans "Sum" %}</th>
-    <th>{% trans "Sum (e)" %}</th>
-    <th>{% trans "(e)" %}</th>
-    {% for excuse_type in excuse_types %}
-      <th>
-        ({{ excuse_type.short_name }})
-      </th>
-    {% endfor %}
-    <th>{% trans "(u)" %}</th>
-    {% for excuse_type in excuse_types_not_absent %}
-      <th>
-        ({{ excuse_type.short_name }})
-      </th>
-    {% endfor %}
-    {% for extra_mark in extra_marks %}
-      <th>
-        {{ extra_mark.short_name }}
-      </th>
-    {% endfor %}
-  </tr>
-  </thead>
-  {% for person in persons %}
-    <tr>
-      <td>
-        <a href="{% url "overview_person" person.pk %}">
-          {{ person }}
-        </a>
-      </td>
-      <td>
-        {% firstof person.primary_group  "–" %}
-      </td>
-      <td>
-        <span class="chip secondary-color white-text" title="{% trans "Absences" %}">
-          {{ person.absences_count }}
-        </span>
-      </td>
-      <td class="green-text">
-        <span class="chip green white-text" title="{% trans "Excused" %}">
-        {{ person.excused }}
-        </span>
-      </td>
-      <td>
-        <span class="chip grey white-text" title="{% trans "Regular excused" %}">
-          {{ person.excused_without_excuse_type }}
-        </span>
-      </td>
-      {% for excuse_type in excuse_types %}
-        <td>
-          <span class="chip grey white-text" title="{{ excuse_type.name }}">
-            {{ person|get_dict:excuse_type.count_label }}
-          </span>
-        </td>
-      {% endfor %}
-      <td class="red-text">
-        <span class="chip red white-text" title="{% trans "Unexcused" %}">
-        {{ person.unexcused }}
-        </span>
-      </td>
-      {% for excuse_type in excuse_types_not_absent %}
-        <td>
-          <span class="chip grey white-text" title="{{ excuse_type.name }}">
-            {{ person|get_dict:excuse_type.count_label }}
-          </span>
-        </td>
-      {% endfor %}
-      {% for extra_mark in extra_marks %}
-        <td>
-          <span class="chip grey white-text" title="{{ extra_mark.name }}">
-            {{ person|get_dict:extra_mark.count_label }}
-          </span>
-        </td>
-      {% endfor %}
-      <td>
-        <span class="chip orange white-text" title="{% trans "Tardiness" %}">
-          {% firstof person.tardiness|to_time|time:"H\h i\m"  "–" %}
-        </span>
-        <span class="chip orange white-text" title="{% trans "Count of tardiness" %}">{{ person.tardiness_count }} &times;</span>
-      </td>
-
-      <td>
-        <a class="btn primary waves-effect waves-light" href="{% url "overview_person" person.pk %}">
-          <i class="material-icons iconify left" data-icon="mdi:chart-box-outline"></i>
-          <span class="hide-on-med-and-down"> {% trans "Show more details" %}</span>
-          <span class="hide-on-large-only">{% trans "Details" %}</span>
-        </a>
-
-        {% has_perm "alsijil.register_absence_rule" user person as can_register_absence %}
-        {% if can_register_absence %}
-          <a class="btn primary-color waves-effect waves-light" href="{% url "register_absence" person.pk %}">
-            <i class="material-icons iconify left" data-icon="mdi:message-draw"></i>
-            {% trans "Register absence" %}
-          </a>
-        {% endif %}
-      </td>
-    </tr>
-  {% endfor %}
-{% endif %}
-</table>
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/register_coursebook.html b/aleksis/apps/alsijil/templates/alsijil/partials/register_coursebook.html
index eb18482185cd5614eca55b219c8a2302c8ceea59..01e05170dc38dd9168c4c73b97c293e6d6f3e0a1 100644
--- a/aleksis/apps/alsijil/templates/alsijil/partials/register_coursebook.html
+++ b/aleksis/apps/alsijil/templates/alsijil/partials/register_coursebook.html
@@ -15,24 +15,19 @@
   </tr>
   </thead>
   <tbody>
-    {% for day, documentations in group.documentations_by_day.items %}
-      {% for doc in documentations %}
-        <tr class="
-                {% if doc.amends %}
-                  {% if doc.amends.cancelled %}
-                    lesson-cancelled
-                  {% endif %}
-                  {% if doc.amends.amends %}
-                    lesson-substituted
-                  {% endif %}
+    {% for documentation in group.documentations_by_day %}
+      {% ifchanged documentation.datetime_start.date %}
+        <tr><th colspan="6">{{ documentation.datetime_start.date|date:"D" }}</th></tr>
+      {% endifchanged %}
+      <tr class="
+              {% if doc.amends %}
+                {% if doc.amends.cancelled %}
+                  lesson-cancelled
                 {% endif %}
                 {% if forloop.first %}
                   lessons-day-first
                 {% endif %}
               ">
-          {% if forloop.first %}
-            <th rowspan="{{ documentations|length }}">{{ day|date:"D" }}</th>
-          {% endif %}
           <td class="lesson-pe">
             {% if doc.amends %}
               {% if doc.amends.slot_number_start == doc.amends.slot_number_end %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/tardinesses.html b/aleksis/apps/alsijil/templates/alsijil/partials/tardinesses.html
deleted file mode 100644
index ca20a9c4d1f420736febd149e657a67e855d45f4..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/partials/tardinesses.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% load rules %}
-{% for note in notes %}
-  {% has_perm "alsijil.view_personalnote_rule" user note as can_view_personalnote %}
-  {% if can_view_personalnote %}
-    <span>{{ note.person }} ({{ note.tardiness }}'){% if not forloop.last %},{% endif %}</span>
-  {% endif %}
-{% endfor %}
diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py
index b03ac2cb3e4d63b581701466e1e541bc108ecd3d..8017db1c94d063930ed462be021846841bce3d40 100644
--- a/aleksis/apps/alsijil/urls.py
+++ b/aleksis/apps/alsijil/urls.py
@@ -3,11 +3,7 @@ from django.urls import path
 from . import views
 
 urlpatterns = [
-    path(
-        "print/groups/<path:ids>/",
-        views.full_register_for_group,
-        name="full_register_for_group"
-    ),
+    path("print/groups/<path:ids>/", views.full_register_for_group, name="full_register_for_group"),
     path("group_roles/", views.GroupRoleListView.as_view(), name="group_roles"),
     path("group_roles/create/", views.GroupRoleCreateView.as_view(), name="create_group_role"),
     path(
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index df6a8c40e7ac5f9b1614888f71d08767c015d7aa..c48c13eea04af8ab0c205ba851717a936cfe50a6 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -1,20 +1,19 @@
-from typing import Any, Dict, Optional
+from typing import Any, Dict
 
+from django.core.exceptions import BadRequest, PermissionDenied
 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.decorators.cache import never_cache
 from django.views.generic import DetailView
-from django.core.exceptions import PermissionDenied
 
 from django_tables2 import SingleTableView
 from reversion.views import RevisionMixin
-from rules.contrib.views import PermissionRequiredMixin, permission_required
+from rules.contrib.views import PermissionRequiredMixin
 
 from aleksis.core.decorators import pwa_cache
 from aleksis.core.mixins import (
@@ -26,7 +25,7 @@ from aleksis.core.mixins import (
 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 has_person, objectgetter_optional
+from aleksis.core.util.core_helpers import has_person
 
 from .forms import (
     AssignGroupRoleForm,
@@ -42,13 +41,15 @@ from .tasks import generate_full_register_printout
 
 def full_register_for_group(request: HttpRequest, ids: str) -> HttpResponse:
     """Show a configurable register printout as PDF for a group."""
+
     def parse_get_param(name):
         """Defaults to true"""
-        print(name)
-        print(request.GET.get(name))
-        return not request.GET.get(name) == "false"
+        return request.GET.get(name) != "false"
 
-    ids = [int(id_) for id_ in ids.split("/")]
+    try:
+        ids = [int(id_) for id_ in ids.split("/")]
+    except ValueError as e:
+        raise BadRequest() from e
 
     groups = []
     for id_ in ids:
@@ -72,7 +73,7 @@ def full_register_for_group(request: HttpRequest, ids: str) -> HttpResponse:
         include_members_table=parse_get_param("members_table"),
         include_teachers_and_subjects_table=parse_get_param("teachers_and_subjects_table"),
         include_person_overviews=parse_get_param("person_overviews"),
-        include_coursebook=parse_get_param("coursebook")
+        include_coursebook=parse_get_param("coursebook"),
     )
 
     back_url = request.GET.get("back", "")
@@ -80,7 +81,9 @@ def full_register_for_group(request: HttpRequest, ids: str) -> HttpResponse:
     return render_progress_page(
         request,
         result,
-        title=_(f"Generate register printout for {', '.join([group.short_name for group in groups])}"),
+        title=_(
+            f"Generate register printout for {', '.join([group.short_name for group in groups])}"
+        ),
         progress_title=_("Generate register printout …"),
         success_message=_("The printout has been generated successfully."),
         error_message=_("There was a problem while generating the printout."),