diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 6d85f5de4f6c23bd43956b2475f754c0578d4346..b17aefc91f99e61f970225347442c094e947153d 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -35,6 +35,8 @@ from aleksis.apps.chronos.mixins import WeekRelatedMixin
 from aleksis.apps.chronos.models import Event, ExtraLesson, LessonEvent, LessonPeriod, TimePeriod
 from aleksis.apps.chronos.util.format import format_m2m
 from aleksis.apps.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, SchoolTerm
@@ -451,11 +453,21 @@ class Documentation(CalendarEvent):
     # FIXME: DataCheck
 
     course = models.ForeignKey(
-        Course, models.CASCADE, related_name="documentations", blank=True, null=True, verbose_name=_("Course")
+        Course,
+        models.CASCADE,
+        related_name="documentations",
+        blank=True,
+        null=True,
+        verbose_name=_("Course"),
     )
 
     lesson_event = models.ForeignKey(
-        LessonEvent, models.CASCADE, related_name="documentation", blank=True, null=True, verbose_name=_("Lesson Event")
+        LessonEvent,
+        models.CASCADE,
+        related_name="documentation",
+        blank=True,
+        null=True,
+        verbose_name=_("Lesson Event"),
     )
 
     subject = models.ForeignKey(
@@ -499,26 +511,29 @@ class Documentation(CalendarEvent):
         ]
 
 
-class Participation(RegisterObjectRelatedMixin, ExtensibleModel):
-    """A personal note about a single person.
+class Participation(ExtensibleModel):
+    """A participation record about a single person.
 
-    Used in the class register to note participation and remarks about a student
+    Used in the class register to note participation of a student
     in a documented unit (e.g. a single lesson event or a custom time frame; see Documentation).
     """
 
     # FIXME: DataChecks
 
-    person = models.ForeignKey("core.Person", models.CASCADE, related_name="participations", verbose_name=_("Person"))
-    groups_of_person = models.ManyToManyField("core.Group", related_name="+", verbose_name=_("Groups of Person"))
+    person = models.ForeignKey(
+        "core.Person", models.CASCADE, related_name="participations", verbose_name=_("Person")
+    )
+    groups_of_person = models.ManyToManyField(
+        "core.Group", related_name="+", verbose_name=_("Groups of Person")
+    )
 
     documentation = models.ForeignKey(
-        Documentation, models.CASCADE, related_name="participations", verbose_name=_("Documentation")
+        Documentation,
+        models.CASCADE,
+        related_name="participations",
+        verbose_name=_("Documentation"),
     )
 
-    remarks = models.CharField(max_length=255, blank=True, verbose_name=_("Remarks"))
-
-    extra_marks = models.ManyToManyField("ExtraMark", blank=True, verbose_name=_("Extra Marks"))
-
     def __str__(self) -> str:
         return f"{self.documentation}, {self.person}"
 
@@ -538,6 +553,88 @@ class Participation(RegisterObjectRelatedMixin, ExtensibleModel):
         ]
 
 
+class Absence(ExtensibleModel):
+    """An absence record about a single person.
+
+    Used in the class register to note absence of a student
+    in a documented unit (e.g. a single lesson event or a custom time frame; see Documentation).
+    """
+
+    # FIXME: DataChecks
+
+    reason = models.ForeignKey(
+        AbsenceReason, verbose_name=_("Absence Reason"), on_delete=models.CASCADE
+    )
+
+    person = models.ForeignKey(
+        "core.Person", models.CASCADE, related_name="lesson_absences", verbose_name=_("Person")
+    )
+    groups_of_person = models.ManyToManyField(
+        "core.Group", related_name="+", verbose_name=_("Groups of Person")
+    )
+
+    documentation = models.ForeignKey(
+        Documentation, models.CASCADE, related_name="absences", verbose_name=_("Documentation")
+    )
+
+    excused = models.BooleanField(default=False, verbose_name=_("Excused"))
+
+    base_absence = models.ForeignKey(
+        KolegoAbsence, models.SET_NULL, related_name="absences", verbose_name=_("Base Absence")
+    )
+
+    def __str__(self) -> str:
+        return f"{self.documentation}, {self.person}"
+
+    class Meta:
+        verbose_name = _("Absence note")
+        verbose_name_plural = _("Absence notes")
+        ordering = [
+            "documentation",
+            "person__last_name",
+            "person__first_name",
+        ]
+        constraints = [
+            models.UniqueConstraint(
+                fields=("documentation", "person"),
+                name="unique_absence_per_documentation",
+            ),
+        ]
+
+
+class NewPersonalNote(ExtensibleModel):
+    person = models.ForeignKey(
+        "core.Person", models.CASCADE, related_name="new_personal_notes", verbose_name=_("Person")
+    )
+
+    documentation = models.ForeignKey(
+        Documentation,
+        models.CASCADE,
+        related_name="personal_notes",
+        verbose_name=_("Documentation"),
+        blank=True,
+        null=True,
+    )
+
+    note = models.TextField(blank=True, verbose_name=_("Note"))
+    extra_mark = models.ForeignKey(
+        ExtraMark, on_delete=models.CASCADE, blank=True, null=True, verbose_name=_("Extra Mark")
+    )
+
+    def __str__(self) -> str:
+        return f"{self.person}, {self.note}, {self.extra_mark}"
+
+    class Meta:
+        verbose_name = _("Personal Note")
+        verbose_name_plural = _("Personal Notes")
+        constraints = [
+            models.CheckConstraint(
+                check=~Q(note="") | Q(extra_mark__isnull=False),
+                name="unique_absence_per_documentation",
+            ),
+        ]
+
+
 class GroupRole(ExtensibleModel):
     data_checks = [field_validation_data_check_factory("alsijil", "GroupRole", "icon")]
 
diff --git a/aleksis/apps/alsijil/rules.py b/aleksis/apps/alsijil/rules.py
index 94fd3aebc1e627fd8654c4bd7f46e7c1ce901ea8..fc4cdfe6e791482e22026f843192d0a267976a3f 100644
--- a/aleksis/apps/alsijil/rules.py
+++ b/aleksis/apps/alsijil/rules.py
@@ -356,14 +356,12 @@ view_register_objects_list_predicate = has_person & (
 add_perm("alsijil.view_register_objects_list_rule", view_register_objects_list_predicate)
 
 view_documentation_predicate = has_person & (
-    has_global_perm("alsijil.view_documentation")
-    | can_view_documentation
+    has_global_perm("alsijil.view_documentation") | can_view_documentation
 )
 add_perm("alsijil.view_documentation_rule", view_documentation_predicate)
 
 edit_documentation_predicate = has_person & (
-    has_global_perm("alsijil.change_documentation")
-    | can_edit_documentation
+    has_global_perm("alsijil.change_documentation") | can_edit_documentation
 )
 add_perm("alsijil.edit_documentation_rule", edit_documentation_predicate)
 add_perm("alsijil.delete_documentation_rule", edit_documentation_predicate)
diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py
index 26cd82507b81a0fff831d23c210cc5f71852be97..b442c7b6e980587a67bd07e3171d5315b58a0c26 100644
--- a/aleksis/apps/alsijil/schema/__init__.py
+++ b/aleksis/apps/alsijil/schema/__init__.py
@@ -1,26 +1,30 @@
+from django.db.models.query_utils import Q
+
 import graphene
 from graphene_django import DjangoObjectType
 
-from django.db.models.query_utils import Q
-
 from aleksis.core.schema.base import DjangoFilterMixin, FilterOrderList
 
 from ..models import Documentation, Participation
 from .documentation import (
-    DocumentationType,
-    DocumentationCreateMutation,
     DocumentationBatchCreateMutation,
+    DocumentationBatchPatchMutation,
+    DocumentationCreateMutation,
     DocumentationDeleteMutation,
-    DocumentationBatchPatchMutation
+    DocumentationType,
 )
 
 
 class Query(graphene.ObjectType):
     documentations = FilterOrderList(DocumentationType)
-    documentations_by_course_id = FilterOrderList(DocumentationType, course_id=graphene.ID(required=True))
+    documentations_by_course_id = FilterOrderList(
+        DocumentationType, course_id=graphene.ID(required=True)
+    )
 
     def resolve_documentations_by_course_id(root, info, course_id, **kwargs):
-        documentations = Documentation.objects.filter(Q(course__pk=course_id) | Q(lesson_event__course__pk=course_id))
+        documentations = Documentation.objects.filter(
+            Q(course__pk=course_id) | Q(lesson_event__course__pk=course_id)
+        )
         return documentations
 
 
diff --git a/aleksis/apps/alsijil/schema/documentation.py b/aleksis/apps/alsijil/schema/documentation.py
index d8d5911a24bed3a32ce88a48df9276630e4dadf5..06ef6b26e94e007363061f5dbb1eb46d5a55b107 100644
--- a/aleksis/apps/alsijil/schema/documentation.py
+++ b/aleksis/apps/alsijil/schema/documentation.py
@@ -24,7 +24,19 @@ from ..models import Documentation
 class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType):
     class Meta:
         model = Documentation
-        fields = ("id", "course", "lesson_event", "subject", "topic", "homework", "group_note", "datetime_start", "datetime_end", "date_start", "date_end")
+        fields = (
+            "id",
+            "course",
+            "lesson_event",
+            "subject",
+            "topic",
+            "homework",
+            "group_note",
+            "datetime_start",
+            "datetime_end",
+            "date_start",
+            "date_end",
+        )
         filter_fields = {
             "id": ["exact", "lte", "gte"],
             "course__name": ["exact"],
@@ -38,15 +50,49 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
 class DocumentationCreateMutation(DjangoCreateMutation):
     class Meta:
         model = Documentation
-        fields = ("course", "lesson_event", "subject", "topic", "homework", "group_note", "datetime_start", "datetime_end", "date_start", "date_end")
-        optional_fields = ("course", "lesson_event", "subject", "topic", "homework", "group_note", "datetime_start", "datetime_end", "date_start", "date_end")
+        fields = (
+            "course",
+            "lesson_event",
+            "subject",
+            "topic",
+            "homework",
+            "group_note",
+            "datetime_start",
+            "datetime_end",
+            "date_start",
+            "date_end",
+        )
+        optional_fields = (
+            "course",
+            "lesson_event",
+            "subject",
+            "topic",
+            "homework",
+            "group_note",
+            "datetime_start",
+            "datetime_end",
+            "date_start",
+            "date_end",
+        )
         permissions = ("alsijil.add_documentation",)  # FIXME
 
 
 class DocumentationBatchCreateMutation(DjangoBatchCreateMutation):
     class Meta:
         model = Documentation
-        fields = ("id", "course", "lesson_event", "subject", "topic", "homework", "group_note", "datetime_start", "datetime_end", "date_start", "date_end")
+        fields = (
+            "id",
+            "course",
+            "lesson_event",
+            "subject",
+            "topic",
+            "homework",
+            "group_note",
+            "datetime_start",
+            "datetime_end",
+            "date_start",
+            "date_end",
+        )
         permissions = ("alsijil.add_documentation",)  # FIXME
 
 
@@ -58,5 +104,17 @@ class DocumentationDeleteMutation(DeleteMutation):
 class DocumentationBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation):
     class Meta:
         model = Documentation
-        fields = ("id", "course", "lesson_event", "subject", "topic", "homework", "group_note", "datetime_start", "datetime_end", "date_start", "date_end")
+        fields = (
+            "id",
+            "course",
+            "lesson_event",
+            "subject",
+            "topic",
+            "homework",
+            "group_note",
+            "datetime_start",
+            "datetime_end",
+            "date_start",
+            "date_end",
+        )
         permissions = ("alsijil.edit_documentation_rule",)  # FIXME
diff --git a/aleksis/apps/alsijil/util/predicates.py b/aleksis/apps/alsijil/util/predicates.py
index b261173ab2e799a91306fd94e1a420c0c49d56f9..0459a96b8c820c6c439b59349cc065745b84d4f9 100644
--- a/aleksis/apps/alsijil/util/predicates.py
+++ b/aleksis/apps/alsijil/util/predicates.py
@@ -312,10 +312,7 @@ def is_lesson_event_teacher(user: User, obj: LessonEvent):
     or a teacher of the course, if the lesson event has one.
     """
     if obj:
-        return (
-            obj.course and is_course_teacher(user, obj)
-            or user.person in obj.all_teachers()
-        )
+        return obj.course and is_course_teacher(user, obj) or user.person in obj.all_teachers()
     return False
 
 
@@ -355,7 +352,9 @@ def can_view_documentation(user: User, obj: Documentation):
         if obj.course:
             return is_course_teacher(user, obj.course) | is_course_member(user, obj.course)
         if obj.lesson_event:
-            return is_lesson_event_teacher(user, obj.course) | is_lesson_event_member(user, obj.course)
+            return is_lesson_event_teacher(user, obj.course) | is_lesson_event_member(
+                user, obj.course
+            )
     return False