diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d58e466b99296765d1b74fb19eb350e5e67b15ea..6e535409dd609de46245d7f9210ec440be8bc59c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,28 @@ All notable changes to this project will be documented in this file.
 The format is based on `Keep a Changelog`_,
 and this project adheres to `Semantic Versioning`_.
 
+`4.0.0.dev3`_ - 2024-07-10
+--------------------------
+
+Added
+~~~~~
+
+* Support for entering personal notes for students in the new coursebook interface.
+* Support for entering tardiness for students in the new coursebook interface.
+
+`4.0.0.dev2`_ - 2024-07-13
+--------------------------
+
+Fixed version of 4.0.0.dev1
+
+`4.0.0.dev1`_ - 2024-06-13
+--------------------------
+
+Added
+~~~~~
+
+* Support for entering absences for students in the new coursebook interface.
+
 `4.0.0.dev0`_ - 2024-04-23
 --------------------------
 
@@ -33,6 +55,14 @@ Fixed
 
 * Migrating failed due to an incorrect field reference.
 
+`3.0.1`_ - 2023-09-02
+-------------------
+
+Fixed
+~~~~~
+
+* Migrations failed on empty database
+
 `3.0`_ - 2023-05-15
 -------------------
 
@@ -343,4 +373,8 @@ Fixed
 .. _2.1.1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/2.1.1
 .. _3.0b0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/3.0b0
 .. _3.0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/3.0
+.. _3.0.1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/3.0.1
 .. _4.0.0.dev0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/4.0.0.dev0
+.. _4.0.0.dev1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/4.0.0.dev1
+.. _4.0.0.dev2: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/4.0.0.dev2
+.. _4.0.0.dev3: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/4.0.0.dev3
diff --git a/README.rst b/README.rst
index 61e80f73c24aa9077218aa86a2427463f75f1f0f..53fbc493f0f1acfd35a0f893c816cfb117a27572 100644
--- a/README.rst
+++ b/README.rst
@@ -34,10 +34,11 @@ Licence
   Copyright © 2019, 2021 Dominik George <dominik.george@teckids.org>
   Copyright © 2019, 2020 Tom Teichler <tom.teichler@teckids.org>
   Copyright © 2019 mirabilos <thorsten.glaser@teckids.org>
-  Copyright © 2020, 2021, 2022 Jonathan Weth <dev@jonathanweth.de>
-  Copyright © 2020, 2021 Julian Leucker <leuckeju@katharineum.de>
-  Copyright © 2020, 2022 Hangzhi Yu <yuha@katharineum.de>
+  Copyright © 2020, 2021, 2022, 2024 Jonathan Weth <dev@jonathanweth.de>
+  Copyright © 2020, 2021, 2024 Julian Leucker <leuckeju@katharineum.de>
+  Copyright © 2020, 2022, 2023, 2024 Hangzhi Yu <yuha@katharineum.de>
   Copyright © 2021 Lloyd Meins <meinsll@katharineum.de>
+  Copyright © 2024 Michael Bauer <michael-bauer@posteo.de>
 
 
   Licenced under the EUPL, version 1.2 or later, by Teckids e.V. (Bonn, Germany).
diff --git a/aleksis/apps/alsijil/apps.py b/aleksis/apps/alsijil/apps.py
index b523b38afa08c965628afbfbe61327e8b03f7067..c52e13fde3532817e6b427355978080e3b856ea1 100644
--- a/aleksis/apps/alsijil/apps.py
+++ b/aleksis/apps/alsijil/apps.py
@@ -13,8 +13,22 @@ class AlsijilConfig(AppConfig):
         ([2019, 2021], "Dominik George", "dominik.george@teckids.org"),
         ([2019, 2020], "Tom Teichler", "tom.teichler@teckids.org"),
         ([2019], "mirabilos", "thorsten.glaser@teckids.org"),
-        ([2020, 2021, 2022], "Jonathan Weth", "dev@jonathanweth.de"),
-        ([2020, 2021], "Julian Leucker", "leuckeju@katharineum.de"),
-        ([2020, 2022], "Hangzhi Yu", "yuha@katharineum.de"),
+        ([2020, 2021, 2022, 2024], "Jonathan Weth", "dev@jonathanweth.de"),
+        ([2020, 2021, 2024], "Julian Leucker", "leuckeju@katharineum.de"),
+        ([2020, 2022, 2023, 2024], "Hangzhi Yu", "yuha@katharineum.de"),
         ([2021], "Lloyd Meins", "meinsll@katharineum.de"),
+        ([2024], "Michael Bauer", "michael-bauer@posteo.de"),
     )
+
+    def post_migrate(
+        self,
+        app_config: AppConfig,
+        verbosity: int,
+        interactive: bool,
+        using: str,
+        **kwargs,
+    ) -> None:
+        super().post_migrate(app_config, verbosity, interactive, using, **kwargs)
+        from .util.alsijil_helpers import get_absence_reason_tag
+
+        get_absence_reason_tag()
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
index 4e673db88090f473f2bc036bd128881327d21fc3..21b903a2fd2894249dcd2398518c4e6f9bab96e8 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
@@ -1,89 +1,108 @@
 <template>
-  <c-r-u-d-iterator
-    i18n-key="alsijil.coursebook"
-    :gql-query="gqlQuery"
-    :gql-additional-query-args="gqlQueryArgs"
-    :enable-create="false"
-    :enable-edit="false"
-    :elevated="false"
-    @lastQuery="lastQuery = $event"
-    ref="iterator"
-    fixed-header
-    disable-pagination
-    hide-default-footer
-    use-deep-search
-  >
-    <template #additionalActions="{ attrs, on }">
-      <coursebook-filters v-model="filters" />
-    </template>
-    <template #default="{ items }">
-      <coursebook-loader />
-      <coursebook-day
-        v-for="{ date, docs, first, last } in groupDocsByDay(items)"
-        v-intersect="{
-          handler: intersectHandler(date, first, last),
-          options: {
-            rootMargin: '-' + topMargin + 'px 0px 0px 0px',
-            threshold: [0, 1],
-          },
-        }"
-        :date="date"
-        :docs="docs"
-        :lastQuery="lastQuery"
-        :focus-on-mount="initDate && initDate.toMillis() === date.toMillis()"
-        @init="transition"
-        :key="'day-' + date"
-        ref="days"
-      />
-      <coursebook-loader />
+  <div>
+    <infinite-scrolling-date-sorted-c-r-u-d-iterator
+      i18n-key="alsijil.coursebook"
+      :gql-query="gqlQuery"
+      :gql-additional-query-args="gqlQueryArgs"
+      :enable-create="false"
+      :enable-edit="false"
+      :elevated="false"
+      @lastQuery="lastQuery = $event"
+      ref="iterator"
+      fixed-header
+      disable-pagination
+      hide-default-footer
+      use-deep-search
+    >
+      <template #additionalActions="{ attrs, on }">
+        <coursebook-filters :page-type="pageType" v-model="filters" />
+        <v-expand-transition>
+          <v-card
+            outlined
+            class="full-width"
+            v-show="
+              pageType === 'absences' && combinedSelectedParticipations.length
+            "
+          >
+            <v-card-text>
+              <v-row align="center">
+                <v-col cols="6">
+                  {{
+                    $tc(
+                      "alsijil.coursebook.absences.action_for_selected",
+                      combinedSelectedParticipations.length,
+                    )
+                  }}
+                </v-col>
+                <v-col cols="6">
+                  <absence-reason-buttons
+                    allow-empty
+                    empty-value="present"
+                    :custom-absence-reasons="absenceReasons"
+                    @input="handleMultipleAction"
+                  />
+                </v-col>
+              </v-row>
+            </v-card-text>
+          </v-card>
+        </v-expand-transition>
+      </template>
 
-      <date-select-footer
-        :value="currentDate"
-        @input="gotoDate"
-        @prev="gotoPrev"
-        @next="gotoNext"
-      />
-    </template>
-    <template #loading>
-      <coursebook-loader :number-of-days="10" :number-of-docs="5" />
-    </template>
+      <template #item="{ item, lastQuery }">
+        <component
+          :is="itemComponent"
+          :extra-marks="extraMarks"
+          :absence-reasons="absenceReasons"
+          :subjects="subjects"
+          :documentation="item"
+          :affected-query="lastQuery"
+          :value="(selectedParticipations[item.id] ??= [])"
+          @input="selectParticipation(item.id, $event)"
+        />
+      </template>
 
-    <template #no-data>
-      <CoursebookEmptyMessage icon="mdi-book-off-outline">
-        {{ $t("alsijil.coursebook.no_data") }}
-      </CoursebookEmptyMessage>
-    </template>
+      <template #loading>
+        <coursebook-loader :number-of-days="10" :number-of-docs="5" />
+      </template>
 
-    <template #no-results>
-      <CoursebookEmptyMessage icon="mdi-book-alert-outline">
-        {{
-          $t("alsijil.coursebook.no_results", { search: $refs.iterator.search })
-        }}
-      </CoursebookEmptyMessage>
-    </template>
-  </c-r-u-d-iterator>
+      <template #itemLoader>
+        <DocumentationLoader />
+      </template>
+    </infinite-scrolling-date-sorted-c-r-u-d-iterator>
+    <v-scale-transition>
+      <absence-creation-dialog v-if="pageType === 'absences'" />
+    </v-scale-transition>
+  </div>
 </template>
 
 <script>
-import CRUDIterator from "aleksis.core/components/generic/CRUDIterator.vue";
-import DateSelectFooter from "aleksis.core/components/generic/DateSelectFooter.vue";
-import CoursebookDay from "./CoursebookDay.vue";
-import { DateTime, Interval } from "luxon";
+import InfiniteScrollingDateSortedCRUDIterator from "aleksis.core/components/generic/InfiniteScrollingDateSortedCRUDIterator.vue";
 import { documentationsForCoursebook } from "./coursebook.graphql";
+import AbsenceReasonButtons from "aleksis.apps.kolego/components/AbsenceReasonButtons.vue";
 import CoursebookFilters from "./CoursebookFilters.vue";
 import CoursebookLoader from "./CoursebookLoader.vue";
-import CoursebookEmptyMessage from "./CoursebookEmptyMessage.vue";
+import DocumentationModal from "./documentation/DocumentationModal.vue";
+import DocumentationAbsencesModal from "./absences/DocumentationAbsencesModal.vue";
+import AbsenceCreationDialog from "./absences/AbsenceCreationDialog.vue";
+import { extraMarks } from "../extra_marks/extra_marks.graphql";
+import DocumentationLoader from "./documentation/DocumentationLoader.vue";
+import sendToServerMixin from "./absences/sendToServerMixin";
+import { absenceReasons } from "./absences/absenceReasons.graphql";
+import { subjects } from "aleksis.apps.cursus/components/subject.graphql";
 
 export default {
   name: "Coursebook",
   components: {
-    CoursebookEmptyMessage,
+    DocumentationLoader,
+    AbsenceReasonButtons,
     CoursebookFilters,
     CoursebookLoader,
-    CRUDIterator,
-    DateSelectFooter,
-    CoursebookDay,
+    DocumentationModal,
+    DocumentationAbsencesModal,
+    InfiniteScrollingDateSortedCRUDIterator,
+    AbsenceCreationDialog,
   },
+  mixins: [sendToServerMixin],
   props: {
     filterType: {
       type: String,
@@ -99,6 +118,11 @@ export default {
       required: false,
       default: null,
     },
+    pageType: {
+      type: String,
+      required: false,
+      default: "documentations",
+    },
     /**
      * Number of consecutive to load at once
      * This number of days is initially loaded and loaded
@@ -128,12 +152,31 @@ export default {
       groups: [],
       courses: [],
       incomplete: false,
+      absencesExist: true,
       ready: false,
       initDate: false,
       currentDate: "",
       hashUpdater: false,
+      extraMarks: [],
+      absenceReasons: [],
+      subjects: [],
+      selectedParticipations: {},
     };
   },
+  apollo: {
+    extraMarks: {
+      query: extraMarks,
+      update: (data) => data.items,
+    },
+    absenceReasons: {
+      query: absenceReasons,
+      update: (data) => data.items,
+    },
+    subjects: {
+      query: subjects,
+      update: (data) => data.items,
+    },
+  },
   computed: {
     // Assertion: Should only fire on page load or selection change.
     //            Resets date range.
@@ -145,6 +188,7 @@ export default {
         dateStart: this.dateStart,
         dateEnd: this.dateEnd,
         incomplete: !!this.incomplete,
+        absencesExist: !!this.absencesExist && this.pageType === "absences",
       };
     },
     filters: {
@@ -154,15 +198,20 @@ export default {
           objId: this.objId,
           filterType: this.filterType,
           incomplete: this.incomplete,
+          pageType: this.pageType,
+          absencesExist: this.absencesExist,
         };
       },
       set(selectedFilters) {
         if (Object.hasOwn(selectedFilters, "incomplete")) {
           this.incomplete = selectedFilters.incomplete;
+        } else if (Object.hasOwn(selectedFilters, "absencesExist")) {
+          this.absencesExist = selectedFilters.absencesExist;
         } else if (
           Object.hasOwn(selectedFilters, "filterType") ||
           Object.hasOwn(selectedFilters, "objId") ||
-          Object.hasOwn(selectedFilters, "objType")
+          Object.hasOwn(selectedFilters, "objType") ||
+          Object.hasOwn(selectedFilters, "pageType")
         ) {
           this.$router.push({
             name: "alsijil.coursebook",
@@ -172,194 +221,65 @@ export default {
                 : this.filterType,
               objType: selectedFilters.objType,
               objId: selectedFilters.objId,
+              pageType: selectedFilters.pageType
+                ? selectedFilters.pageType
+                : this.pageType,
             },
             hash: this.$route.hash,
           });
           // computed should not have side effects
           // but this was actually done before filters was refactored into
           // its own component
-          this.resetDate();
+          this.$refs.iterator.resetDate();
           // might skip query until both set = atomic
-        }
-      },
-    },
-  },
-  methods: {
-    resetDate(toDate) {
-      // Assure current date
-      console.log("Resetting date range", this.$route.hash);
-      this.currentDate = toDate || this.$route.hash?.substring(1);
-      if (!this.currentDate) {
-        console.log("Set default date");
-        this.setDate(DateTime.now().toISODate());
-      }
-
-      const date = DateTime.fromISO(this.currentDate);
-      this.initDate = date;
-      this.dateStart = date.minus({ days: this.dayIncrement }).toISODate();
-      this.dateEnd = date.plus({ days: this.dayIncrement }).toISODate();
-    },
-    transition() {
-      this.initDate = false;
-      this.ready = true;
-    },
-    groupDocsByDay(docs) {
-      // => {dt: {date: dt, docs: doc ...} ...}
-      const docsByDay = docs.reduce((byDay, doc) => {
-        // This works with dummy. Does actual doc have dateStart instead?
-        const day = DateTime.fromISO(doc.datetimeStart).startOf("day");
-        byDay[day] ??= { date: day, docs: [] };
-        byDay[day].docs.push(doc);
-        return byDay;
-      }, {});
-      // => [{date: dt, docs: doc ..., idx: idx, lastIdx: last-idx} ...]
-      // sorting is necessary since backend can send docs unordered
-      return Object.keys(docsByDay)
-        .sort()
-        .map((key, idx, { length }) => {
-          const day = docsByDay[key];
-          day.first = idx === 0;
-          const lastIdx = length - 1;
-          day.last = idx === lastIdx;
-          return day;
-        });
-    },
-    // docsByDay: {dt: [dt doc ...] ...}
-    fetchMore(from, to, then) {
-      console.log("fetching", from, to);
-      this.lastQuery.fetchMore({
-        variables: {
-          dateStart: from,
-          dateEnd: to,
-        },
-        // Transform the previous result with new data
-        updateQuery: (previousResult, { fetchMoreResult }) => {
-          console.log("Received more");
-          then();
-          return { items: previousResult.items.concat(fetchMoreResult.items) };
-        },
-      });
-    },
-    setDate(date) {
-      this.currentDate = date;
-      if (!this.hashUpdater) {
-        this.hashUpdater = window.requestIdleCallback(() => {
-          if (!(this.$route.hash.substring(1) === this.currentDate)) {
-            this.$router.replace({ hash: this.currentDate });
-          }
-          this.hashUpdater = false;
-        });
-      }
-    },
-    fixScrollPos(height, top) {
-      this.$nextTick(() => {
-        if (height < document.documentElement.scrollHeight) {
-          document.documentElement.scrollTop =
-            document.documentElement.scrollHeight - height + top;
-          this.ready = true;
-        } else {
-          // Update top, could have changed in the meantime.
-          this.fixScrollPos(height, document.documentElement.scrollTop);
-        }
-      });
-    },
-    intersectHandler(date, first, last) {
-      let once = true;
-      return (entries) => {
-        const entry = entries[0];
-        if (entry.isIntersecting) {
-          if (entry.boundingClientRect.top <= this.topMargin || first) {
-            console.log("@ ", date.toISODate());
-            this.setDate(date.toISODate());
-          }
-
-          if (once && this.ready && first) {
-            console.log("load up", date.toISODate());
-            this.ready = false;
-            this.fetchMore(
-              date.minus({ days: this.dayIncrement }).toISODate(),
-              date.minus({ days: 1 }).toISODate(),
-              () => {
-                this.fixScrollPos(
-                  document.documentElement.scrollHeight,
-                  document.documentElement.scrollTop,
-                );
-              },
+          if (Object.hasOwn(selectedFilters, "pageType")) {
+            this.absencesExist = true;
+            this.$setToolBarTitle(
+              this.$t(`alsijil.coursebook.title_${selectedFilters.pageType}`),
+              null,
             );
-            once = false;
-          } else if (once && this.ready && last) {
-            console.log("load down", date.toISODate());
-            this.ready = false;
-            this.fetchMore(
-              date.plus({ days: 1 }).toISODate(),
-              date.plus({ days: this.dayIncrement }).toISODate(),
-              () => {
-                this.ready = true;
-              },
-            );
-            once = false;
           }
         }
-      };
+      },
     },
-    // Improve me?
-    // The navigation logic could be a bit simpler if the current days
-    // where known as a sorted array (= result of groupDocsByDay) But
-    // then the list would need its own component and this gets rather
-    // complicated. Then the calendar could also show the present days
-    // / gray out the missing.
-    //
-    // Next two: arg date is ts object
-    findPrev(date) {
-      return this.$refs.days
-        .map((day) => day.date)
-        .sort()
-        .reverse()
-        .find((date2) => date2 < date);
+    itemComponent() {
+      if (this.pageType === "documentations") {
+        return "DocumentationModal";
+      } else {
+        return "DocumentationAbsencesModal";
+      }
     },
-    findNext(date) {
-      return this.$refs.days
-        .map((day) => day.date)
-        .sort()
-        .find((date2) => date2 > date);
+    combinedSelectedParticipations() {
+      return Object.values(this.selectedParticipations).flat();
     },
-    gotoDate(date) {
-      const present = this.$refs.days.find(
-        (day) => day.date.toISODate() === date,
+  },
+  methods: {
+    selectParticipation(id, value) {
+      this.selectedParticipations = Object.assign(
+        {},
+        this.selectedParticipations,
+        { [id]: value },
       );
-
-      if (present) {
-        // React immediatly -> smoother navigation
-        // Also intersect handler does not always react to scrollIntoView
-        this.setDate(date);
-        present.focus("smooth");
-      } else {
-        const prev = this.findPrev(DateTime.fromISO(date));
-        const next = this.findNext(DateTime.fromISO(date));
-        if (prev && next) {
-          // In between two present days -> goto prev
-          this.gotoDate(prev.toISODate());
-        } else {
-          // Outsite present day range
-          this.resetDate(date);
-        }
-      }
     },
-    gotoPrev() {
-      const prev = this.findPrev(DateTime.fromISO(this.currentDate));
-      if (prev) {
-        this.gotoDate(prev.toISODate());
-      }
+    handleMultipleAction(absenceReasonId) {
+      this.loadSelectedParticiptions = true;
+      this.sendToServer(
+        this.combinedSelectedParticipations,
+        "absenceReason",
+        absenceReasonId,
+      );
+      this.$once("save", this.resetMultipleAction);
     },
-    gotoNext() {
-      const next = this.findNext(DateTime.fromISO(this.currentDate));
-      if (next) {
-        this.gotoDate(next.toISODate());
-      }
+    resetMultipleAction() {
+      this.loadSelectedParticiptions = false;
+      this.selectedParticipations = {};
     },
   },
-  created() {
-    this.resetDate();
+  mounted() {
+    this.$setToolBarTitle(
+      this.$t(`alsijil.coursebook.title_${this.pageType}`),
+      null,
+    );
   },
 };
 </script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookDay.vue b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookDay.vue
deleted file mode 100644
index 2f15c3fd031c62ff9fc96cf5c35042f2274d3321..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookDay.vue
+++ /dev/null
@@ -1,67 +0,0 @@
-<template>
-  <v-list-item :style="{ scrollMarginTop: '145px' }" two-line class="px-0">
-    <v-list-item-content>
-      <v-subheader class="text-h6 px-1">{{
-        $d(date, "dateWithWeekday")
-      }}</v-subheader>
-      <v-list max-width="100%" class="pt-0 mt-n1">
-        <v-list-item
-          v-for="doc in docs"
-          :key="'documentation-' + (doc.oldId || doc.id)"
-          class="px-1"
-        >
-          <documentation-modal
-            :documentation="doc"
-            :affected-query="lastQuery"
-          />
-        </v-list-item>
-      </v-list>
-    </v-list-item-content>
-  </v-list-item>
-</template>
-
-<script>
-import DocumentationModal from "./documentation/DocumentationModal.vue";
-export default {
-  name: "CoursebookDay",
-  components: {
-    DocumentationModal,
-  },
-  props: {
-    date: {
-      type: Object,
-      required: true,
-    },
-    docs: {
-      type: Array,
-      required: true,
-    },
-    lastQuery: {
-      type: Object,
-      required: true,
-    },
-    focusOnMount: {
-      type: Boolean,
-      required: false,
-      default: false,
-    },
-  },
-  emits: ["init"],
-  methods: {
-    focus(how) {
-      this.$el.scrollIntoView({
-        behavior: how,
-        block: "start",
-        inline: "nearest",
-      });
-      console.log("focused @", this.date.toISODate());
-    },
-  },
-  mounted() {
-    if (this.focusOnMount) {
-      this.$nextTick(this.focus("instant"));
-      this.$emit("init");
-    }
-  },
-};
-</script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookEmptyMessage.vue b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookEmptyMessage.vue
deleted file mode 100644
index 346ecc63c272ab5c57a1da9f5e5f78b825739a79..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookEmptyMessage.vue
+++ /dev/null
@@ -1,25 +0,0 @@
-<template>
-  <v-list-item>
-    <v-list-item-content
-      class="d-flex justify-center align-center flex-column full-width"
-    >
-      <div class="mb-4">
-        <v-icon large color="primary">{{ icon }}</v-icon>
-      </div>
-      <v-list-item-title>
-        <slot></slot>
-      </v-list-item-title>
-    </v-list-item-content>
-  </v-list-item>
-</template>
-<script>
-export default {
-  name: "CoursebookEmptyMessage",
-  props: {
-    icon: {
-      type: String,
-      default: "mdi-book-alert-outline",
-    },
-  },
-};
-</script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue
index b47ebbfbb1d1cd8219686f05085eb0eeb8f453d3..6b9dcc92fb433ff320f0c76790656c7b76c0472f 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue
@@ -1,8 +1,11 @@
 <template>
-  <div class="d-flex flex-grow-1 justify-end">
+  <div
+    class="d-flex flex-column flex-sm-row flex-nowrap flex-grow-1 justify-end gap align-stretch"
+  >
     <v-autocomplete
       :items="selectable"
       item-text="name"
+      :item-value="(item) => `${item.__typename}-${item.id}`"
       clearable
       return-object
       filled
@@ -15,7 +18,7 @@
       @click:clear="selectObject"
       class="max-width"
     />
-    <div class="ml-6">
+    <div class="mx-6">
       <v-switch
         :loading="selectLoading"
         :label="$t('alsijil.coursebook.filter.own')"
@@ -38,7 +41,29 @@
         inset
         hide-details
       />
+      <v-switch
+        v-if="pageType === 'absences'"
+        :loading="selectLoading"
+        :label="$t('alsijil.coursebook.filter.absences_exist')"
+        :input-value="value.absencesExist"
+        @change="
+          $emit('input', {
+            absencesExist: $event,
+          })
+        "
+        dense
+        inset
+        hide-details
+      />
     </div>
+    <v-btn
+      outlined
+      color="primary"
+      :loading="selectLoading"
+      @click="togglePageType()"
+    >
+      {{ pageTypeButtonText }}
+    </v-btn>
   </div>
 </template>
 
@@ -63,6 +88,11 @@ export default {
       type: Object,
       required: true,
     },
+    pageType: {
+      type: String,
+      required: false,
+      default: "documentations",
+    },
   },
   emits: ["input"],
   apollo: {
@@ -95,6 +125,13 @@ export default {
           o.id === this.value.objId,
       );
     },
+    pageTypeButtonText() {
+      if (this.value.pageType === "documentations") {
+        return this.$t("alsijil.coursebook.filter.page_type.absences");
+      } else {
+        return this.$t("alsijil.coursebook.filter.page_type.documentations");
+      }
+    },
   },
   methods: {
     selectObject(selection) {
@@ -110,6 +147,16 @@ export default {
         objId: this.value.objId,
       });
     },
+    togglePageType() {
+      this.$emit("input", {
+        pageType:
+          this.value.pageType === "documentations"
+            ? "absences"
+            : "documentations",
+        objType: this.value.objType,
+        objId: this.value.objId,
+      });
+    },
   },
 };
 </script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue
index 51ac72d2873661f5247dfbb197f547dc5f73f5ff..3263225dc890242ed038f8b5f58153278e5aea64 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue
@@ -1,30 +1,79 @@
 <template>
-  <mobile-fullscreen-dialog v-model="popup">
+  <mobile-fullscreen-dialog v-model="popup" persistent :close-button="false">
     <template #activator="activator">
-      <!-- button +? -->
-      <!-- -> popup = true -->
+      <fab-button
+        color="secondary"
+        @click="popup = true"
+        :disabled="popup"
+        :class="{
+          'd-none': !checkPermission('alsijil.view_register_absence_rule'),
+        }"
+        icon-text="$plus"
+        i18n-key="alsijil.coursebook.absences.button"
+      >
+        <v-icon>$plus</v-icon>
+      </fab-button>
     </template>
     <template #title>
-      <!-- Abwesenheit/Entschuldigung erfassen -->
-      <!-- Abwesenheit/Entschuldigung Zusammenfassung -->
+      <div>
+        {{ $t("alsijil.coursebook.absences.title") }}
+      </div>
+      <span v-if="!form" class="px-2">·</span>
+      <div v-if="!form">
+        {{ $t("alsijil.coursebook.absences.summary") }}
+      </div>
     </template>
     <template #content>
-      <absence-form v-if="form" />
-      <absence-summary v-else />
+      <absence-creation-form
+        :persons="persons"
+        :start-date="startDate"
+        :end-date="endDate"
+        :comment="comment"
+        :absence-reason="absenceReason"
+        @valid="formValid = $event"
+        @persons="persons = $event"
+        @start-date="startDate = $event"
+        @end-date="endDate = $event"
+        @comment="comment = $event"
+        @absence-reason="absenceReason = $event"
+        :class="{
+          'd-none': !form,
+        }"
+      />
+      <absence-creation-summary
+        v-if="!form"
+        :persons="persons"
+        :start-date="startDate"
+        :end-date="endDate"
+        @loading="handleLoading"
+      />
+    </template>
+    <template #actionsLeft>
+      <cancel-button @click="cancel" />
     </template>
     <template #actions>
       <!-- secondary -->
-      <!-- TODO: Return to form on cancel? form=true -->
-      <cancel-button @click="popup = false" disabled="loading" />
+      <secondary-action-button
+        @click="form = true"
+        v-if="!form"
+        :disabled="loading"
+        i18n-key="actions.back"
+      >
+        <v-icon left>$prev</v-icon>
+        {{ $t("actions.back") }}
+      </secondary-action-button>
       <!-- primary -->
       <save-button
         v-if="form"
-        i18n-key="actions.continue"
         @click="form = false"
         :loading="loading"
-      />
+        :disabled="!formValid"
+      >
+        {{ $t("actions.continue") }}
+        <v-icon right>$next</v-icon>
+      </save-button>
       <save-button
-        v-if="form"
+        v-else
         i18n-key="actions.confirm"
         @click="confirm"
         :loading="loading"
@@ -35,31 +84,96 @@
 
 <script>
 import MobileFullscreenDialog from "aleksis.core/components/generic/dialogs/MobileFullscreenDialog.vue";
-import AbsenceForm from "./AbsenceForm.vue";
-import AbsenceSummary from "./AbsenceSummary.vue";
+import AbsenceCreationForm from "./AbsenceCreationForm.vue";
+import AbsenceCreationSummary from "./AbsenceCreationSummary.vue";
+import FabButton from "aleksis.core/components/generic/buttons/FabButton.vue";
 import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.vue";
 import SaveButton from "aleksis.core/components/generic/buttons/SaveButton.vue";
+import SecondaryActionButton from "aleksis.core/components/generic/buttons/SecondaryActionButton.vue";
+import loadingMixin from "aleksis.core/mixins/loadingMixin.js";
+import permissionsMixin from "aleksis.core/mixins/permissions.js";
+import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
+import { DateTime } from "luxon";
+
+import { createAbsencesForPersons } from "./absenceCreation.graphql";
 
 export default {
-  name: "AbsenceDialog",
+  name: "AbsenceCreationDialog",
   components: {
     MobileFullscreenDialog,
-    AbsenceForm,
-    AbsenceSummary,
+    AbsenceCreationForm,
+    AbsenceCreationSummary,
     CancelButton,
     SaveButton,
+    SecondaryActionButton,
+    FabButton,
   },
+  mixins: [loadingMixin, mutateMixin, permissionsMixin],
   data() {
     return {
       popup: false,
       form: true,
-      loading: false,
+      formValid: false,
+      persons: [],
+      startDate: "",
+      endDate: "",
+      comment: "",
+      absenceReason: "",
     };
   },
+  mounted() {
+    this.addPermissions(["alsijil.view_register_absence_rule"]);
+    this.clearForm();
+  },
   methods: {
+    cancel() {
+      this.popup = false;
+      this.form = true;
+      this.clearForm();
+    },
+    clearForm() {
+      this.persons = [];
+      this.startDate = DateTime.now()
+        .startOf("day")
+        .toISO({ suppressSeconds: true });
+      this.endDate = DateTime.now()
+        .endOf("day")
+        .toISO({ suppressSeconds: true });
+      this.comment = "";
+      this.absenceReason = "";
+    },
     confirm() {
-      // TODO: Send mutation (shown in absence-summary)
-      popup = false,
+      this.handleLoading(true);
+      this.mutate(
+        createAbsencesForPersons,
+        {
+          persons: this.persons.map((p) => p.id),
+          start: this.$toUTCISO(this.$parseISODate(this.startDate)),
+          end: this.$toUTCISO(this.$parseISODate(this.endDate)),
+          comment: this.comment,
+          reason: this.absenceReason,
+        },
+        (storedDocumentations, incomingStatuses) => {
+          const documentation = storedDocumentations.find(
+            (doc) => doc.id === this.documentation.id,
+          );
+
+          incomingStatuses.forEach((newStatus) => {
+            const participationStatus = documentation.participations.find(
+              (part) => part.id === newStatus.id,
+            );
+            participationStatus.absenceReason = newStatus.absenceReason;
+            participationStatus.isOptimistic = newStatus.isOptimistic;
+          });
+
+          return storedDocumentations;
+        },
+      );
+      this.$once("save", this.handleSave);
+    },
+    handleSave() {
+      this.cancel();
+      this.$toastSuccess(this.$t("alsijil.coursebook.absences.success"));
     },
   },
 };
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue
index 09cf3ea5adbb59e39ed3b782bc5a114a22a123da..239c83d6bbd48700773c024967636a82f3d3c4a8 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue
@@ -1,44 +1,140 @@
 <template>
-  <v-container>
-    <v-row>
-      <!-- persons -->
-      <!-- v-autocomplete -->
-    </v-row>
-    <v-row>
-      <!-- Start -->
-      <v-col cols="12" :sm="6">
-        <date-field
-          :value="value"
-          @input="$emit('input', $event)"
-          :label="$t('date_select.label')"
-          :disabled="loading"
+  <v-form @input="$emit('valid', $event)">
+    <v-container>
+      <v-row>
+        <div aria-required="true" class="full-width">
+          <!-- FIXME Vue 3: clear-on-select -->
+          <v-autocomplete
+            :label="$t('forms.labels.persons')"
+            :items="allPersons"
+            item-text="fullName"
+            return-object
+            multiple
+            chips
+            deletable-chips
+            :rules="
+              $rules().build([
+                (value) => value.length > 0 || $t('forms.errors.required'),
+              ])
+            "
+            :value="persons"
+            :loading="$apollo.queries.allPersons.loading"
+            @input="$emit('persons', $event)"
+          />
+        </div>
+      </v-row>
+      <v-row>
+        <v-col cols="12" :sm="6" class="pl-0">
+          <div aria-required="true">
+            <date-time-field
+              :label="$t('forms.labels.start')"
+              :max-date="endDate"
+              :max-time="maxStartTime"
+              :rules="$rules().required.build()"
+              :value="startDate"
+              @input="$emit('start-date', $event)"
+            />
+          </div>
+        </v-col>
+        <v-col cols="12" :sm="6" class="pr-0">
+          <div aria-required="true">
+            <date-time-field
+              :label="$t('forms.labels.end')"
+              :min-date="startDate"
+              :min-time="minEndTime"
+              :rules="$rules().required.build()"
+              :value="endDate"
+              @input="$emit('end-date', $event)"
+            />
+          </div>
+        </v-col>
+      </v-row>
+      <v-row>
+        <v-text-field
+          :label="$t('forms.labels.comment')"
+          :value="comment"
+          @input="$emit('comment', $event)"
         />
-      </v-col>
-      <!-- End -->
-      <v-col cols="12" :sm="6">
-        <date-field
-          :value="value"
-          @input="$emit('input', $event)"
-          :label="$t('date_select.label')"
-          :disabled="loading"
-        />
-      </v-col>
-    </v-row>
-    <v-row>
-      <!-- comment -->
-    </v-row>
-    <v-row>
-      <!-- TODO: Component from Julian -->
-    </v-row>
-  </v-container>
+      </v-row>
+      <v-row>
+        <div aria-required="true">
+          <absence-reason-group-select
+            :rules="$rules().required.build()"
+            :value="absenceReason"
+            @input="$emit('absence-reason', $event)"
+          />
+        </div>
+      </v-row>
+    </v-container>
+  </v-form>
 </template>
 
 <script>
-import DateField from "aleksis.core/components/generic/forms/DateField.vue";
+import AbsenceReasonGroupSelect from "aleksis.apps.kolego/components/AbsenceReasonGroupSelect.vue";
+import DateTimeField from "aleksis.core/components/generic/forms/DateTimeField.vue";
+import { persons } from "./absenceCreation.graphql";
+import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js";
+import { DateTime } from "luxon";
+
 export default {
-  name: "AbsenceForm",
+  name: "AbsenceCreationForm",
   components: {
-    DateField,
+    AbsenceReasonGroupSelect,
+    DateTimeField,
+  },
+  mixins: [formRulesMixin],
+  emits: [
+    "valid",
+    "persons",
+    "start-date",
+    "end-date",
+    "comment",
+    "absence-reason",
+  ],
+  apollo: {
+    allPersons: persons,
+  },
+  props: {
+    persons: {
+      type: Array,
+      required: true,
+    },
+    startDate: {
+      type: String,
+      required: true,
+    },
+    endDate: {
+      type: String,
+      required: true,
+    },
+    comment: {
+      type: String,
+      required: true,
+    },
+    absenceReason: {
+      type: String,
+      required: true,
+    },
+  },
+  computed: {
+    maxStartTime() {
+      // Only if on the same day
+      const start = DateTime.fromISO(this.startDate);
+      const end = DateTime.fromISO(this.endDate);
+
+      if (start.day !== end.day) return;
+
+      return end.minus({ minutes: 5 }).toFormat("HH:mm");
+    },
+    minEndTime() {
+      // Only if on the same day
+      const start = DateTime.fromISO(this.startDate);
+      const end = DateTime.fromISO(this.endDate);
+
+      if (start.day !== end.day) return;
+
+      return start.plus({ minutes: 5 }).toFormat("HH:mm");
+    },
   },
 };
 </script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationSummary.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationSummary.vue
index 569bc64903ecb8740ba166f457956cc64ca09496..17a7795e697caeed9346c3aa13fe6b10e2f54c28 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationSummary.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationSummary.vue
@@ -1,25 +1,123 @@
 <template>
-  <!-- TODO: Hide header -->
-  <c-r-u-d-iterator
-    :gql-query=""
-    :gql-additional-query-args="FROM FORM"
-    :enable-create="false"
-    :enable-edit="false"
-    :elevated="false"
-  >
-    <template #default="{ items }">
-      <!-- expandable card per person -->
-    </template>
-  </c-r-u-d-iterator>
+  <div>
+    <message-box dense type="warning" class="mt-5">
+      {{ $t("alsijil.coursebook.absences.warning") }}
+    </message-box>
+    <!-- MAYBE introduce a minimal variant of CRUDIterator -->
+    <!--       with most features disabled for this list usecase -->
+    <c-r-u-d-iterator
+      i18n-key=""
+      :gql-query="gqlQuery"
+      :gql-additional-query-args="gqlArgs"
+      :enable-search="false"
+      :enable-create="false"
+      :enable-edit="false"
+      :elevated="false"
+      disable-pagination
+      hide-default-footer
+      @loading="handleLoading"
+    >
+      <template #default="{ items }">
+        <v-expansion-panels>
+          <v-expansion-panel v-for="person in items" :key="person.id">
+            <v-expansion-panel-header>
+              <div>
+                {{ persons.find((p) => p.id === person.id).fullName }}
+              </div>
+              <v-spacer />
+              <div>
+                {{
+                  $tc(
+                    "alsijil.coursebook.absences.lessons",
+                    person.lessons.length,
+                    { count: person.lessons.length },
+                  )
+                }}
+              </div>
+            </v-expansion-panel-header>
+            <v-expansion-panel-content>
+              <v-list-item
+                v-for="lesson in person.lessons"
+                class="px-0"
+                :key="lesson.id"
+              >
+                <v-row>
+                  <!-- TODO: We should extract this display & share it -->
+                  <v-col cols="3">
+                    <time :datetime="lesson.datetimeStart" class="text-no-wrap">
+                      {{
+                        $d(
+                          $parseISODate(lesson.datetimeStart),
+                          "shortWithWeekday",
+                        )
+                      }}&nbsp;
+                    </time>
+                  </v-col>
+                  <v-col cols="3">
+                    <time :datetime="lesson.datetimeStart" class="text-no-wrap">
+                      {{ $d($parseISODate(lesson.datetimeStart), "shortTime") }}
+                    </time>
+                    <span> - </span>
+                    <time :datetime="lesson.datetimeEnd" class="text-no-wrap">
+                      {{ $d($parseISODate(lesson.datetimeEnd), "shortTime") }}
+                    </time>
+                  </v-col>
+                  <v-col cols="3">
+                    {{ lesson.course?.name }}
+                  </v-col>
+                  <v-col cols="3">
+                    <subject-chip :subject="lesson.subject" />
+                  </v-col>
+                </v-row>
+              </v-list-item>
+            </v-expansion-panel-content>
+          </v-expansion-panel>
+        </v-expansion-panels>
+      </template>
+    </c-r-u-d-iterator>
+  </div>
 </template>
 
 <script>
 import CRUDIterator from "aleksis.core/components/generic/CRUDIterator.vue";
+import SubjectChip from "aleksis.apps.cursus/components/SubjectChip.vue";
+import { lessonsForPersons } from "./absenceCreation.graphql";
+import loadingMixin from "aleksis.core/mixins/loadingMixin.js";
 
 export default {
-  name: "AbsenceSummary",
+  name: "AbsenceCreationSummary",
   components: {
     CRUDIterator,
+    SubjectChip,
+  },
+  mixins: [loadingMixin],
+  props: {
+    persons: {
+      type: Array,
+      required: true,
+    },
+    startDate: {
+      type: String,
+      required: true,
+    },
+    endDate: {
+      type: String,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      gqlQuery: lessonsForPersons,
+    };
+  },
+  computed: {
+    gqlArgs() {
+      return {
+        persons: this.persons.map((person) => person.id),
+        start: this.startDate,
+        end: this.endDate,
+      };
+    },
   },
 };
 </script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/DocumentationAbsences.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/DocumentationAbsences.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8dbe68847629ba81e0bca1da804c516de4562bf4
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/DocumentationAbsences.vue
@@ -0,0 +1,111 @@
+<template>
+  <v-card :class="{ 'my-1 full-width': true, 'd-flex flex-column': !compact }">
+    <v-card-title v-if="!compact">
+      <lesson-information v-bind="documentationPartProps" />
+    </v-card-title>
+
+    <v-card-text
+      class="full-width main-body"
+      :class="{
+        vertical: !compact || $vuetify.breakpoint.mobile,
+        'pa-2': compact,
+      }"
+    >
+      <lesson-information v-if="compact" v-bind="documentationPartProps" />
+
+      <lesson-notes class="span-2" v-bind="documentationPartProps" />
+      <participation-list
+        v-if="documentation.canEditParticipationStatus"
+        :include-present="false"
+        class="participation-list"
+        v-bind="documentationPartProps"
+        :value="value"
+        @input="$emit('input', $event)"
+      />
+    </v-card-text>
+    <v-spacer />
+    <v-divider />
+    <v-card-actions v-if="!compact">
+      <v-spacer />
+      <cancel-button
+        v-if="documentation.canEdit"
+        @click="$emit('close')"
+        :disabled="loading"
+      />
+      <save-button
+        v-if="documentation.canEdit"
+        @click="save"
+        :loading="loading"
+      />
+      <cancel-button
+        v-if="!documentation.canEdit"
+        i18n-key="actions.close"
+        @click="$emit('close')"
+      />
+    </v-card-actions>
+  </v-card>
+</template>
+
+<script>
+import ParticipationList from "./ParticipationList.vue";
+import LessonInformation from "../documentation/LessonInformation.vue";
+import LessonNotes from "../documentation/LessonNotes.vue";
+
+import SaveButton from "aleksis.core/components/generic/buttons/SaveButton.vue";
+import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.vue";
+
+import { createOrUpdateDocumentations } from "../coursebook.graphql";
+
+import documentationPartMixin from "../documentation/documentationPartMixin";
+
+export default {
+  name: "DocumentationAbsences",
+  components: {
+    ParticipationList,
+    LessonInformation,
+    LessonNotes,
+    SaveButton,
+    CancelButton,
+  },
+  emits: ["open", "close"],
+  mixins: [documentationPartMixin],
+  data() {
+    return {
+      loading: false,
+      documentationsMutation: createOrUpdateDocumentations,
+      selectedParticipations: [],
+    };
+  },
+  props: {
+    value: {
+      type: Array,
+      required: true,
+    },
+  },
+  methods: {
+    save() {
+      this.$refs.summary.save();
+      this.$emit("close");
+    },
+  },
+};
+</script>
+
+<style scoped>
+.main-body {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  grid-template-rows: min-content min-content;
+  column-gap: 1em;
+}
+.participation-list {
+  grid-column-start: 1;
+  grid-column-end: span 3;
+}
+.span-2 {
+  grid-column-end: span 2;
+}
+.vertical > * {
+  grid-column-end: span 3;
+}
+</style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/DocumentationAbsencesModal.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/DocumentationAbsencesModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f639e731da82cd400c1d2b5cbba8dbc90da8d70e
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/DocumentationAbsencesModal.vue
@@ -0,0 +1,60 @@
+<!-- Wrapper around DocumentationAbsences.vue -->
+<!-- That uses it either as list item or as editable modal dialog. -->
+<template>
+  <mobile-fullscreen-dialog v-model="popup" max-width="500px">
+    <template #activator="activator">
+      <!-- list view -> activate dialog -->
+      <documentation-absences
+        compact
+        v-bind="$attrs"
+        :extra-marks="extraMarks"
+        :absence-reasons="absenceReasons"
+        :dialog-activator="activator"
+        :value="value"
+        @input="$emit('input', $event)"
+      />
+    </template>
+    <!-- dialog view -> deactivate dialog -->
+    <!-- cancel | save (through lesson-summary) -->
+    <documentation
+      v-bind="$attrs"
+      :extra-marks="extraMarks"
+      :absence-reasons="absenceReasons"
+      @close="popup = false"
+    />
+  </mobile-fullscreen-dialog>
+</template>
+
+<script>
+import MobileFullscreenDialog from "aleksis.core/components/generic/dialogs/MobileFullscreenDialog.vue";
+import DocumentationAbsences from "./DocumentationAbsences.vue";
+import Documentation from "../documentation/Documentation.vue";
+
+export default {
+  name: "DocumentationAbsencesModal",
+  components: {
+    MobileFullscreenDialog,
+    Documentation,
+    DocumentationAbsences,
+  },
+  data() {
+    return {
+      popup: false,
+    };
+  },
+  props: {
+    value: {
+      type: Array,
+      required: true,
+    },
+    extraMarks: {
+      type: Array,
+      required: true,
+    },
+    absenceReasons: {
+      type: Array,
+      required: true,
+    },
+  },
+};
+</script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
index 3af1db58846f37b5e7e7837dba08a4468294269e..3248becfa4ddeaaaf998794f5398d5845f38d21f 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
@@ -2,27 +2,46 @@
 import AbsenceReasonButtons from "aleksis.apps.kolego/components/AbsenceReasonButtons.vue";
 import AbsenceReasonChip from "aleksis.apps.kolego/components/AbsenceReasonChip.vue";
 import AbsenceReasonGroupSelect from "aleksis.apps.kolego/components/AbsenceReasonGroupSelect.vue";
-import CancelButton from "aleksis.core/components/generic/buttons/CancelButton.vue";
+import DialogCloseButton from "aleksis.core/components/generic/buttons/DialogCloseButton.vue";
+import SecondaryActionButton from "aleksis.core/components/generic/buttons/SecondaryActionButton.vue";
 import MobileFullscreenDialog from "aleksis.core/components/generic/dialogs/MobileFullscreenDialog.vue";
-import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
-import documentationPartMixin from "../documentation/documentationPartMixin";
+import updateParticipationMixin from "./updateParticipationMixin.js";
+import deepSearchMixin from "aleksis.core/mixins/deepSearchMixin.js";
 import LessonInformation from "../documentation/LessonInformation.vue";
-import { updateParticipationStatuses } from "./participationStatus.graphql";
+import {
+  extendParticipationStatuses,
+  updateParticipationStatuses,
+} from "./participationStatus.graphql";
 import SlideIterator from "aleksis.core/components/generic/SlideIterator.vue";
+import PersonalNotes from "../personal_notes/PersonalNotes.vue";
+import PersonalNoteChip from "../personal_notes/PersonalNoteChip.vue";
+import ExtraMarkChip from "../../extra_marks/ExtraMarkChip.vue";
+import TardinessChip from "./TardinessChip.vue";
+import TardinessField from "./TardinessField.vue";
+import ExtraMarkButtons from "../../extra_marks/ExtraMarkButtons.vue";
+import MessageBox from "aleksis.core/components/generic/MessageBox.vue";
 
 export default {
   name: "ManageStudentsDialog",
   extends: MobileFullscreenDialog,
   components: {
+    ExtraMarkButtons,
+    TardinessChip,
+    ExtraMarkChip,
     AbsenceReasonChip,
     AbsenceReasonGroupSelect,
     AbsenceReasonButtons,
-    CancelButton,
+    PersonalNotes,
+    PersonalNoteChip,
     LessonInformation,
+    MessageBox,
     MobileFullscreenDialog,
+    SecondaryActionButton,
     SlideIterator,
+    TardinessField,
+    DialogCloseButton,
   },
-  mixins: [documentationPartMixin, mutateMixin],
+  mixins: [updateParticipationMixin, deepSearchMixin],
   data() {
     return {
       dialog: false,
@@ -30,6 +49,14 @@ export default {
       loadSelected: false,
       selected: [],
       isExpanded: false,
+      markAsAbsentDay: {
+        showAlert: false,
+        num: 0,
+        reason: "no reason",
+        name: "nobody",
+        participationIDs: [],
+        loading: false,
+      },
     };
   },
   props: {
@@ -38,6 +65,11 @@ export default {
       default: false,
       required: false,
     },
+    useDeepSearch: {
+      type: Boolean,
+      default: true,
+      required: false,
+    },
   },
   computed: {
     items() {
@@ -45,16 +77,56 @@ export default {
     },
   },
   methods: {
-    sendToServer(participations, field, value) {
-      if (field !== "absenceReason") return;
+    handleMultipleAction(field, id) {
+      this.loadSelected = true;
+      this.sendToServer(this.selected, field, id);
+      this.$once("save", this.resetMultipleAction);
+    },
+    resetMultipleAction() {
+      this.loadSelected = false;
+      this.$set(this.selected, []);
+      this.$refs.iterator.selected = [];
+    },
+    activateFullDayDialog(items) {
+      const itemIds = items.map((item) => item.id);
+      const participations = this.documentation.participations.filter((part) =>
+        itemIds.includes(part.id),
+      );
+
+      if (this.markAsAbsentDay.num === 1) {
+        this.markAsAbsentDay.name = participations[0].person.firstName;
+      }
+
+      this.$set(this.markAsAbsentDay, "participationIDs", itemIds);
+
+      this.markAsAbsentDay.loading = false;
+      this.markAsAbsentDay.showAlert = true;
+    },
+    beforeSendToServer() {
+      this.markAsAbsentDay.showAlert = false;
+      this.markAsAbsentDay.participationIDs = [];
+    },
+    duringUpdateSendToServer(
+      _participations,
+      _field,
+      _value,
+      incomingStatuses,
+    ) {
+      this.markAsAbsentDay.reason = incomingStatuses[0].absenceReason?.name;
+      this.markAsAbsentDay.num = incomingStatuses.length;
+    },
+    afterSendToServer(_participations, field, value) {
+      if (field === "absenceReason" && value !== "present") {
+        this.$once("save", this.activateFullDayDialog);
+      }
+    },
+    markAsAbsentDayClick() {
+      this.markAsAbsentDay.loading = true;
 
       this.mutate(
-        updateParticipationStatuses,
+        extendParticipationStatuses,
         {
-          input: participations.map((participation) => ({
-            id: participation.id,
-            absenceReason: value === "present" ? null : value,
-          })),
+          input: this.markAsAbsentDay.participationIDs,
         },
         (storedDocumentations, incomingStatuses) => {
           const documentation = storedDocumentations.find(
@@ -65,24 +137,21 @@ export default {
             const participationStatus = documentation.participations.find(
               (part) => part.id === newStatus.id,
             );
-            participationStatus.absenceReason = newStatus.absenceReason;
+            participationStatus.baseAbsence = newStatus.baseAbsence;
             participationStatus.isOptimistic = newStatus.isOptimistic;
           });
 
+          this.markAsAbsentDay.reason = "no reason";
+          this.markAsAbsentDay.num = 0;
+          this.markAsAbsentDay.participationIDs = [];
+          this.markAsAbsentDay.loading = false;
+
+          this.markAsAbsentDay.showAlert = false;
+
           return storedDocumentations;
         },
       );
     },
-    handleMultipleAction(absenceReasonId) {
-      this.loadSelected = true;
-      this.sendToServer(this.selected, "absenceReason", absenceReasonId);
-      this.$once("save", this.resetMultipleAction);
-    },
-    resetMultipleAction() {
-      this.loadSelected = false;
-      this.$set(this.selected, []);
-      this.$refs.iterator.selected = [];
-    },
   },
 };
 </script>
@@ -93,13 +162,17 @@ export default {
     v-bind="$attrs"
     v-on="$listeners"
     v-model="dialog"
+    :close-button="false"
   >
     <template #activator="activator">
       <slot name="activator" v-bind="activator" />
     </template>
 
     <template #title>
-      <lesson-information v-bind="documentationPartProps" :compact="false" />
+      <div class="d-flex full-width">
+        <lesson-information v-bind="documentationPartProps" :compact="false" />
+        <dialog-close-button @click="dialog = false" class="ml-4" />
+      </div>
       <v-scroll-x-transition leave-absolute>
         <v-text-field
           v-show="!isExpanded"
@@ -116,15 +189,40 @@ export default {
           class="pt-4 full-width"
         />
       </v-scroll-x-transition>
-      <v-scroll-x-transition>
-        <div v-show="selected.length > 0" class="full-width mt-4">
-          <absence-reason-buttons
-            allow-empty
-            empty-value="present"
-            @input="handleMultipleAction"
-          />
+      <message-box
+        v-model="markAsAbsentDay.showAlert"
+        color="success"
+        icon="$success"
+        transition="slide-y-transition"
+        dismissible
+        class="mt-4 mb-0 full-width"
+      >
+        <div class="text-subtitle-2">
+          {{
+            $tc(
+              "alsijil.coursebook.mark_as_absent_day.title",
+              markAsAbsentDay.num,
+              markAsAbsentDay,
+            )
+          }}
         </div>
-      </v-scroll-x-transition>
+        <p class="text-body-2 pa-0 ma-0" style="word-break: break-word">
+          {{
+            $t(
+              "alsijil.coursebook.mark_as_absent_day.description",
+              markAsAbsentDay,
+            )
+          }}
+        </p>
+
+        <secondary-action-button
+          color="success"
+          i18n-key="alsijil.coursebook.mark_as_absent_day.action_button"
+          class="mt-2"
+          :loading="markAsAbsentDay.loading"
+          @click="markAsAbsentDayClick"
+        />
+      </message-box>
     </template>
     <template #content>
       <slide-iterator
@@ -139,13 +237,43 @@ export default {
         :loading="loadingIndicator || loadSelected"
         :load-only-selected="loadSelected"
         :disabled="loading"
+        :custom-filter="deepSearch"
       >
         <template #listItemContent="{ item }">
           <v-list-item-title>
             {{ item.person.fullName }}
           </v-list-item-title>
-          <v-list-item-subtitle v-if="item.absenceReason">
-            <absence-reason-chip small :absence-reason="item.absenceReason" />
+          <v-list-item-subtitle
+            v-if="
+              item.absenceReason ||
+              item.notesWithNote?.length > 0 ||
+              item.notesWithExtraMark?.length > 0 ||
+              item.tardiness
+            "
+            class="d-flex flex-wrap gap"
+          >
+            <absence-reason-chip
+              v-if="item.absenceReason"
+              small
+              :absence-reason="item.absenceReason"
+            />
+            <personal-note-chip
+              v-for="note in item.notesWithNote"
+              :key="'text-note-note-overview-' + note.id"
+              :note="note"
+              small
+            />
+            <extra-mark-chip
+              v-for="note in item.notesWithExtraMark"
+              :key="'extra-mark-note-overview-' + note.id"
+              :extra-mark="extraMarks.find((e) => e.id === note.extraMark.id)"
+              small
+            />
+            <tardiness-chip
+              v-if="item.tardiness"
+              :tardiness="item.tardiness"
+              small
+            />
           </v-list-item-subtitle>
         </template>
 
@@ -164,22 +292,59 @@ export default {
           <v-card-text>
             <absence-reason-group-select
               allow-empty
-              empty-value="present"
-              :loadSelectedChip="loading"
+              :load-selected-chip="loading"
               :value="item.absenceReason?.id || 'present'"
+              :custom-absence-reasons="absenceReasons"
               @input="sendToServer([item], 'absenceReason', $event)"
             />
+            <tardiness-field
+              v-bind="documentationPartProps"
+              :loading="loading"
+              :disabled="loading"
+              :participation="item"
+              :value="item.tardiness"
+              @input="sendToServer([item], 'tardiness', $event)"
+            />
+          </v-card-text>
+          <v-divider />
+          <v-card-text>
+            <personal-notes
+              v-bind="documentationPartProps"
+              :participation="
+                documentation.participations.find((p) => p.id === item.id)
+              "
+            />
           </v-card-text>
         </template>
       </slide-iterator>
     </template>
 
     <template #actions>
-      <cancel-button
-        @click="dialog = false"
-        i18n-key="actions.close"
-        v-show="$vuetify.breakpoint.mobile"
-      />
+      <v-scroll-y-reverse-transition>
+        <div v-show="selected.length > 0" class="full-width">
+          <h4>{{ $t("alsijil.coursebook.participation_status") }}</h4>
+          <absence-reason-buttons
+            class="mb-1"
+            allow-empty
+            empty-value="present"
+            :custom-absence-reasons="absenceReasons"
+            @input="handleMultipleAction('absenceReason', $event)"
+          />
+          <h4>{{ $t("alsijil.extra_marks.title_plural") }}</h4>
+          <extra-mark-buttons
+            @input="handleMultipleAction('extraMark', $event)"
+          />
+          <h4>{{ $t("alsijil.personal_notes.tardiness") }}</h4>
+          <tardiness-field
+            v-bind="documentationPartProps"
+            :loading="loading"
+            :disabled="loading"
+            :value="0"
+            :participations="selected"
+            @input="handleMultipleAction('tardiness', $event)"
+          />
+        </div>
+      </v-scroll-y-reverse-transition>
     </template>
   </mobile-fullscreen-dialog>
 </template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsTrigger.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsTrigger.vue
index 572036c67955b3365bb46eb69f6ab41ee86cf074..963367cbddb77174251a3b2deb21f65102e3c960 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsTrigger.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsTrigger.vue
@@ -15,6 +15,13 @@ export default {
       timeout: null,
     };
   },
+  props: {
+    labelKey: {
+      type: String,
+      required: false,
+      default: undefined,
+    },
+  },
   mounted() {
     const lessonStart = DateTime.fromISO(this.documentation.datetimeStart);
     const now = DateTime.now();
@@ -50,6 +57,17 @@ export default {
       );
     },
   },
+  computed: {
+    showLabel() {
+      return !!this.labelKey || !this.canOpenParticipation;
+    },
+    innerLabelKey() {
+      if (this.documentation.futureNoticeParticipationStatus) {
+        return "alsijil.coursebook.notes.future";
+      }
+      return this.labelKey;
+    },
+  },
 };
 </script>
 
@@ -58,6 +76,7 @@ export default {
     v-bind="documentationPartProps"
     @update="() => null"
     :loading-indicator="loading"
+    v-if="!documentation.amends?.cancelled"
   >
     <template #activator="{ attrs, on }">
       <v-chip
@@ -69,7 +88,10 @@ export default {
         v-on="on"
         @click="touchDocumentation"
       >
-        <v-icon>$edit</v-icon>
+        <v-icon :left="showLabel">mdi-account-edit-outline</v-icon>
+        <template v-if="showLabel">
+          {{ $t(innerLabelKey) }}
+        </template>
       </v-chip>
     </template>
   </manage-students-dialog>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ParticipationList.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ParticipationList.vue
new file mode 100644
index 0000000000000000000000000000000000000000..dd84e644a78059284cdc338b6cd899f96a36066c
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ParticipationList.vue
@@ -0,0 +1,96 @@
+<script setup>
+import AbsenceReasonGroupSelect from "aleksis.apps.kolego/components/AbsenceReasonGroupSelect.vue";
+</script>
+
+<template>
+  <v-list v-if="filteredParticipations.length">
+    <v-divider />
+
+    <v-list-item-group :value="value" multiple @change="changeSelect">
+      <template v-for="(participation, index) in filteredParticipations">
+        <v-list-item
+          :key="`documentation-${documentation.id}-participation-${participation.id}`"
+          :value="participation.id"
+          v-bind="$attrs"
+          two-line
+        >
+          <template #default="{ active }">
+            <v-list-item-action>
+              <v-checkbox :input-value="active" />
+            </v-list-item-action>
+            <v-list-item-content>
+              <v-list-item-title>
+                {{ participation.person.fullName }}
+              </v-list-item-title>
+              <absence-reason-group-select
+                v-if="participation.absenceReason && !compact"
+                class="full-width"
+                allow-empty
+                :load-selected-chip="loading"
+                :custom-absence-reasons="absenceReasons"
+                :value="participation.absenceReason?.id || 'present'"
+                @input="sendToServer([participation], 'absenceReason', $event)"
+              />
+            </v-list-item-content>
+            <v-list-item-action v-if="participation.absenceReason && compact">
+              <absence-reason-group-select
+                allow-empty
+                :load-selected-chip="loading"
+                :custom-absence-reasons="absenceReasons"
+                :value="participation.absenceReason?.id || 'present'"
+                @input="sendToServer([participation], 'absenceReason', $event)"
+              />
+            </v-list-item-action>
+          </template>
+        </v-list-item>
+        <v-divider
+          v-if="index < filteredParticipations.length - 1"
+          :key="index"
+        ></v-divider>
+      </template>
+    </v-list-item-group>
+  </v-list>
+</template>
+
+<script>
+import updateParticipationMixin from "./updateParticipationMixin";
+
+export default {
+  name: "ParticipationList",
+  mixins: [updateParticipationMixin],
+  data() {
+    return {
+      loading: false,
+      participationDialogs: false,
+      isExpanded: false,
+    };
+  },
+  props: {
+    includePresent: {
+      type: Boolean,
+      required: false,
+      default: true,
+    },
+    value: {
+      type: Array,
+      required: true,
+    },
+  },
+  computed: {
+    filteredParticipations() {
+      if (!this.includePresent) {
+        return this.documentation.participations.filter(
+          (p) => !!p.absenceReason,
+        );
+      } else {
+        return this.documentation.participations;
+      }
+    },
+  },
+  methods: {
+    changeSelect(value) {
+      this.$emit("input", value);
+    },
+  },
+};
+</script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessChip.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessChip.vue
new file mode 100644
index 0000000000000000000000000000000000000000..6be1fea3942fed64d37d2a68dde699344e55c35b
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessChip.vue
@@ -0,0 +1,34 @@
+<script>
+export default {
+  name: "TardinessChip",
+  props: {
+    tardiness: {
+      type: Number,
+      required: false,
+      default: 0,
+    },
+    loading: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
+  },
+  extends: "v-chip",
+};
+</script>
+
+<template>
+  <v-chip dense outlined v-bind="$attrs" v-on="$listeners">
+    <v-avatar left>
+      <v-icon small>mdi-clock-alert-outline</v-icon>
+    </v-avatar>
+    <slot name="prepend" />
+    <slot>
+      {{ $tc("alsijil.personal_notes.minutes_late", tardiness) }}
+    </slot>
+    <slot name="append" />
+    <v-avatar right v-if="loading">
+      <v-progress-circular indeterminate :size="16" :width="2" />
+    </v-avatar>
+  </v-chip>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessField.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessField.vue
new file mode 100644
index 0000000000000000000000000000000000000000..774decab0a88867af0fdb510ac8813edefec9a14
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/TardinessField.vue
@@ -0,0 +1,175 @@
+<script>
+import { DateTime } from "luxon";
+import documentationPartMixin from "../documentation/documentationPartMixin";
+import ConfirmDialog from "aleksis.core/components/generic/dialogs/ConfirmDialog.vue";
+import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js";
+
+export default {
+  name: "TardinessField",
+  components: { ConfirmDialog },
+  mixins: [documentationPartMixin, formRulesMixin],
+  props: {
+    value: {
+      type: Number,
+      default: null,
+      required: false,
+    },
+    participations: {
+      type: Array,
+      required: true,
+    },
+  },
+  computed: {
+    lessonLength() {
+      const lessonStart = DateTime.fromISO(this.documentation.datetimeStart);
+      const lessonEnd = DateTime.fromISO(this.documentation.datetimeEnd);
+
+      let diff = lessonEnd.diff(lessonStart, "minutes");
+      return diff.toObject().minutes;
+    },
+    defaultTimes() {
+      const lessonStart = DateTime.fromISO(this.documentation.datetimeStart);
+      const lessonEnd = DateTime.fromISO(this.documentation.datetimeEnd);
+      const now = DateTime.now();
+
+      let current = [];
+
+      if (now >= lessonStart && now <= lessonEnd) {
+        const diff = parseInt(
+          now.diff(lessonStart, "minutes").toObject().minutes,
+        );
+
+        current.push({
+          text: diff,
+          value: diff,
+          current: true,
+        });
+      }
+
+      return current.concat([
+        {
+          text: 5,
+          value: 5,
+        },
+        {
+          text: 10,
+          value: 10,
+        },
+        {
+          text: 15,
+          value: 15,
+        },
+      ]);
+    },
+  },
+  methods: {
+    lessonLengthRule(time) {
+      return (
+        time == null ||
+        time <= this.lessonLength ||
+        this.$t("alsijil.personal_notes.lesson_length_exceeded")
+      );
+    },
+    saveValue(value) {
+      this.$emit("input", value);
+      this.previousValue = value;
+    },
+    confirm() {
+      this.saveValue(0);
+    },
+    cancel() {
+      this.saveValue(this.previousValue);
+    },
+    processValueObjectOptional(value) {
+      if (Object.hasOwn(value, "value")) {
+        return value.value;
+      }
+
+      return value;
+    },
+    set(newValue) {
+      newValue = this.processValueObjectOptional(newValue);
+
+      if (!newValue || parseInt(newValue) === 0) {
+        // this is a DELETE action, show the dialog, ...
+        this.showDeleteConfirm = true;
+        return;
+      }
+
+      this.saveValue(parseInt(newValue));
+    },
+  },
+  data() {
+    return {
+      showDeleteConfirm: false,
+      previousValue: 0,
+    };
+  },
+  mounted() {
+    this.previousValue = this.value;
+  },
+};
+</script>
+
+<template>
+  <v-combobox
+    outlined
+    class="mt-1"
+    prepend-inner-icon="mdi-clock-alert-outline"
+    :suffix="$t('time.minutes')"
+    :label="$t('alsijil.personal_notes.tardiness')"
+    :rules="
+      $rules()
+        .isANumber.isAWholeNumber.isGreaterThan(0)
+        .build([lessonLengthRule])
+        .map((f) => (v) => f(this.processValueObjectOptional(v)))
+    "
+    :items="defaultTimes"
+    :value="value"
+    @change="set($event)"
+    v-bind="$attrs"
+  >
+    <template #item="{ item }">
+      <v-list-item-icon v-if="item.current">
+        <v-icon>mdi-shimmer</v-icon>
+      </v-list-item-icon>
+      <v-list-item-content>
+        <v-list-item-title>
+          {{
+            $tc(
+              item.current
+                ? "alsijil.personal_notes.minutes_late_current"
+                : "time.minutes_n",
+              item.value,
+            )
+          }}
+        </v-list-item-title>
+      </v-list-item-content>
+    </template>
+    <template #append>
+      <confirm-dialog
+        v-model="showDeleteConfirm"
+        @confirm="confirm"
+        @cancel="cancel"
+      >
+        <template #title>
+          {{ $t("alsijil.personal_notes.confirm_delete") }}
+        </template>
+        <template #text>
+          {{
+            $t("alsijil.personal_notes.confirm_delete_tardiness", {
+              tardiness: previousValue,
+              name: participations.map((p) => p.person.firstName).join(", "),
+            })
+          }}
+        </template>
+      </confirm-dialog>
+    </template>
+  </v-combobox>
+</template>
+
+<style scoped>
+.mt-n1-5 {
+  margin-top: -6px;
+}
+</style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql
index 5bc9ee2d62125800ccb43fa0ec677956b9343e32..5a520453f35062d49edd5eb82aca291384e2f739 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql
@@ -1,13 +1,12 @@
 # Uses core persons query
 query persons {
-  persons: persons {
+  allPersons: absenceCreationPersons {
     id
-    firstName
-    lastName
+    fullName
   }
 }
 
-query lessonsForPersons($persons: [ID!]!, $start: Date!, $end: Date!) {
+query lessonsForPersons($persons: [ID]!, $start: DateTime!, $end: DateTime!) {
   items: lessonsForPersons(persons: $persons, start: $start, end: $end) {
     id
     lessons {
@@ -30,22 +29,33 @@ query lessonsForPersons($persons: [ID!]!, $start: Date!, $end: Date!) {
 }
 
 # Use absencesInputType?
-mutation createAbsences(
-  $persons: [ID!]!
-  $start: Date!
-  $end: Date!
+mutation createAbsencesForPersons(
+  $persons: [ID]!
+  $start: DateTime!
+  $end: DateTime!
   $comment: String
   $reason: ID!
 ) {
-  createAbsences(
-    person: $persons
+  createAbsencesForPersons(
+    persons: $persons
     start: $start
     end: $end
     comment: $comment
     reason: $reason
   ) {
-    items: absences {
-      ok
+    ok
+    items: participationStatuses {
+      id
+      isOptimistic
+      relatedDocumentation {
+        id
+      }
+      absenceReason {
+        id
+        name
+        shortName
+        colour
+      }
     }
   }
 }
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceReasons.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceReasons.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..a86f608ec78d1bbc50bf558b5e0650cba5b5e8a7
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceReasons.graphql
@@ -0,0 +1,11 @@
+query absenceReasons($orderBy: [String], $filters: JSONString) {
+  items: coursebookAbsenceReasons(orderBy: $orderBy, filters: $filters) {
+    id
+    shortName
+    name
+    colour
+    default
+    canEdit
+    canDelete
+  }
+}
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql
index 81a3a5fb1eb3a99bef25eb9938cd254b9068981b..39eb59528c4f876eb2c75c20f187e5ded6fae2bc 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql
@@ -4,7 +4,6 @@ mutation updateParticipationStatuses(
   updateParticipationStatuses(input: $input) {
     items: participationStatuses {
       id
-      isOptimistic
       relatedDocumentation {
         id
       }
@@ -14,6 +13,19 @@ mutation updateParticipationStatuses(
         shortName
         colour
       }
+      notesWithExtraMark {
+        id
+        extraMark {
+          id
+          showInCoursebook
+        }
+      }
+      notesWithNote {
+        id
+        note
+      }
+      tardiness
+      isOptimistic
     }
   }
 }
@@ -35,8 +47,31 @@ mutation touchDocumentation($documentationId: ID!) {
           shortName
           colour
         }
+        notesWithExtraMark {
+          id
+          extraMark {
+            id
+            showInCoursebook
+          }
+        }
+        notesWithNote {
+          id
+          note
+        }
+        tardiness
         isOptimistic
       }
     }
   }
 }
+
+mutation extendParticipationStatuses($input: [ID]!) {
+  extendParticipationStatuses(input: $input) {
+    items: participations {
+      id
+    }
+    absences {
+      id
+    }
+  }
+}
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/selectParticipationMixin.js b/aleksis/apps/alsijil/frontend/components/coursebook/absences/selectParticipationMixin.js
new file mode 100644
index 0000000000000000000000000000000000000000..2d6d522692690c6def6aaa53e2dc0e7835019681
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/selectParticipationMixin.js
@@ -0,0 +1,27 @@
+/**
+ * Mixin to provide passing through functionality for the events emitted when (de)selecting participations on the absence overview page
+ */
+export default {
+  emits: ["select", "deselect"],
+  methods: {
+    handleSelect(participation) {
+      this.$emit("select", participation);
+    },
+    handleDeselect(participation) {
+      this.$emit("deselect", participation);
+    },
+  },
+
+  computed: {
+    /**
+     * All necessary listeners bundled together to easily pass to child components
+     * @returns {{select: Function, deselect: Function}}
+     */
+    selectListeners() {
+      return {
+        select: this.handleSelect,
+        deselect: this.handleDeselect,
+      };
+    },
+  },
+};
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/sendToServerMixin.js b/aleksis/apps/alsijil/frontend/components/coursebook/absences/sendToServerMixin.js
new file mode 100644
index 0000000000000000000000000000000000000000..e269bddee80dbe9056e399d63581212ebfdd36c4
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/sendToServerMixin.js
@@ -0,0 +1,115 @@
+/**
+ * Mixin to provide shared functionality needed to send updated participation data to the server
+ */
+import { createPersonalNotes } from "../personal_notes/personal_notes.graphql";
+import { updateParticipationStatuses } from "./participationStatus.graphql";
+import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
+
+export default {
+  mixins: [mutateMixin],
+  methods: {
+    sendToServer(participations, field, value) {
+      let fieldValue;
+
+      if (field === "absenceReason") {
+        fieldValue = {
+          absenceReason: value === "present" ? null : value,
+        };
+      } else if (field === "tardiness") {
+        fieldValue = {
+          tardiness: value,
+        };
+      } else if (field === "extraMark") {
+        // Too much different logic → own method
+        this.addExtraMarks(participations, value);
+        return;
+      } else {
+        console.error(`Wrong field '${field}' for sendToServer`);
+        return;
+      }
+
+      this.beforeSendToServer(participations, field, value);
+
+      this.mutate(
+        updateParticipationStatuses,
+        {
+          input: participations.map((participation) => ({
+            id: participation?.id || participation,
+            ...fieldValue,
+          })),
+        },
+        (storedDocumentations, incomingStatuses) => {
+          // TODO: what should happen here in places where there is more than one documentation?
+          const documentation = storedDocumentations.find(
+            (doc) => doc.id === this.documentation.id,
+          );
+
+          incomingStatuses.forEach((newStatus) => {
+            const participationStatus = documentation.participations.find(
+              (part) => part.id === newStatus.id,
+            );
+            participationStatus.absenceReason = newStatus.absenceReason;
+            participationStatus.tardiness = newStatus.tardiness;
+            participationStatus.isOptimistic = newStatus.isOptimistic;
+          });
+
+          this.duringUpdateSendToServer(
+            participations,
+            field,
+            value,
+            incomingStatuses,
+          );
+
+          return storedDocumentations;
+        },
+      );
+
+      this.afterSendToServer(participations, field, value);
+    },
+    addExtraMarks(participations, extraMarkId) {
+      // Get all participation statuses without this extra mark and get the respective person ids
+      const participants = participations
+        .filter(
+          (participation) =>
+            !participation.notesWithExtraMark.some(
+              (note) => note.extraMark.id === extraMarkId,
+            ),
+        )
+        .map((participation) => participation.person.id);
+
+      // CREATE new personal note
+      this.mutate(
+        createPersonalNotes,
+        {
+          input: participants.map((person) => ({
+            documentation: this.documentation.id,
+            person: person,
+            extraMark: extraMarkId,
+          })),
+        },
+        (storedDocumentations, incomingPersonalNotes) => {
+          const documentation = storedDocumentations.find(
+            (doc) => doc.id === this.documentation.id,
+          );
+          incomingPersonalNotes.forEach((note, index) => {
+            const participationStatus = documentation.participations.find(
+              (part) => part.person.id === participants[index],
+            );
+            participationStatus.notesWithExtraMark.push(note);
+          });
+
+          return storedDocumentations;
+        },
+      );
+    },
+    beforeSendToServer(_participations, _field, _value) {
+      // Noop hook
+    },
+    duringUpdateSendToServer(_participations, _field, _value, _incoming) {
+      // Noop hook
+    },
+    afterSendToServer(_participations, _field, _value) {
+      // Noop hook
+    },
+  },
+};
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/updateParticipationMixin.js b/aleksis/apps/alsijil/frontend/components/coursebook/absences/updateParticipationMixin.js
new file mode 100644
index 0000000000000000000000000000000000000000..2ed2c537d8bda9d11b2757468f72ad19cb89b7c6
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/updateParticipationMixin.js
@@ -0,0 +1,9 @@
+/**
+ * Mixin to provide shared functionality needed to update participations
+ */
+import documentationPartMixin from "../documentation/documentationPartMixin";
+import sendToServerMixin from "./sendToServerMixin";
+
+export default {
+  mixins: [documentationPartMixin, sendToServerMixin],
+};
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
index 6348a24f189033fc60e97325c0c69cde5d11fbc9..a49b73f149129fa86e131b804e027908c84f65db 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
@@ -19,6 +19,7 @@ query documentationsForCoursebook(
   $dateStart: Date!
   $dateEnd: Date!
   $incomplete: Boolean
+  $absencesExist: Boolean
 ) {
   items: documentationsForCoursebook(
     own: $own
@@ -27,6 +28,7 @@ query documentationsForCoursebook(
     dateStart: $dateStart
     dateEnd: $dateEnd
     incomplete: $incomplete
+    absencesExist: $absencesExist
   ) {
     id
     course {
@@ -35,8 +37,14 @@ query documentationsForCoursebook(
     }
     amends {
       id
+      title
+      slotNumberStart
+      slotNumberEnd
       amends {
         id
+        title
+        slotNumberStart
+        slotNumberEnd
         teachers {
           id
           shortName
@@ -79,6 +87,18 @@ query documentationsForCoursebook(
         shortName
         colour
       }
+      notesWithExtraMark {
+        id
+        extraMark {
+          id
+          showInCoursebook
+        }
+      }
+      notesWithNote {
+        id
+        note
+      }
+      tardiness
       isOptimistic
     }
     topic
@@ -92,6 +112,9 @@ query documentationsForCoursebook(
     canEdit
     futureNotice
     canDelete
+    futureNoticeParticipationStatus
+    canEditParticipationStatus
+    canViewParticipationStatus
   }
 }
 
@@ -116,8 +139,27 @@ mutation createOrUpdateDocumentations($input: [DocumentationInputType]!) {
           shortName
           colour
         }
+        notesWithExtraMark {
+          id
+          extraMark {
+            id
+            showInCoursebook
+          }
+        }
+        notesWithNote {
+          id
+          note
+        }
+        tardiness
         isOptimistic
       }
+      subject {
+        id
+        name
+        shortName
+        colourFg
+        colourBg
+      }
     }
   }
 }
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue
index 5bbc21c0de53b613986c30360e853ee6720526b5..1a70d655e00a3bc6c85f002d417497282387af7a 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/Documentation.vue
@@ -1,7 +1,11 @@
 <template>
   <v-card :class="{ 'my-1 full-width': true, 'd-flex flex-column': !compact }">
     <v-card-title v-if="!compact">
-      <lesson-information v-bind="documentationPartProps" />
+      <lesson-information
+        v-bind="{ ...$attrs, ...documentationPartProps }"
+        :is-create="false"
+        :gql-patch-mutation="documentationsMutation"
+      />
     </v-card-title>
 
     <v-card-text
@@ -11,7 +15,12 @@
         'pa-2': compact,
       }"
     >
-      <lesson-information v-if="compact" v-bind="documentationPartProps" />
+      <lesson-information
+        v-if="compact"
+        v-bind="documentationPartProps"
+        :is-create="false"
+        :gql-patch-mutation="documentationsMutation"
+      />
       <lesson-summary
         ref="summary"
         v-bind="{ ...$attrs, ...documentationPartProps }"
@@ -91,6 +100,6 @@ export default {
   gap: 1em;
 }
 .vertical {
-  grid-template-columns: 1fr;
+  grid-template-columns: minmax(0, 1fr);
 }
 </style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationCompactDetails.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationCompactDetails.vue
index 09ae0e5d09903387dd314bc25b8885c9eff1b370..6bfa946e18004dcb7e300f142c482bdae6414acd 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationCompactDetails.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationCompactDetails.vue
@@ -1,19 +1,25 @@
 <template>
   <v-card outlined dense rounded="lg" v-bind="$attrs" v-on="$listeners">
-    <div class="font-weight-medium mr-2">
-      {{ $t("alsijil.coursebook.summary.topic.label") }}:
-    </div>
-    <div class="text-truncate">{{ documentation.topic || "–" }}</div>
+    <template v-if="documentation.topic">
+      <div class="font-weight-medium mr-2">
+        {{ $t("alsijil.coursebook.summary.topic.label") }}:
+      </div>
+      <div class="text-truncate">{{ documentation.topic }}</div>
+    </template>
 
-    <div class="font-weight-medium mr-2">
-      {{ $t("alsijil.coursebook.summary.homework.label") }}:
-    </div>
-    <div class="text-truncate">{{ documentation.homework || "–" }}</div>
+    <template v-if="documentation.homework">
+      <div class="font-weight-medium mr-2">
+        {{ $t("alsijil.coursebook.summary.homework.label") }}:
+      </div>
+      <div class="text-truncate">{{ documentation.homework }}</div>
+    </template>
 
-    <div class="font-weight-medium mr-2">
-      {{ $t("alsijil.coursebook.summary.group_note.label") }}:
-    </div>
-    <div class="text-truncate">{{ documentation.groupNote || "–" }}</div>
+    <template v-if="documentation.groupNote">
+      <div class="font-weight-medium mr-2">
+        {{ $t("alsijil.coursebook.summary.group_note.label") }}:
+      </div>
+      <div class="text-truncate">{{ documentation.groupNote }}</div>
+    </template>
   </v-card>
 </template>
 
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationFullDetails.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationFullDetails.vue
index 58ef4285647a5b80e7c8dd94aae54955aa95c81d..e9bd354bafe68b8785c8996988b5893989a21a8c 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationFullDetails.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationFullDetails.vue
@@ -10,13 +10,23 @@
       <v-card-title class="text-subtitle-2 pb-1 font-weight-medium">
         {{ $t("alsijil.coursebook.summary.homework.label") }}
       </v-card-title>
-      <v-card-text>{{ documentation.homework || "–" }}</v-card-text>
+      <v-card-text>
+        {{
+          documentation.homework ||
+          $t("alsijil.coursebook.summary.homework.empty_yet")
+        }}
+      </v-card-text>
     </v-card>
     <v-card outlined dense rounded="lg">
       <v-card-title class="text-subtitle-2 pb-1 font-weight-medium">
         {{ $t("alsijil.coursebook.summary.group_note.label") }}
       </v-card-title>
-      <v-card-text>{{ documentation.groupNote || "–" }}</v-card-text>
+      <v-card-text>
+        {{
+          documentation.groupNote ||
+          $t("alsijil.coursebook.summary.group_note.empty")
+        }}
+      </v-card-text>
     </v-card>
   </div>
 </template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue
index 460f39f97fc15b61d0476dd99ed33d29cf43029c..5b5dd101b76288cd1b09ba5418a452d70a9425fa 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/DocumentationModal.vue
@@ -4,11 +4,24 @@
   <mobile-fullscreen-dialog v-model="popup" max-width="500px">
     <template #activator="activator">
       <!-- list view -> activate dialog -->
-      <documentation compact v-bind="$attrs" :dialog-activator="activator" />
+      <documentation
+        compact
+        v-bind="$attrs"
+        :dialog-activator="activator"
+        :extra-marks="extraMarks"
+        :absence-reasons="absenceReasons"
+        :subjects="subjects"
+      />
     </template>
     <!-- dialog view -> deactivate dialog -->
     <!-- cancel | save (through lesson-summary) -->
-    <documentation v-bind="$attrs" @close="popup = false" />
+    <documentation
+      v-bind="$attrs"
+      :extra-marks="extraMarks"
+      :absence-reasons="absenceReasons"
+      :subjects="subjects"
+      @close="popup = false"
+    />
   </mobile-fullscreen-dialog>
 </template>
 
@@ -27,5 +40,19 @@ export default {
       popup: false,
     };
   },
+  props: {
+    extraMarks: {
+      type: Array,
+      required: true,
+    },
+    absenceReasons: {
+      type: Array,
+      required: true,
+    },
+    subjects: {
+      type: Array,
+      required: true,
+    },
+  },
 };
 </script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue
index 652609dccaf430d3a4ab138f80ee2f810b84b4af..a104d84425d8317091d649ce7b0b88919fdf9c52 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonInformation.vue
@@ -1,12 +1,35 @@
 <script setup>
 import DocumentationStatus from "./DocumentationStatus.vue";
 import PersonChip from "aleksis.core/components/person/PersonChip.vue";
+import SubjectChip from "aleksis.apps.cursus/components/SubjectChip.vue";
+import SubjectChipSelectField from "aleksis.apps.cursus/components/SubjectChipSelectField.vue";
 </script>
 
 <template>
   <div :class="{ 'full-width grid': true, 'large-grid': largeGrid }">
     <div class="d-flex">
       <documentation-status v-if="compact" v-bind="documentationPartProps" />
+      <div
+        v-if="documentation.amends?.slotNumberStart"
+        :class="{
+          'text-h5 mr-3 d-flex flex-column justify-center slot-number': true,
+          'ml-2 slot-number-mobile': !largeGrid,
+        }"
+      >
+        <span
+          v-if="
+            documentation.amends?.slotNumberStart ==
+            documentation.amends?.slotNumberEnd
+          "
+        >
+          {{ documentation.amends?.slotNumberStart }}.
+        </span>
+        <span v-else>
+          {{ documentation.amends?.slotNumberStart }}.–{{
+            documentation.amends?.slotNumberEnd
+          }}.
+        </span>
+      </div>
       <div :class="{ 'text-right d-flex flex-column fit-content': largeGrid }">
         <time :datetime="documentation.datetimeStart" class="text-no-wrap">
           {{ $d(toDateTime(documentation.datetimeStart), "shortTime") }}
@@ -24,7 +47,11 @@ import PersonChip from "aleksis.core/components/person/PersonChip.vue";
         'font-weight-medium': largeGrid,
       }"
     >
-      {{ documentation.course?.name }}
+      {{
+        documentation.course?.name ||
+        documentation.amends.title ||
+        documentation.amends.amends.title
+      }}
     </span>
     <div
       :class="{
@@ -33,12 +60,21 @@ import PersonChip from "aleksis.core/components/person/PersonChip.vue";
         'justify-start': !largeGrid,
       }"
     >
-      <subject-chip
-        v-if="documentation.subject"
-        :subject="documentation.subject"
-        v-bind="compact ? dialogActivator.attrs : {}"
-        v-on="compact ? dialogActivator.on : {}"
-      />
+      <template v-if="documentation.subject">
+        <subject-chip-select-field
+          v-if="documentation.canEdit"
+          :items="subjects"
+          :value="documentation.subject"
+          :disabled="loading"
+          :loading="loading"
+          @input="editSubject"
+        />
+        <subject-chip
+          v-else
+          :subject="documentation.subject"
+          :disabled="loading"
+        />
+      </template>
       <subject-chip
         v-if="
           documentation?.amends?.amends?.subject &&
@@ -80,20 +116,32 @@ import PersonChip from "aleksis.core/components/person/PersonChip.vue";
 </template>
 
 <script>
-import SubjectChip from "aleksis.apps.cursus/components/SubjectChip.vue";
 import { DateTime } from "luxon";
+
+import createOrPatchMixin from "aleksis.core/mixins/createOrPatchMixin.js";
+
 import documentationPartMixin from "./documentationPartMixin";
+import documentationCacheUpdateMixin from "./documentationCacheUpdateMixin";
 
 export default {
   name: "LessonInformation",
-  mixins: [documentationPartMixin],
-  components: {
-    SubjectChip,
-  },
+  mixins: [
+    createOrPatchMixin,
+    documentationCacheUpdateMixin,
+    documentationPartMixin,
+  ],
   methods: {
     toDateTime(dateString) {
       return DateTime.fromISO(dateString);
     },
+    editSubject(subject) {
+      this.createOrPatch([
+        {
+          id: this.documentation.id,
+          subject: subject.id,
+        },
+      ]);
+    },
   },
   computed: {
     largeGrid() {
@@ -140,4 +188,15 @@ export default {
 .gap {
   gap: 0.25em;
 }
+
+.slot-number {
+  font-size: 1.6rem !important;
+  font-weight: 300;
+  line-height: 1.6rem;
+}
+
+.slot-number-mobile {
+  font-size: 1.4rem !important;
+  line-height: 1.4rem;
+}
 </style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue
index bc0da4a742917e0639a0c1983186fad29764babb..a412d39deed9232c8668e60716a3825096394347 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonNotes.vue
@@ -1,41 +1,238 @@
 <script setup>
 import AbsenceReasonChip from "aleksis.apps.kolego/components/AbsenceReasonChip.vue";
+import ExtraMarkChip from "../../extra_marks/ExtraMarkChip.vue";
+import ExtraMarksNote from "../personal_notes/ExtraMarksNote.vue";
+import TardinessChip from "../absences/TardinessChip.vue";
+import PersonalNoteChip from "../personal_notes/PersonalNoteChip.vue";
+import TextNoteCard from "../personal_notes/TextNoteCard.vue";
 </script>
 
 <template>
-  <div
-    class="d-flex align-center justify-space-between justify-md-end flex-wrap gap"
-  >
-    <v-chip dense color="success" outlined v-if="total > 0">
-      {{ $t("alsijil.coursebook.present_number", { present, total }) }}
-    </v-chip>
-    <absence-reason-chip
-      v-for="[reasonId, participations] in Object.entries(absences)"
-      :key="'reason-' + reasonId"
-      :absence-reason="participations[0].absenceReason"
-      dense
+  <div>
+    <div
+      class="d-flex align-center justify-space-between justify-md-end flex-wrap gap"
+      v-if="compact || documentation.canViewParticipationStatus"
     >
-      <template #append>
-        <span
-          >:
-          <span>
-            {{
-              participations
-                .slice(0, 5)
-                .map((participation) => participation.person.firstName)
-                .join(", ")
-            }}
-          </span>
-          <span v-if="participations.length > 5">
-            <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
-            +{{ participations.length - 5 }}
-            <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
-          </span>
-        </span>
+      <v-chip
+        dense
+        color="success"
+        outlined
+        v-if="total > 0 && documentation.canViewParticipationStatus"
+      >
+        {{
+          $t("alsijil.coursebook.participations.present_number", {
+            present,
+            total,
+          })
+        }}
+      </v-chip>
+      <v-chip
+        dense
+        color="success"
+        outlined
+        @click="$emit('open')"
+        v-bind="dialogActivator.attrs"
+        v-on="dialogActivator.on"
+        v-else-if="
+          total == 1 &&
+          present == 1 &&
+          !documentation.canViewParticipationStatus
+        "
+      >
+        {{ $t("alsijil.coursebook.participations.present") }}
+      </v-chip>
+
+      <template v-if="documentation.canViewParticipationStatus">
+        <absence-reason-chip
+          v-for="[reasonId, participations] in Object.entries(absences)"
+          :key="'reason-' + reasonId"
+          :absence-reason="participations[0].absenceReason"
+          dense
+        >
+          <template #append>
+            <span
+              >:
+              <span>
+                {{
+                  participations
+                    .slice(0, 5)
+                    .map((participation) => participation.person.firstName)
+                    .join(", ")
+                }}
+              </span>
+              <span v-if="participations.length > 5">
+                <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
+                +{{ participations.length - 5 }}
+                <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
+              </span>
+            </span>
+          </template>
+        </absence-reason-chip>
+      </template>
+      <template v-else>
+        <absence-reason-chip
+          v-for="[reasonId, participations] in Object.entries(absences)"
+          :key="'reason-' + reasonId"
+          :absence-reason="participations[0].absenceReason"
+          dense
+          @click="$emit('open')"
+          v-bind="dialogActivator.attrs"
+          v-on="dialogActivator.on"
+        />
+      </template>
+
+      <template v-if="documentation.canViewParticipationStatus">
+        <extra-mark-chip
+          v-for="[markId, [mark, ...participations]] in Object.entries(
+            extraMarkChips,
+          )"
+          :key="'extra-mark-' + markId"
+          :extra-mark="mark"
+          dense
+        >
+          <template #append>
+            <span
+              >:
+              <span>
+                {{
+                  participations
+                    .slice(0, 5)
+                    .map((participation) => participation.person.firstName)
+                    .join(", ")
+                }}
+              </span>
+              <span v-if="participations.length > 5">
+                <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
+                +{{ participations.length - 5 }}
+                <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
+              </span>
+            </span>
+          </template>
+        </extra-mark-chip>
+      </template>
+      <template v-else>
+        <extra-mark-chip
+          v-for="[markId, [mark, ...participations]] in Object.entries(
+            extraMarkChips,
+          )"
+          :key="'extra-mark-' + markId"
+          :extra-mark="mark"
+          dense
+          @click="$emit('open')"
+          v-bind="dialogActivator.attrs"
+          v-on="dialogActivator.on"
+        />
+      </template>
+
+      <template v-if="documentation.canViewParticipationStatus">
+        <tardiness-chip v-if="tardyParticipations.length > 0">
+          <template #default>
+            {{ $t("alsijil.personal_notes.late") }}
+          </template>
+
+          <template #append>
+            <span
+              >:
+              {{
+                tardyParticipations
+                  .slice(0, 5)
+                  .map((participation) => participation.person.firstName)
+                  .join(", ")
+              }}
+
+              <span v-if="tardyParticipations.length > 5">
+                <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
+                +{{ tardyParticipations.length - 5 }}
+                <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
+              </span>
+            </span>
+          </template>
+        </tardiness-chip>
+      </template>
+      <template v-else>
+        <tardiness-chip
+          v-if="tardyParticipations.length > 0"
+          :tardiness="
+            tardyParticipations.length == 1
+              ? tardyParticipations[0].tardiness
+              : undefined
+          "
+          @click="$emit('open')"
+          v-bind="dialogActivator.attrs"
+          v-on="dialogActivator.on"
+        />
+      </template>
+
+      <template v-if="!documentation.canViewParticipationStatus && total == 1">
+        <personal-note-chip
+          v-for="note in documentation?.participations[0]?.notesWithNote"
+          :key="'text-note-note-' + note.id"
+          :note="note"
+          @click="$emit('open')"
+          v-bind="dialogActivator.attrs"
+          v-on="dialogActivator.on"
+        />
       </template>
-    </absence-reason-chip>
 
-    <manage-students-trigger v-bind="documentationPartProps" />
+      <manage-students-trigger
+        v-if="documentation.canEditParticipationStatus"
+        :label-key="manageStudentsLabelKey"
+        v-bind="documentationPartProps"
+      />
+    </div>
+
+    <!-- not compact -->
+    <div class="main-body" v-else>
+      <template
+        v-if="
+          tardyParticipations.length > 0 || Object.entries(absences).length > 0
+        "
+      >
+        <v-divider />
+        <div
+          class="d-flex align-center justify-space-between justify-md-end flex-wrap gap"
+        >
+          <tardiness-chip
+            v-if="tardyParticipations.length > 0"
+            :tardiness="
+              tardyParticipations.length == 1
+                ? tardyParticipations[0].tardiness
+                : undefined
+            "
+          />
+          <absence-reason-chip
+            v-for="[reasonId, participations] in Object.entries(absences)"
+            :key="'reason-' + reasonId"
+            :absence-reason="participations[0].absenceReason"
+            dense
+          />
+        </div>
+      </template>
+      <template v-if="total == 1">
+        <v-divider />
+        <extra-marks-note
+          v-bind="documentationPartProps"
+          :participation="documentation?.participations[0]"
+          :value="documentation?.participations[0].notesWithExtraMark"
+          :disabled="true"
+        />
+      </template>
+      <template
+        v-if="
+          total == 1 &&
+          documentation?.participations[0]?.notesWithNote.length > 0
+        "
+      >
+        <v-divider />
+        <div>
+          <text-note-card
+            v-for="note in documentation?.participations[0]?.notesWithNote"
+            :key="'text-note-note-' + note.id"
+            :note="note"
+          />
+        </div>
+      </template>
+    </div>
   </div>
 </template>
 
@@ -51,13 +248,18 @@ export default {
     total() {
       return this.documentation.participations.length;
     },
+    /**
+     * Return the number of present people.
+     */
     present() {
       return this.documentation.participations.filter(
         (p) => p.absenceReason === null,
       ).length;
     },
+    /**
+     * Get all course attendants who have an absence reason, grouped by that reason.
+     */
     absences() {
-      // Get all course attendants who have an absence reason
       return Object.groupBy(
         this.documentation.participations.filter(
           (p) => p.absenceReason !== null,
@@ -65,6 +267,48 @@ export default {
         ({ absenceReason }) => absenceReason.id,
       );
     },
+    /**
+     * Parse and combine all extraMark notes.
+     *
+     * Notes with extraMarks are grouped by ExtraMark. ExtraMarks with the showInCoursebook property set to false are ignored.
+     * @return An object where the keys are extraMark IDs and the values have the structure [extraMark, note1, note2, ..., noteN]
+     */
+    extraMarkChips() {
+      // Apply the inner function to each participation, with value being the resulting object
+      return this.documentation.participations.reduce((value, p) => {
+        // Go through every extra mark of this participation
+        for (const { extraMark } of p.notesWithExtraMark) {
+          // Only proceed if the extraMark should be displayed here
+          if (!extraMark.showInCoursebook) {
+            continue;
+          }
+
+          // value[extraMark.id] is an Array with the structure [extraMark, note1, note2, ..., noteN]
+          if (value[extraMark.id]) {
+            value[extraMark.id].push(p);
+          } else {
+            value[extraMark.id] = [
+              this.extraMarks.find((e) => e.id === extraMark.id),
+              p,
+            ];
+          }
+        }
+
+        return value;
+      }, {});
+    },
+    /**
+     * Return a list Participations with a set tardiness
+     */
+    tardyParticipations() {
+      return this.documentation.participations.filter((p) => p.tardiness);
+    },
+    manageStudentsLabelKey() {
+      if (this.total == 0) {
+        return "alsijil.coursebook.notes.show_list";
+      }
+      return "";
+    },
   },
 };
 </script>
@@ -73,4 +317,9 @@ export default {
 .gap {
   gap: 0.25em;
 }
+.main-body {
+  display: grid;
+  grid-template-columns: minmax(0, 1fr);
+  gap: 1em;
+}
 </style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue
index f4ef410e2f12f17d7e284178e7a2b6acc8022e66..3710a45f83c7d267ba281f95d2cbbaaae95f46f3 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/LessonSummary.vue
@@ -146,10 +146,15 @@ import DocumentationFullDetails from "./DocumentationFullDetails.vue";
 <script>
 import createOrPatchMixin from "aleksis.core/mixins/createOrPatchMixin.js";
 import documentationPartMixin from "./documentationPartMixin";
+import documentationCacheUpdateMixin from "./documentationCacheUpdateMixin";
 
 export default {
   name: "LessonSummary",
-  mixins: [createOrPatchMixin, documentationPartMixin],
+  mixins: [
+    createOrPatchMixin,
+    documentationCacheUpdateMixin,
+    documentationPartMixin,
+  ],
   emits: ["open"],
   data() {
     return {
@@ -161,28 +166,6 @@ export default {
     };
   },
   methods: {
-    handleUpdateAfterCreateOrPatch(itemId) {
-      return (cached, incoming) => {
-        for (const object of incoming) {
-          console.log("summary: handleUpdateAfterCreateOrPatch", object);
-          // Replace the current documentation
-          const index = cached.findIndex(
-            (o) => o[itemId] === this.documentation.id,
-          );
-          // merged with the incoming partial documentation
-          // if creation of proper documentation from dummy one, set ID of documentation currently being edited as oldID so that key in coursebook doesn't change
-          cached[index] = {
-            ...this.documentation,
-            ...object,
-            oldId:
-              this.documentation.id !== object.id
-                ? this.documentation.id
-                : this.documentation.oldId,
-          };
-        }
-        return cached;
-      };
-    },
     handleAppendIconSuccess() {
       this.topicError = null;
       this.appendIcon = "$success";
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/documentationCacheUpdateMixin.js b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/documentationCacheUpdateMixin.js
new file mode 100644
index 0000000000000000000000000000000000000000..0e00a61a6cbcd5fbadbe5638a9138730acbf1369
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/documentationCacheUpdateMixin.js
@@ -0,0 +1,29 @@
+/**
+ * Mixin to provide the cache update functionality used after creating or patching documentations
+ */
+export default {
+  methods: {
+    handleUpdateAfterCreateOrPatch(itemId) {
+      return (cached, incoming) => {
+        for (const object of incoming) {
+          console.log("summary: handleUpdateAfterCreateOrPatch", object);
+          // Replace the current documentation
+          const index = cached.findIndex(
+            (o) => o[itemId] === this.documentation.id,
+          );
+          // merged with the incoming partial documentation
+          // if creation of proper documentation from dummy one, set ID of documentation currently being edited as oldID so that key in coursebook doesn't change
+          cached[index] = {
+            ...this.documentation,
+            ...object,
+            oldId:
+              this.documentation.id !== object.id
+                ? this.documentation.id
+                : this.documentation.oldId,
+          };
+        }
+        return cached;
+      };
+    },
+  },
+};
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/documentationPartMixin.js b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/documentationPartMixin.js
index 88a8e852f8cc6e333303034fb5f590d174708886..2b30e38d57a9f803b56a4e0ea1c8f7c5c27dc49f 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/documentation/documentationPartMixin.js
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/documentation/documentationPartMixin.js
@@ -33,6 +33,27 @@ export default {
       required: false,
       default: () => ({ attrs: {}, on: {} }),
     },
+    /**
+     * Once loaded list of all extra marks to avoid excessive network and database queries
+     */
+    extraMarks: {
+      type: Array,
+      required: true,
+    },
+    /**
+     * Once loaded list of absence reasons to avoid excessive network and database queries
+     */
+    absenceReasons: {
+      type: Array,
+      required: true,
+    },
+    /**
+     * Once loaded list of subjects to avoid excessive network and database queries
+     */
+    subjects: {
+      type: Array,
+      required: true,
+    },
   },
 
   computed: {
@@ -46,6 +67,9 @@ export default {
         compact: this.compact,
         dialogActivator: this.dialogActivator,
         affectedQuery: this.affectedQuery,
+        extraMarks: this.extraMarks,
+        absenceReasons: this.absenceReasons,
+        subjects: this.subjects,
       };
     },
   },
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/ExtraMarkNoteCheckbox.vue b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/ExtraMarkNoteCheckbox.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e6985a1f854b9915d6effb282674b0ca9bd478f0
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/ExtraMarkNoteCheckbox.vue
@@ -0,0 +1,104 @@
+<script>
+import {
+  createPersonalNotes,
+  deletePersonalNotes,
+} from "./personal_notes.graphql";
+import personalNoteRelatedMixin from "./personalNoteRelatedMixin";
+import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
+
+export default {
+  name: "ExtraMarkNoteCheckbox",
+  mixins: [mutateMixin, personalNoteRelatedMixin],
+  props: {
+    personalNote: {
+      type: Object,
+      default: null,
+    },
+    /**
+     * Extra Mark
+     */
+    value: {
+      type: Object,
+      required: true,
+    },
+  },
+  computed: {
+    model: {
+      get() {
+        return !!this.personalNote?.id;
+      },
+      set(newValue) {
+        if (newValue && !this.personalNote) {
+          // CREATE new personal note
+          this.mutate(
+            createPersonalNotes,
+            {
+              input: [
+                {
+                  documentation: this.documentation.id,
+                  person: this.participation.person.id,
+                  extraMark: this.value.id,
+                },
+              ],
+            },
+            (storedDocumentations, incomingPersonalNotes) => {
+              const note = incomingPersonalNotes[0];
+              const documentation = storedDocumentations.find(
+                (doc) => doc.id === this.documentation.id,
+              );
+              const participationStatus = documentation.participations.find(
+                (part) => part.id === this.participation.id,
+              );
+              participationStatus.notesWithExtraMark.push(note);
+
+              return storedDocumentations;
+            },
+          );
+        } else {
+          // DELETE personal note
+          this.mutate(
+            deletePersonalNotes,
+            {
+              ids: [this.personalNote.id],
+            },
+            (storedDocumentations) => {
+              const documentation = storedDocumentations.find(
+                (doc) => doc.id === this.documentation.id,
+              );
+              const participationStatus = documentation.participations.find(
+                (part) => part.id === this.participation.id,
+              );
+              const index = participationStatus.notesWithExtraMark.findIndex(
+                (n) => n.id === this.personalNote.id,
+              );
+              participationStatus.notesWithExtraMark.splice(index, 1);
+
+              return storedDocumentations;
+            },
+          );
+        }
+      },
+    },
+  },
+};
+</script>
+
+<template>
+  <v-checkbox
+    :label="value.name"
+    :value="value.id"
+    v-model="model"
+    :disabled="$attrs?.disabled || loading"
+    :true-value="true"
+    :false-value="false"
+  >
+    <template #append>
+      <v-progress-circular
+        v-if="loading"
+        indeterminate
+        :size="16"
+        :width="2"
+      ></v-progress-circular>
+    </template>
+  </v-checkbox>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/ExtraMarksNote.vue b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/ExtraMarksNote.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f6bed5bb8f03e7442aebad04ec01346eee801fbd
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/ExtraMarksNote.vue
@@ -0,0 +1,28 @@
+<script>
+import ExtraMarkNoteCheckbox from "./ExtraMarkNoteCheckbox.vue";
+import personalNoteRelatedMixin from "./personalNoteRelatedMixin";
+
+export default {
+  name: "ExtraMarksNote",
+  components: { ExtraMarkNoteCheckbox },
+  mixins: [personalNoteRelatedMixin],
+  props: {
+    value: {
+      type: Array,
+      required: true,
+    },
+  },
+};
+</script>
+
+<template>
+  <div>
+    <extra-mark-note-checkbox
+      v-for="extraMark in extraMarks"
+      :key="'checkbox-extramark-' + extraMark.id"
+      v-bind="{ ...personalNoteRelatedProps, ...$attrs }"
+      :value="extraMark"
+      :personal-note="value.find((pn) => pn.extraMark.id === extraMark.id)"
+    />
+  </div>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/PersonalNoteChip.vue b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/PersonalNoteChip.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5d7326894ecbd367b83ac08b385732d17e2a5310
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/PersonalNoteChip.vue
@@ -0,0 +1,45 @@
+<script>
+export default {
+  name: "PersonalNoteChip",
+  props: {
+    note: {
+      type: Object,
+      required: true,
+    },
+    loading: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
+  },
+  extends: "v-chip",
+};
+</script>
+
+<template>
+  <v-tooltip bottom>
+    <template #activator="{ on, attrs }">
+      <v-chip
+        dense
+        outlined
+        v-bind="{ ...$attrs, ...attrs }"
+        v-on="{ ...$listeners, ...on }"
+      >
+        <v-avatar left>
+          <v-icon small>mdi-note-outline</v-icon>
+        </v-avatar>
+        <slot name="prepend" />
+        <slot>
+          <span class="text-truncate" style="max-width: 30ch">
+            {{ note.note }}
+          </span>
+        </slot>
+        <slot name="append" />
+        <v-avatar right v-if="loading">
+          <v-progress-circular indeterminate :size="16" :width="2" />
+        </v-avatar>
+      </v-chip>
+    </template>
+    <span v-text="note.note" />
+  </v-tooltip>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/PersonalNotes.vue b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/PersonalNotes.vue
new file mode 100644
index 0000000000000000000000000000000000000000..2a3d953f03484ef2af2f1ac7a8f876bcb658f8bf
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/PersonalNotes.vue
@@ -0,0 +1,28 @@
+<script setup>
+import ExtraMarksNote from "./ExtraMarksNote.vue";
+import TextNotes from "./TextNotes.vue";
+</script>
+<script>
+import personalNoteRelatedMixin from "./personalNoteRelatedMixin";
+
+export default {
+  name: "PersonalNotes",
+  mixins: [personalNoteRelatedMixin],
+};
+</script>
+
+<template>
+  <div>
+    <text-notes
+      v-bind="personalNoteRelatedProps"
+      :value="participation.notesWithNote"
+    />
+
+    <extra-marks-note
+      v-bind="personalNoteRelatedProps"
+      :value="participation.notesWithExtraMark"
+    />
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNote.vue b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNote.vue
new file mode 100644
index 0000000000000000000000000000000000000000..43175c7402100c5ef9e4b84c153a8e79a9bb7f76
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNote.vue
@@ -0,0 +1,164 @@
+<script>
+import {
+  createPersonalNotes,
+  deletePersonalNotes,
+  updatePersonalNotes,
+} from "./personal_notes.graphql";
+import personalNoteRelatedMixin from "./personalNoteRelatedMixin";
+import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
+import DeleteDialog from "aleksis.core/components/generic/dialogs/DeleteDialog.vue";
+
+export default {
+  name: "TextNote",
+  components: { DeleteDialog },
+  mixins: [mutateMixin, personalNoteRelatedMixin],
+  props: {
+    value: {
+      type: Object,
+      required: true,
+    },
+  },
+  computed: {
+    model: {
+      get() {
+        return this.value.note;
+      },
+      set(newValue) {
+        if (!newValue) {
+          // this is a DELETE action, show the dialog, ...
+          this.showDeleteConfirm = true;
+          return;
+        }
+        const create = !this.value.id;
+
+        this.mutate(
+          create ? createPersonalNotes : updatePersonalNotes,
+          this.getInput(
+            newValue,
+            create
+              ? {
+                  documentation: this.documentation.id,
+                  person: this.participation.person.id,
+                  extraMark: null,
+                }
+              : {
+                  id: this.value.id,
+                },
+          ),
+          this.getUpdater(create ? "create" : "update"),
+        );
+      },
+    },
+  },
+  methods: {
+    getInput(newValue, extras) {
+      return {
+        input: [
+          {
+            note: newValue,
+            ...extras,
+          },
+        ],
+      };
+    },
+    getUpdater(mode) {
+      return (storedDocumentations, incomingPersonalNotes) => {
+        const note = incomingPersonalNotes?.[0] || undefined;
+        const documentation = storedDocumentations.find(
+          (doc) => doc.id === this.documentation.id,
+        );
+        const participationStatus = documentation.participations.find(
+          (part) => part.id === this.participation.id,
+        );
+        switch (mode.toLowerCase()) {
+          case "update":
+          case "delete": {
+            const updateIndex = participationStatus.notesWithNote.findIndex(
+              (n) => n.id === this.value.id,
+            );
+            if (mode === "update") {
+              participationStatus.notesWithNote.splice(updateIndex, 1, note);
+            } else {
+              participationStatus.notesWithNote.splice(updateIndex, 1);
+            }
+
+            break;
+          }
+
+          case "create":
+            participationStatus.notesWithNote.push(note);
+
+            this.$emit("create");
+            break;
+
+          default:
+            console.error("Invalid mode in getUpdater():", mode);
+            break;
+        }
+
+        return storedDocumentations;
+      };
+    },
+  },
+  data() {
+    return {
+      showDeleteConfirm: false,
+      deletePersonalNotes,
+    };
+  },
+};
+</script>
+
+<template>
+  <v-textarea
+    auto-grow
+    :rows="3"
+    outlined
+    hide-details
+    class="mb-4"
+    :label="$t('alsijil.personal_notes.note')"
+    :value="model"
+    @change="model = $event"
+    :loading="loading"
+  >
+    <template #append>
+      <v-slide-x-reverse-transition>
+        <v-btn
+          v-if="!!model"
+          icon
+          @click="showDeleteConfirm = true"
+          class="mt-n1-5"
+        >
+          <v-icon> $deleteContent </v-icon>
+        </v-btn>
+      </v-slide-x-reverse-transition>
+
+      <delete-dialog
+        v-model="showDeleteConfirm"
+        :gql-delete-mutation="deletePersonalNotes"
+        :affected-query="affectedQuery"
+        item-attribute="fullName"
+        :items="[value]"
+        :custom-update="getUpdater('delete')"
+      >
+        <template #title>
+          {{ $t("alsijil.personal_notes.confirm_delete") }}
+        </template>
+        <template #body>
+          {{
+            $t("alsijil.personal_notes.confirm_delete_explanation", {
+              note: value.note,
+              name: participation.person.fullName,
+            })
+          }}
+        </template>
+      </delete-dialog>
+    </template>
+  </v-textarea>
+</template>
+
+<style scoped>
+.mt-n1-5 {
+  margin-top: -6px;
+}
+</style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNoteCard.vue b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNoteCard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f180603b7563f36f5ecd59bcce134d586bae0bda
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNoteCard.vue
@@ -0,0 +1,27 @@
+<template>
+  <v-card
+    outlined
+    dense
+    rounded="lg"
+    class="mb-2"
+    v-bind="$attrs"
+    v-on="$listeners"
+  >
+    <v-card-title class="text-subtitle-2 pb-1 font-weight-medium">
+      {{ $t("alsijil.personal_notes.card.title") }}
+    </v-card-title>
+    <v-card-text>{{ note.note || "–" }}</v-card-text>
+  </v-card>
+</template>
+
+<script>
+export default {
+  name: "TextNoteCard",
+  props: {
+    note: {
+      type: Object,
+      required: true,
+    },
+  },
+};
+</script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNotes.vue b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNotes.vue
new file mode 100644
index 0000000000000000000000000000000000000000..dd32164db251b16acdc8b61daa29559b9a776fde
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/TextNotes.vue
@@ -0,0 +1,48 @@
+<script setup>
+import SecondaryActionButton from "aleksis.core/components/generic/buttons/SecondaryActionButton.vue";
+import TextNote from "./TextNote.vue";
+</script>
+<script>
+import personalNoteRelatedMixin from "./personalNoteRelatedMixin";
+
+export default {
+  name: "TextNotes",
+  mixins: [personalNoteRelatedMixin],
+  props: {
+    value: {
+      type: Array,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      showNewNote: true,
+    };
+  },
+  computed: {
+    notes() {
+      return this.showNewNote ? [...this.value, { note: "" }] : this.value;
+    },
+  },
+};
+</script>
+
+<template>
+  <div>
+    <text-note
+      v-for="note in notes"
+      :key="note.id || -1"
+      v-bind="personalNoteRelatedProps"
+      :value="note"
+      @create="showNewNote = false"
+    />
+
+    <secondary-action-button
+      i18n-key="alsijil.personal_notes.create_personal_note"
+      icon-text="$plus"
+      class="full-width"
+      @click="showNewNote = true"
+      :disabled="showNewNote"
+    />
+  </div>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/personalNoteRelatedMixin.js b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/personalNoteRelatedMixin.js
new file mode 100644
index 0000000000000000000000000000000000000000..0eda6c4ca625afb16742d9ea8e585254d041e970
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/personalNoteRelatedMixin.js
@@ -0,0 +1,19 @@
+import documentationPartMixin from "../documentation/documentationPartMixin";
+
+export default {
+  mixins: [documentationPartMixin],
+  props: {
+    participation: {
+      type: Object,
+      required: true,
+    },
+  },
+  computed: {
+    personalNoteRelatedProps() {
+      return {
+        ...this.documentationPartProps,
+        participation: this.participation,
+      };
+    },
+  },
+};
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/personal_notes.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/personal_notes.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..014178c1326960f02dee24cf92f8eb5c679298f5
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/personal_notes/personal_notes.graphql
@@ -0,0 +1,45 @@
+query personalNotes($orderBy: [String], $filters: JSONString) {
+  items: personalNotes(orderBy: $orderBy, filters: $filters) {
+    id
+    note
+    extraMark {
+      id
+    }
+    canEdit
+    canDelete
+  }
+}
+
+mutation createPersonalNotes($input: [BatchCreatePersonalNoteInput]!) {
+  createPersonalNotes(input: $input) {
+    items: personalNotes {
+      id
+      note
+      extraMark {
+        id
+      }
+      canEdit
+      canDelete
+    }
+  }
+}
+
+mutation deletePersonalNotes($ids: [ID]!) {
+  deletePersonalNotes(ids: $ids) {
+    deletionCount
+  }
+}
+
+mutation updatePersonalNotes($input: [BatchPatchPersonalNoteInput]!) {
+  updatePersonalNotes(input: $input) {
+    items: personalNotes {
+      id
+      note
+      extraMark {
+        id
+      }
+      canEdit
+      canDelete
+    }
+  }
+}
diff --git a/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkButtons.vue b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkButtons.vue
new file mode 100644
index 0000000000000000000000000000000000000000..919749ac85ed97f937d5b862a6aa37d199529b30
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkButtons.vue
@@ -0,0 +1,73 @@
+<script>
+import { extraMarks } from "./extra_marks.graphql";
+
+export default {
+  name: "ExtraMarkButtons",
+  data() {
+    return {
+      extraMarks: [],
+    };
+  },
+  apollo: {
+    extraMarks: {
+      query: extraMarks,
+      update: (data) => data.items,
+      skip() {
+        return this.customExtraMarks > 0;
+      },
+    },
+  },
+  props: {
+    customExtraMarks: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
+  },
+  computed: {
+    innerExtraMarks() {
+      if (this.customExtraMarks.length > 0) {
+        return this.customExtraMarks;
+      } else {
+        return this.extraMarks;
+      }
+    },
+  },
+  methods: {
+    emit(value) {
+      this.$emit("input", value);
+      this.$emit("click", value);
+    },
+  },
+};
+</script>
+
+<template>
+  <div class="d-flex flex-wrap" style="gap: 0.5em">
+    <v-skeleton-loader
+      class="full-width d-flex flex-wrap child-flex-grow-1"
+      style="gap: 0.5em"
+      v-if="$apollo.queries.extraMarks.loading"
+      type="button@4"
+    />
+    <template v-else>
+      <v-btn
+        v-for="extraMark in innerExtraMarks"
+        :key="'extra-mark-' + extraMark.id"
+        :color="extraMark.colourBg"
+        :style="{ color: extraMark.colourFg }"
+        class="flex-grow-1"
+        depressed
+        @click="emit(extraMark.id)"
+      >
+        {{ extraMark.name }}
+      </v-btn>
+    </template>
+  </div>
+</template>
+
+<style>
+.child-flex-grow-1 > * {
+  flex-grow: 1;
+}
+</style>
diff --git a/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkChip.vue b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkChip.vue
new file mode 100644
index 0000000000000000000000000000000000000000..da5cd4182d6f25965374934a31d64691dbdf852f
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkChip.vue
@@ -0,0 +1,49 @@
+<script>
+import CounterChip from "aleksis.core/components/generic/chips/CounterChip.vue";
+
+export default {
+  name: "ExtraMarkChip",
+  components: { CounterChip },
+  props: {
+    extraMark: {
+      type: Object,
+      required: true,
+    },
+    short: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
+    loading: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
+  },
+  extends: CounterChip,
+  computed: {
+    text() {
+      return this.short ? this.extraMark.shortName : this.extraMark.name;
+    },
+  },
+};
+</script>
+
+<template>
+  <counter-chip
+    :color="extraMark.colourBg"
+    :text-color="extraMark.colourFg"
+    :value="extraMark.id"
+    :count="count"
+    :outlined="false"
+    v-bind="$attrs"
+    v-on="$listeners"
+  >
+    <slot name="prepend" />
+    {{ text }}
+    <slot name="append" />
+    <v-avatar right v-if="loading">
+      <v-progress-circular indeterminate :size="16" :width="2" />
+    </v-avatar>
+  </counter-chip>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarks.vue b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarks.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9468ac1dba5d291abd251659586d24aa95fe0278
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarks.vue
@@ -0,0 +1,138 @@
+<script setup>
+import ColorField from "aleksis.core/components/generic/forms/ColorField.vue";
+import InlineCRUDList from "aleksis.core/components/generic/InlineCRUDList.vue";
+</script>
+
+<template>
+  <v-container>
+    <inline-c-r-u-d-list
+      :headers="headers"
+      :i18n-key="i18nKey"
+      create-item-i18n-key="alsijil.extra_marks.create"
+      :gql-query="gqlQuery"
+      :gql-create-mutation="gqlCreateMutation"
+      :gql-patch-mutation="gqlPatchMutation"
+      :gql-delete-mutation="gqlDeleteMutation"
+      :default-item="defaultItem"
+    >
+      <!-- eslint-disable-next-line vue/valid-v-slot -->
+      <template #name.field="{ attrs, on }">
+        <div aria-required="true">
+          <v-text-field
+            v-bind="attrs"
+            v-on="on"
+            :rules="$rules().required.build()"
+          />
+        </div>
+      </template>
+
+      <!-- eslint-disable-next-line vue/valid-v-slot -->
+      <template #shortName.field="{ attrs, on }">
+        <div aria-required="true">
+          <v-text-field
+            v-bind="attrs"
+            v-on="on"
+            :rules="$rules().required.build()"
+          />
+        </div>
+      </template>
+
+      <template #colourFg="{ item }">
+        <v-chip :color="item.colourFg" outlined v-if="item.colourFg">
+          {{ item.colourFg }}
+        </v-chip>
+        <span v-else>–</span>
+      </template>
+      <!-- eslint-disable-next-line vue/valid-v-slot -->
+      <template #colourFg.field="{ attrs, on }">
+        <color-field v-bind="attrs" v-on="on" />
+      </template>
+
+      <template #colourBg="{ item }">
+        <v-chip :color="item.colourBg" outlined v-if="item.colourBg">
+          {{ item.colourBg }}
+        </v-chip>
+        <span v-else>–</span>
+      </template>
+      <!-- eslint-disable-next-line vue/valid-v-slot -->
+      <template #colourBg.field="{ attrs, on }">
+        <color-field v-bind="attrs" v-on="on" />
+      </template>
+
+      <template #showInCoursebook="{ item }">
+        <v-switch
+          :input-value="item.showInCoursebook"
+          disabled
+          inset
+          :false-value="false"
+          :true-value="true"
+        />
+      </template>
+      <!-- eslint-disable-next-line vue/valid-v-slot -->
+      <template #showInCoursebook.field="{ attrs, on }">
+        <v-switch
+          v-bind="attrs"
+          v-on="on"
+          inset
+          :false-value="false"
+          :true-value="true"
+          :hint="$t('alsijil.extra_marks.show_in_coursebook_helptext')"
+          persistent-hint
+        />
+      </template>
+    </inline-c-r-u-d-list>
+  </v-container>
+</template>
+
+<script>
+import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js";
+import {
+  extraMarks,
+  createExtraMarks,
+  deleteExtraMarks,
+  updateExtraMarks,
+} from "./extra_marks.graphql";
+
+export default {
+  name: "ExtraMarks",
+  mixins: [formRulesMixin],
+  data() {
+    return {
+      headers: [
+        {
+          text: this.$t("alsijil.extra_marks.short_name"),
+          value: "shortName",
+        },
+        {
+          text: this.$t("alsijil.extra_marks.name"),
+          value: "name",
+        },
+        {
+          text: this.$t("alsijil.extra_marks.colour_fg"),
+          value: "colourFg",
+        },
+        {
+          text: this.$t("alsijil.extra_marks.colour_bg"),
+          value: "colourBg",
+        },
+        {
+          text: this.$t("alsijil.extra_marks.show_in_coursebook"),
+          value: "showInCoursebook",
+        },
+      ],
+      i18nKey: "alsijil.extra_marks",
+      gqlQuery: extraMarks,
+      gqlCreateMutation: createExtraMarks,
+      gqlPatchMutation: updateExtraMarks,
+      gqlDeleteMutation: deleteExtraMarks,
+      defaultItem: {
+        shortName: "",
+        name: "",
+        colourFg: "",
+        colourBg: "",
+        showInCoursebook: true,
+      },
+    };
+  },
+};
+</script>
diff --git a/aleksis/apps/alsijil/frontend/components/extra_marks/extra_marks.graphql b/aleksis/apps/alsijil/frontend/components/extra_marks/extra_marks.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..73e8ba4121c215dc4c3968b3ed2021b71b5ecfc1
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/extra_marks/extra_marks.graphql
@@ -0,0 +1,48 @@
+query extraMarks($orderBy: [String], $filters: JSONString) {
+  items: extraMarks(orderBy: $orderBy, filters: $filters) {
+    id
+    shortName
+    name
+    colourFg
+    colourBg
+    showInCoursebook
+    canEdit
+    canDelete
+  }
+}
+
+mutation createExtraMarks($input: [BatchCreateExtraMarkInput]!) {
+  createExtraMarks(input: $input) {
+    items: extraMarks {
+      id
+      shortName
+      name
+      colourFg
+      colourBg
+      showInCoursebook
+      canEdit
+      canDelete
+    }
+  }
+}
+
+mutation deleteExtraMarks($ids: [ID]!) {
+  deleteExtraMarks(ids: $ids) {
+    deletionCount
+  }
+}
+
+mutation updateExtraMarks($input: [BatchPatchExtraMarkInput]!) {
+  updateExtraMarks(input: $input) {
+    items: extraMarks {
+      id
+      shortName
+      name
+      colourFg
+      colourBg
+      showInCoursebook
+      canEdit
+      canDelete
+    }
+  }
+}
diff --git a/aleksis/apps/alsijil/frontend/index.js b/aleksis/apps/alsijil/frontend/index.js
index c7cc473f95e134565a701d11e95afbed1dc6a7ba..e63cfc25222367f53265f4041e95879f5f4dece2 100644
--- a/aleksis/apps/alsijil/frontend/index.js
+++ b/aleksis/apps/alsijil/frontend/index.js
@@ -1,7 +1,4 @@
-import {
-  notLoggedInValidator,
-  hasPersonValidator,
-} from "aleksis.core/routeValidators";
+import { hasPersonValidator } from "aleksis.core/routeValidators";
 import { DateTime } from "luxon";
 
 export const collectionItems = {
@@ -30,45 +27,6 @@ export default {
     byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
   },
   children: [
-    {
-      path: "extra_marks/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "alsijil.extraMarks",
-      meta: {
-        inMenu: true,
-        titleKey: "alsijil.extra_marks.menu_title",
-        icon: "mdi-label-variant-outline",
-        iconActive: "mdi-label-variant",
-        permission: "alsijil.view_extramarks_rule",
-      },
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "extra_marks/create/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "alsijil.createExtraMark",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "extra_marks/:pk(\\d+)/edit/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "alsijil.editExtraMark",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "extra_marks/:pk(\\d+)/delete/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "alsijil.deleteExtraMark",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
     {
       path: "coursebook/",
       component: () => import("./components/coursebook/Coursebook.vue"),
@@ -77,6 +35,7 @@ export default {
           name: "alsijil.coursebook",
           params: {
             filterType: "my",
+            pageType: "documentations",
           },
           hash: "#" + DateTime.now().toISODate(),
         };
@@ -93,7 +52,7 @@ export default {
       },
       children: [
         {
-          path: ":filterType(my|all)/:objType(group|course|teacher)?/:objId(\\d+)?/",
+          path: ":pageType(documentations|absences)/:filterType(my|all)/:objType(group|course|teacher)?/:objId(\\d+)?/",
           component: () => import("./components/coursebook/Coursebook.vue"),
           name: "alsijil.coursebook",
           meta: {
@@ -105,6 +64,18 @@ export default {
         },
       ],
     },
+    {
+      path: "extra_marks/",
+      component: () => import("./components/extra_marks/ExtraMarks.vue"),
+      name: "alsijil.extraMarks",
+      meta: {
+        inMenu: true,
+        titleKey: "alsijil.extra_marks.menu_title",
+        icon: "mdi-label-variant-outline",
+        iconActive: "mdi-label-variant",
+        permission: "alsijil.view_extramarks_rule",
+      },
+    },
     {
       path: "stats/",
       component: () =>
diff --git a/aleksis/apps/alsijil/frontend/messages/de.json b/aleksis/apps/alsijil/frontend/messages/de.json
index 034f1482cb55c852f8cfbc0456cdc992a33e0b67..8966f6e068e342b876f790aae1f2dc2e00f7c2fc 100644
--- a/aleksis/apps/alsijil/frontend/messages/de.json
+++ b/aleksis/apps/alsijil/frontend/messages/de.json
@@ -1,4 +1,7 @@
 {
+  "actions": {
+    "back_to_overview": "Zurück zur Übersicht"
+  },
   "alsijil": {
     "absence": {
       "menu_title": "Abwesenheit eintragen"
@@ -7,21 +10,55 @@
       "menu_title": "Alle Stunden"
     },
     "coursebook": {
+      "absences": {
+        "action_for_selected": "Ausgewählten Teilnehmer markieren als: | {count} ausgewählte Teilnehmer markieren als",
+        "button": "Abwesenheiten erfassen",
+        "lessons": "Keine Stunden | 1 Stunde | {count} Stunden",
+        "success": "Die Abwesenheiten wurden erfolgreich erfasst.",
+        "summary": "Zusammenfassung",
+        "title": "Abwesenheiten erfassen",
+        "warning": "Die folgenden Stunden liegen im ausgewählten Zeitraum. Bitte überprüfen Sie vor dem Bestätigen, ob Sie die Abwesenheiten für diese Stunden erfassen möchten."
+      },
       "filter": {
+        "absences_exist": "Nur Stunden mit abwesenden Teilnehmenden anzeigen",
         "courses": "Kurse",
         "filter_for_obj": "Nach Gruppe und Kurs filtern",
         "groups": "Gruppen",
         "missing": "Nur unvollständige Stunden anzeigen",
-        "own": "Nur eigene Stunden anzeigen"
+        "own": "Nur eigene Stunden anzeigen",
+        "page_type": {
+          "absences": "Abwesenheiten anzeigen",
+          "documentations": "Dokumentationen anzeigen"
+        }
+      },
+      "information": {
+        "subject": {
+          "field": "Fach bearbeiten"
+        }
+      },
+      "mark_as_absent_day": {
+        "action_button": "Abwesenheit ausweiten",
+        "description": "Wollen Sie die Person(en) für den Rest des Tages als {reason} markieren?",
+        "title": "Fehler: keine Person | {name} erfolgreich als {reason} markiert | {n} Personen erfolgreich als {reason} markiert"
       },
       "menu_title": "Kursbuch",
       "no_data": "Keine Stunden der ausgewählten Gruppen und Kurse im aktuellen Zeitraum",
       "no_results": "Keine Suchergebnisse für {search}",
+      "notes": {
+        "future": "Stunde ist in der Zukunft",
+        "show_list": "Liste der Teilnehmer*innen"
+      },
       "notices": {
         "future": "Diese Stunde darf nicht bearbeitet werden, da sie in der Zukunft liegt.",
         "no_entry": "Für diese Stunde gibt es noch keinen Eintrag."
       },
       "page_title": "Kursbuch für {name}",
+      "participation_status": "Teilnahmestatus",
+      "participations": {
+        "present": "Anwesend",
+        "present_number": "{present}/{total} anwesend"
+      },
+      "present_number": "{present}/{total} anwesend",
       "status": {
         "available": "Kursbucheintrag vorhanden",
         "cancelled": "Stunde fällt aus",
@@ -38,6 +75,7 @@
         },
         "homework": {
           "empty": "Keine Hausaufgaben",
+          "empty_yet": "Noch keine Hausaufgaben festgelegt.",
           "label": "Hausaufgaben",
           "value": "HA: {homework}"
         },
@@ -49,8 +87,9 @@
           }
         }
       },
+      "title_absences": "Kursbuch · Abwesenheiten",
+      "title_documentations": "Kursbuch",
       "title_plural": "Kursbuch",
-      "present_number": "{present}/{total} anwesend",
       "statistics": {
         "person_compact": {
           "title": "Kursbuch · Statistiken"
@@ -64,20 +103,16 @@
       "menu_title": "Entschuldigungsarten"
     },
     "extra_marks": {
-      "menu_title": "Zusätzliche Markierungen",
+      "colour_bg": "Hintergrundfarbe",
+      "colour_fg": "Schriftfarbe",
       "create": "Markierung erstellen",
+      "menu_title": "Zusätzliche Markierungen",
       "name": "Markierung",
       "short_name": "Abkürzung",
-      "colour_fg": "Schriftfarbe",
-      "colour_bg": "Hintergrundfarbe",
       "show_in_coursebook": "In Kursbuch-Übersicht zeigen",
-      "show_in_coursebook_helptext": "Wenn aktiviert tauchen diese Markierungen in den Zeilen im Kursbuch auf."
-    },
-    "personal_notes": {
-      "note": "Notiz",
-      "create_personal_note": "Weitere Notiz",
-      "confirm_delete": "Notiz wirklich löschen?",
-      "confirm_delete_explanation": "Die Notiz \"{note}\" für {name} wird entfernt."
+      "show_in_coursebook_helptext": "Wenn aktiviert tauchen diese Markierungen in den Zeilen im Kursbuch auf.",
+      "title": "Zusätzliche Markierung",
+      "title_plural": "Zusätzliche Markierungen"
     },
     "group_roles": {
       "menu_title_assign": "Gruppenrollen zuweisen",
@@ -93,6 +128,21 @@
     "my_overview": {
       "menu_title": "Meine Übersicht"
     },
+    "personal_notes": {
+      "card": {
+        "title": "Persönliche Notiz"
+      },
+      "confirm_delete": "Anmerkung wirklich löschen?",
+      "confirm_delete_explanation": "Die Notiz \"{note}\" für {name} wird entfernt.",
+      "confirm_delete_tardiness": "Die Verspätung von {name} in Höhe von {tardiness} Minuten wird entfernt.",
+      "create_personal_note": "Weitere Notiz",
+      "late": "Verspätet",
+      "lesson_length_exceeded": "Die Verspätung überschreitet die Stundenlänge.",
+      "minutes_late": "pünktlich | eine Minute verspätet | {n} Minuten zu spät",
+      "minutes_late_current": "pünktlich (basierend auf der aktuellen Uhrzeit) | eine Minute zu spät (basierend auf der aktuellen Uhrzeit) | {n} Minuten zu spät (basierend auf der aktuellen Uhrzeit)",
+      "note": "Notiz",
+      "tardiness": "Verspätung"
+    },
     "persons": {
       "menu_title": "Meine Schüler*innen"
     },
@@ -103,7 +153,8 @@
       "plural": "Verspätungen"
     }
   },
-  "actions": {
-    "back_to_overview": "Zurück zur Übersicht"
+  "time": {
+    "minutes": "Minuten",
+    "minutes_n": "keine Minuten | eine Minute | {n} Minuten"
   }
 }
diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json
index c9e36553c1c34208c231cb008a2632967ed2283b..0f3a1bb0f7e29eaf4f44e96471187baeeb25e124 100644
--- a/aleksis/apps/alsijil/frontend/messages/en.json
+++ b/aleksis/apps/alsijil/frontend/messages/en.json
@@ -19,7 +19,16 @@
       "menu_title": "My overview"
     },
     "extra_marks": {
-      "menu_title": "Extra marks"
+      "menu_title": "Extra marks",
+      "title": "Extra mark",
+      "title_plural": "Extra marks",
+      "create": "Create Extra mark",
+      "name": "Mark",
+      "short_name": "Abbreviation",
+      "colour_fg": "Foreground Colour",
+      "colour_bg": "Background Colour",
+      "show_in_coursebook": "Show in Coursebook Overview",
+      "show_in_coursebook_helptext": "When checked, this extra mark will be displayed in the lesson summary in the coursebook"
     },
     "excuse_types": {
       "menu_title": "Excuse types"
@@ -39,6 +48,8 @@
       "menu_title": "Coursebook",
       "page_title": "Coursebook for {name}",
       "title_plural": "Coursebook",
+      "title_documentations": "Coursebook",
+      "title_absences": "Coursebook · Absences",
       "status": {
         "available": "Documentation available",
         "missing": "Documentation missing",
@@ -47,6 +58,7 @@
         "cancelled": "Lesson cancelled",
         "pending": "Lesson pending"
       },
+      "participation_status": "Participation Status",
       "summary": {
         "topic": {
           "label": "Topic",
@@ -58,7 +70,8 @@
         "homework": {
           "label": "Homework",
           "value": "HW: {homework}",
-          "empty": "No homework"
+          "empty": "No homework",
+          "empty_yet": "No homework defined yet."
         },
         "group_note": {
           "label": "Group note",
@@ -66,6 +79,10 @@
           "empty": "No group note"
         }
       },
+      "notes": {
+        "show_list": "List of participants",
+        "future": "Lesson is in the future"
+      },
       "notices": {
         "future": "Editing this lesson isn't allowed as this lesson is in the future.",
         "no_entry": "There is no entry for this lesson yet."
@@ -75,11 +92,54 @@
         "missing": "Only show incomplete lessons",
         "groups": "Groups",
         "courses": "Courses",
-        "filter_for_obj": "Filter for group and course"
+        "filter_for_obj": "Filter for group and course",
+        "page_type": {
+          "documentations": "Show documentations",
+          "absences": "Show absences"
+        },
+        "absences_exist": "Only show lessons with absent participants"
+      },
+      "information": {
+        "subject": {
+          "field": "Edit subject"
+        }
       },
-      "present_number": "{present}/{total} present",
       "no_data": "No lessons for the selected groups and courses in this period",
       "no_results": "No search results for {search}",
+      "participations": {
+        "present_number": "{present}/{total} present",
+        "present": "Present"
+      },
+      "absences": {
+        "action_for_selected": "Mark selected participant as: | Mark {count} selected participants as",
+        "title": "Register absences",
+        "button": "Register absences",
+        "summary": "Summary",
+        "lessons": "No lessons | 1 lesson | {count} lessons",
+        "success": "The absences were registered successfully.",
+        "warning": "The following lessons are in the selected time period. Please check that you want to register the absences for these lessons before confirming."
+      },
+      "mark_as_absent_day": {
+        "title": "Error: no person | Successfully marked {name} as {reason} | Successfully marked {n} people as {reason}",
+        "description": "Do you want to mark them as {reason} for the rest of their day?",
+        "action_button": "Extend absence"
+      }
+    },
+    "personal_notes": {
+      "card": {
+        "title": "Personal note"
+      },
+      "note": "Note",
+      "create_personal_note": "Add another note",
+      "tardiness": "Tardiness",
+      "late": "Late",
+      "minutes_late": "on time | one minute late | {n} minutes late",
+      "minutes_late_current": "on time (based on current time) | one minute late (based on current time) | {n} minutes late (based on current time)",
+      "lesson_length_exceeded": "The tardiness exceeds the length of the lesson",
+      "confirm_delete": "Delete note?",
+      "confirm_delete_explanation": "The note \"{note}\" for {name} will be removed.",
+      "confirm_delete_tardiness": "The tardiness of {tardiness} minutes will be removed for {name}.",
+      "no_results": "No search results for {search}",
       "statistics": {
         "person_compact": {
           "title": "Coursebook · Statistics"
@@ -93,6 +153,10 @@
   "actions": {
     "back_to_overview": "Back to overview"
   },
+  "time": {
+    "minutes": "minutes",
+    "minutes_n": "no minutes | one minute | {n} minutes"
+  },
   "group": {
     "tabs": {
       "statistics": "Coursebook Statistics",
diff --git a/aleksis/apps/alsijil/frontend/messages/ru.json b/aleksis/apps/alsijil/frontend/messages/ru.json
index 80cd02ad8e25ac807b4c294140f076529dafd77e..3f20ae686429082b97521d0f36ba0bdca9e6367a 100644
--- a/aleksis/apps/alsijil/frontend/messages/ru.json
+++ b/aleksis/apps/alsijil/frontend/messages/ru.json
@@ -1,4 +1,7 @@
 {
+  "actions": {
+    "back_to_overview": "Назад к обзору"
+  },
   "alsijil": {
     "absence": {
       "menu_title": "Регистрация отсутствия"
diff --git a/aleksis/apps/alsijil/frontend/messages/uk.json b/aleksis/apps/alsijil/frontend/messages/uk.json
index 1c516bb201cf66daecc93129aa34de51f2860638..f567697a8af4deb02905af7eeaf0dd61e10c62f9 100644
--- a/aleksis/apps/alsijil/frontend/messages/uk.json
+++ b/aleksis/apps/alsijil/frontend/messages/uk.json
@@ -1,4 +1,7 @@
 {
+  "actions": {
+    "back_to_overview": "Назад до огляду"
+  },
   "alsijil": {
     "absence": {
       "menu_title": "Реєстрація відсутності"
@@ -7,8 +10,17 @@
       "menu_title": "Усі уроки"
     },
     "coursebook": {
+      "absences": {
+        "action_for_selected": "Відмітити обраного відвідувача як: | Відмітити {count} відвідувачів як",
+        "button": "Зареєструвати відсутності",
+        "title": "Зареєструвати відсутності"
+      },
       "filter": {
+        "courses": "Курси",
         "groups": "Групи"
+      },
+      "participations": {
+        "present": "Присутній"
       }
     },
     "excuse_types": {
@@ -31,6 +43,9 @@
     "my_overview": {
       "menu_title": "Мій огляд"
     },
+    "personal_notes": {
+      "tardiness": "Запізнення"
+    },
     "persons": {
       "menu_title": "Мої студенти"
     },
diff --git a/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po
index 461e0f3cec97715fef05f37eae2d09b466b6e984..dcf1d42ca73b6368729531ed4b7798b1ace1f3e4 100644
--- a/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/ar/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -31,8 +31,8 @@ msgstr ""
 msgid "Mark as {excuse_type.name}"
 msgstr ""
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr ""
@@ -111,7 +111,7 @@ msgid "Before"
 msgstr ""
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
@@ -152,20 +152,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -178,8 +178,8 @@ msgstr ""
 msgid "End period"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -188,7 +188,7 @@ msgstr ""
 msgid "Absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -198,8 +198,8 @@ msgstr ""
 msgid "Excused"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
 msgid "Excuse type"
@@ -220,7 +220,7 @@ msgstr ""
 msgid "Has lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -228,7 +228,7 @@ msgstr ""
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 msgid "Event"
 msgstr ""
 
@@ -272,41 +272,39 @@ msgstr ""
 msgid "Can register an absence for a person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
 msgid "Short name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
 msgid "Count as absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:65
+#: aleksis/apps/alsijil/models.py:71
 msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
 msgid "Excuse types"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
 msgid "Year"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
@@ -314,18 +312,18 @@ msgstr ""
 msgid "Extra marks"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300
 msgid "Personal note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
 msgid "Personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
@@ -334,7 +332,7 @@ msgstr ""
 msgid "Lesson topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
@@ -343,7 +341,7 @@ msgstr ""
 msgid "Homework"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
@@ -351,293 +349,316 @@ msgstr ""
 msgid "Group note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
 msgid "Lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
 msgid "Lesson documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
 msgid "Teachers"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:482
+#: aleksis/apps/alsijil/models.py:490
 msgid "Lesson Topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:484
+#: aleksis/apps/alsijil/models.py:492
 msgid "Group Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
+#: aleksis/apps/alsijil/models.py:496
+msgid "Participation touched at"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886
 msgid "Documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:513
+#: aleksis/apps/alsijil/models.py:526
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:612
+#: aleksis/apps/alsijil/models.py:791
 msgid "Groups of Person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:625
+#: aleksis/apps/alsijil/models.py:804
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:634
+#: aleksis/apps/alsijil/models.py:816
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+msgid "Tardiness"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:670
+#: aleksis/apps/alsijil/models.py:891
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:672
+#: aleksis/apps/alsijil/models.py:893
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:679
+#: aleksis/apps/alsijil/models.py:900
 msgid "Personal Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:680
+#: aleksis/apps/alsijil/models.py:901
 msgid "Personal Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
 msgid "Class register"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:17
+#: aleksis/apps/alsijil/preferences.py:24
 msgid "Block adding personal notes for cancelled lessons"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:25
+#: aleksis/apps/alsijil/preferences.py:32
 msgid "Allow users to view their own personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:34
+#: aleksis/apps/alsijil/preferences.py:41
 msgid "Allow primary group owners to register future absences for students in their groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:44
+#: aleksis/apps/alsijil/preferences.py:51
 msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:54
+#: aleksis/apps/alsijil/preferences.py:61
 msgid "Allow original teachers to edit their lessons although they are substituted"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:63
+#: aleksis/apps/alsijil/preferences.py:70
 msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:66
+#: aleksis/apps/alsijil/preferences.py:73
 msgid "This will carry over data only if the data in the following periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:75
+#: aleksis/apps/alsijil/preferences.py:82
 msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:79
+#: aleksis/apps/alsijil/preferences.py:86
 msgid "This will carry over data only if the data in the aforementioned periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:88
+#: aleksis/apps/alsijil/preferences.py:95
 msgid "Carry over personal notes to all following lesson periods on the same day."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:97
+#: aleksis/apps/alsijil/preferences.py:104
 msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:101
+#: aleksis/apps/alsijil/preferences.py:108
 msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:110
+#: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:119
+#: aleksis/apps/alsijil/preferences.py:126
 msgid "Allow group owners to assign group roles to the parents of the group's members"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:128
+#: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:129
+#: aleksis/apps/alsijil/preferences.py:136
 msgid "Only week view of groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:137
+#: aleksis/apps/alsijil/preferences.py:144
 msgid "Show assigned group roles in lesson view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:147
+#: aleksis/apps/alsijil/preferences.py:154
 msgid "Items per page in lessons table"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:151
+#: aleksis/apps/alsijil/preferences.py:158
 msgid "Each page must show at least one item."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:159
+#: aleksis/apps/alsijil/preferences.py:166
 msgid "Filter lessons by existence of their lesson documentation on default"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:170
+#: aleksis/apps/alsijil/preferences.py:177
 msgid "Allow editing of all future documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:173
+#: aleksis/apps/alsijil/preferences.py:180
 msgid "Allow editing of all documentations up to and including those on the current day"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:178
+#: aleksis/apps/alsijil/preferences.py:185
 msgid "Allow editing of all documentations up to and including those on the current date and time"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:183
+#: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr ""
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr ""
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 msgid "Excuse"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:156
+#: aleksis/apps/alsijil/tables.py:136
 #, python-brace-format
 msgid "{value}' tardiness"
 msgstr ""
@@ -964,17 +985,6 @@ msgid ""
 "  "
 msgstr ""
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr ""
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr ""
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1468,98 +1478,86 @@ msgstr ""
 msgid "Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:114
+#: aleksis/apps/alsijil/views.py:112
 msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:147
+#: aleksis/apps/alsijil/views.py:145
 msgid "You are not allowed to create a lesson documentation for a lesson in the future."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:658
+#: aleksis/apps/alsijil/views.py:656
 msgid "Generate full register printout for {}"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:659
+#: aleksis/apps/alsijil/views.py:657
 msgid "Generate full register printout …"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:660
+#: aleksis/apps/alsijil/views.py:658
 msgid "The printout has been generated successfully."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 msgid "The personal note has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1074
-msgid "The extra mark has been created."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1086
-msgid "The extra mark has been saved."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 msgid "The group role has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 msgid "The group role has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 msgid "The group role has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 msgid "The group role has been assigned."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 msgid "The group role assignment has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 msgid "The group role assignment has been stopped."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 msgid "The group role assignment has been deleted."
 msgstr ""
diff --git a/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po
index 8f5aa6cf99694bfd5f1220819de95475f837bfca..32ea0b2793090ad547ce81871145a6d11216904e 100644
--- a/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/de_DE/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
-"PO-Revision-Date: 2024-04-23 19:39+0000\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"PO-Revision-Date: 2024-08-19 09:46+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/de/>\n"
 "Language: de_DE\n"
@@ -31,8 +31,8 @@ msgstr "Als unentschuldigt markieren"
 msgid "Mark as {excuse_type.name}"
 msgstr "Als {excuse_type.name} markieren"
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr "Löschen"
@@ -43,8 +43,12 @@ msgstr "{} bittet Sie, einige Klassenbucheinträge zu überprüfen."
 
 #: aleksis/apps/alsijil/actions.py:86
 #, python-brace-format
-msgid "We have successfully sent notifications to {count_teachers} persons for {count_items} lessons."
-msgstr "Wir haben erfolgreich Benachrichtigungen an {count_teachers} Personen für {count_items} Stunden gesendet."
+msgid ""
+"We have successfully sent notifications to {count_teachers} persons for "
+"{count_items} lessons."
+msgstr ""
+"Wir haben erfolgreich Benachrichtigungen an {count_teachers} Personen für "
+"{count_items} Stunden gesendet."
 
 #: aleksis/apps/alsijil/actions.py:92
 msgid "Ask teacher to check data"
@@ -64,7 +68,9 @@ msgstr "Persönliche Notiz zurücksetzen"
 
 #: aleksis/apps/alsijil/data_checks.py:49
 msgid "Ensure that there are no personal notes in cancelled lessons"
-msgstr "Sicherstellen, dass es keine persönlichen Notizen in ausgefallenen Stunden gibt"
+msgstr ""
+"Sicherstellen, dass es keine persönlichen Notizen in ausgefallenen Stunden "
+"gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:50
 msgid "The personal note is related to a cancelled lesson."
@@ -72,7 +78,9 @@ msgstr "Die persönliche Notiz ist einer ausgefallenen Stunde zugeordnet."
 
 #: aleksis/apps/alsijil/data_checks.py:77
 msgid "Ensure that 'groups_of_person' is set for every personal note"
-msgstr "Sicherstellen, dass \"groups_of_person\" für alle persönlichen Notizen gesetzt ist"
+msgstr ""
+"Sicherstellen, dass \"groups_of_person\" für alle persönlichen Notizen "
+"gesetzt ist"
 
 #: aleksis/apps/alsijil/data_checks.py:78
 msgid "The personal note has no group in 'groups_of_person'."
@@ -80,7 +88,9 @@ msgstr "Die persönliche Notiz hat keine Gruppe in \"groups_of_person\"."
 
 #: aleksis/apps/alsijil/data_checks.py:103
 msgid "Ensure that there are no filled out lesson documentations on holidays"
-msgstr "Sicherstellen, dass es keine ausgefüllten Stundendokumentationen in den Ferien gibt"
+msgstr ""
+"Sicherstellen, dass es keine ausgefüllten Stundendokumentationen in den "
+"Ferien gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:104
 msgid "The lesson documentation is on holidays."
@@ -88,7 +98,9 @@ msgstr "Die Stundendokumentation ist in den Ferien."
 
 #: aleksis/apps/alsijil/data_checks.py:137
 msgid "Ensure that there are no filled out personal notes on holidays"
-msgstr "Sicherstellen, dass es keine ausgefüllten persönlichen Notizen in den Ferien gibt"
+msgstr ""
+"Sicherstellen, dass es keine ausgefüllten persönlichen Notizen in den Ferien "
+"gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:138
 msgid "The personal note is on holidays."
@@ -96,11 +108,14 @@ msgstr "Die persönliche Notiz ist in den Ferien."
 
 #: aleksis/apps/alsijil/data_checks.py:166
 msgid "Ensure that there are no excused personal notes without an absence"
-msgstr "Sicherstellen, dass es keine entschuldigten persönlichen Notizen ohne eine Absenz gibt"
+msgstr ""
+"Sicherstellen, dass es keine entschuldigten persönlichen Notizen ohne eine "
+"Absenz gibt"
 
 #: aleksis/apps/alsijil/data_checks.py:167
 msgid "The personal note is marked as excused, but not as absent."
-msgstr "Die persönliche Notiz ist als entschuldigt, aber nicht als abwesend markiert."
+msgstr ""
+"Die persönliche Notiz ist als entschuldigt, aber nicht als abwesend markiert."
 
 #: aleksis/apps/alsijil/filters.py:12
 msgid "After"
@@ -111,11 +126,12 @@ msgid "Before"
 msgstr "Bevor"
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:220
+#: aleksis/apps/alsijil/models.py:478
 msgid "Subject"
 msgstr "Fach"
 
@@ -133,7 +149,9 @@ msgstr "Hausaufgabe zur nächsten Stunde"
 
 #: aleksis/apps/alsijil/forms.py:53
 msgid "Carry over data to all other lessons with the same subject in this week"
-msgstr "Daten zu allen weiteren Stunden mit dem gleichen Fach in dieser Woche übernehmen"
+msgstr ""
+"Daten zu allen weiteren Stunden mit dem gleichen Fach in dieser Woche "
+"übernehmen"
 
 #: aleksis/apps/alsijil/forms.py:98 aleksis/apps/alsijil/forms.py:278
 #: aleksis/apps/alsijil/forms.py:370
@@ -149,24 +167,28 @@ msgstr "Lehrkraft"
 
 #: aleksis/apps/alsijil/forms.py:121
 msgid "You can't select a group and a teacher both."
-msgstr "Es kann nur entweder eine Gruppe oder eine Lehrkraft ausgewählt werden."
+msgstr ""
+"Es kann nur entweder eine Gruppe oder eine Lehrkraft ausgewählt werden."
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
+#: aleksis/apps/alsijil/models.py:763 aleksis/apps/alsijil/models.py:822
 msgid "Person"
 msgstr "Person"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
+#: aleksis/apps/alsijil/models.py:903
 msgid "Start date"
 msgstr "Startdatum"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
+#: aleksis/apps/alsijil/models.py:907
 msgid "End date"
 msgstr "Enddatum"
 
@@ -178,8 +200,8 @@ msgstr "Startstunde"
 msgid "End period"
 msgstr "Endstunde"
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -188,7 +210,7 @@ msgstr "Endstunde"
 msgid "Absent"
 msgstr "Abwesend"
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -198,10 +220,11 @@ msgstr "Abwesend"
 msgid "Excused"
 msgstr "Entschuldigt"
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
+#: aleksis/apps/alsijil/models.py:84 aleksis/apps/alsijil/models.py:260
 msgid "Excuse type"
 msgstr "Entschuldigungsart"
 
@@ -220,7 +243,7 @@ msgstr "Schuljahr"
 msgid "Has lesson documentation"
 msgstr "Hat eine Stunden-Dokumentation"
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -228,6 +251,7 @@ msgstr "Hat eine Stunden-Dokumentation"
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 #: aleksis/apps/alsijil/util/alsijil_helpers.py:330
 msgid "Event"
 msgstr "Veranstaltung"
@@ -272,372 +296,499 @@ msgstr "Kann eine Gruppenrolle für diese Gruppe zuweisen"
 msgid "Can register an absence for a person"
 msgstr "Kann eine Absenz für eine Person registrieren"
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/models.py:437
 msgid "Short name"
 msgstr "Kurzname"
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr "Name"
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
+#: aleksis/apps/alsijil/models.py:68
 msgid "Count as absent"
 msgstr "Als abwesend zählen"
 
-#: aleksis/apps/alsijil/models.py:65
-msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
-msgstr "Wenn ausgewählt wird diese Entschuldigungsart als eine verpasste Stunde gezählt. Wenn nicht ausgewählt wird es nicht im Abwesenheitsbericht auftauchen."
+#: aleksis/apps/alsijil/models.py:71 aleksis/apps/alsijil/models.py:70
+msgid ""
+"If checked, this excuse type will be counted as a missed lesson. If not "
+"checked,it won't show up in the absence report."
+msgstr ""
+"Wenn ausgewählt wird diese Entschuldigungsart als eine verpasste Stunde "
+"gezählt. Wenn nicht ausgewählt wird es nicht im Abwesenheitsbericht "
+"auftauchen."
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
+#: aleksis/apps/alsijil/models.py:85
 msgid "Excuse types"
 msgstr "Entschuldigungsarten"
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
+#: aleksis/apps/alsijil/models.py:240 aleksis/apps/alsijil/models.py:339
 msgid "Year"
 msgstr "Jahr"
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:331
+#: aleksis/apps/alsijil/models.py:265 aleksis/apps/alsijil/models.py:455
 msgid "Extra marks"
 msgstr "Zusätzliche Markierungen"
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300 aleksis/apps/alsijil/models.py:299
 msgid "Personal note"
 msgstr "Persönliche Notiz"
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
+#: aleksis/apps/alsijil/models.py:300
 msgid "Personal notes"
 msgstr "Persönliche Notizen"
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:25
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:421
+#: aleksis/apps/alsijil/models.py:351
 msgid "Lesson topic"
 msgstr "Stundenthema"
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:33
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:422
+#: aleksis/apps/alsijil/models.py:352 aleksis/apps/alsijil/models.py:490
 msgid "Homework"
 msgstr "Hausaufgaben"
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:41
+#: aleksis/apps/alsijil/models.py:353
 msgid "Group note"
 msgstr "Gruppennotiz"
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
+#: aleksis/apps/alsijil/models.py:403
 msgid "Lesson documentation"
 msgstr "Stunden-Dokumentation"
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
+#: aleksis/apps/alsijil/models.py:404
 msgid "Lesson documentations"
 msgstr "Stunden-Dokumentationen"
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441 aleksis/apps/alsijil/models.py:440
 msgid "Foreground colour"
 msgstr "Vordergrundfarbe"
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442 aleksis/apps/alsijil/models.py:441
 msgid "Background colour"
 msgstr "Hintergrundfarbe"
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444 aleksis/apps/alsijil/models.py:443
 msgid "Show in coursebook"
 msgstr "In Kursbuch anzeigen"
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455 aleksis/apps/alsijil/models.py:454
 msgid "Extra mark"
 msgstr "Zusätzliche Markierung"
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475 aleksis/apps/alsijil/models.py:474
 msgid "Course"
 msgstr "Kurs"
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
+#: aleksis/apps/alsijil/models.py:486
 msgid "Teachers"
 msgstr "Lehrkräfte"
 
-#: aleksis/apps/alsijil/models.py:482
+#: aleksis/apps/alsijil/models.py:490 aleksis/apps/alsijil/models.py:489
 msgid "Lesson Topic"
 msgstr "Stundenthema"
 
-#: aleksis/apps/alsijil/models.py:484
+#: aleksis/apps/alsijil/models.py:492 aleksis/apps/alsijil/models.py:491
 msgid "Group Note"
 msgstr "Gruppennotiz"
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
+#: aleksis/apps/alsijil/models.py:496 aleksis/apps/alsijil/models.py:495
+msgid "Participation touched at"
+msgstr "Teilnahmestatus angelegt am"
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886 aleksis/apps/alsijil/models.py:524
+#: aleksis/apps/alsijil/models.py:773 aleksis/apps/alsijil/models.py:829
 msgid "Documentation"
 msgstr "Dokumentation"
 
-#: aleksis/apps/alsijil/models.py:513
+#: aleksis/apps/alsijil/models.py:526 aleksis/apps/alsijil/models.py:525
 msgid "Documentations"
 msgstr "Dokumentationen"
 
-#: aleksis/apps/alsijil/models.py:612
+#: aleksis/apps/alsijil/models.py:791 aleksis/apps/alsijil/models.py:766
 msgid "Groups of Person"
 msgstr "Gruppen der Person"
 
-#: aleksis/apps/alsijil/models.py:625
+#: aleksis/apps/alsijil/models.py:804 aleksis/apps/alsijil/models.py:779
 msgid "Absence Reason"
 msgstr "Abwesenheitsgrund"
 
-#: aleksis/apps/alsijil/models.py:634
+#: aleksis/apps/alsijil/models.py:816 aleksis/apps/alsijil/models.py:791
 msgid "Base Absence"
 msgstr "Basis-Abwesenheit"
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+#: aleksis/apps/alsijil/models.py:794
+msgid "Tardiness"
+msgstr "Verspätung"
+
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
+#: aleksis/apps/alsijil/models.py:805 aleksis/apps/alsijil/models.py:806
 msgid "Participation Status"
 msgstr "Teilnahmestatus"
 
-#: aleksis/apps/alsijil/models.py:670
+#: aleksis/apps/alsijil/models.py:891 aleksis/apps/alsijil/models.py:834
 msgid "Note"
 msgstr "Notiz"
 
-#: aleksis/apps/alsijil/models.py:672
+#: aleksis/apps/alsijil/models.py:893 aleksis/apps/alsijil/models.py:836
 msgid "Extra Mark"
 msgstr "Zusätzliche Markierung"
 
-#: aleksis/apps/alsijil/models.py:679
+#: aleksis/apps/alsijil/models.py:900 aleksis/apps/alsijil/models.py:843
 msgid "Personal Note"
 msgstr "Persönliche Notiz"
 
-#: aleksis/apps/alsijil/models.py:680
+#: aleksis/apps/alsijil/models.py:901 aleksis/apps/alsijil/models.py:844
 msgid "Personal Notes"
 msgstr "Persönliche Notizen"
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912 aleksis/apps/alsijil/models.py:855
+msgid ""
+"A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+"Eine Person hat die gleiche zusätzliche Markierung für eine Dokumentation "
+"mehrfach zugeordnet bekommen."
+
+#: aleksis/apps/alsijil/models.py:925 aleksis/apps/alsijil/models.py:868
 msgid "Icon"
 msgstr "Symbol"
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926 aleksis/apps/alsijil/models.py:869
 msgid "Colour"
 msgstr "Farbe"
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
+#: aleksis/apps/alsijil/models.py:875 aleksis/apps/alsijil/models.py:890
 msgid "Group role"
 msgstr "Gruppenrolle"
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
+#: aleksis/apps/alsijil/models.py:876
 msgid "Group roles"
 msgstr "Gruppenrollen"
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934 aleksis/apps/alsijil/models.py:877
 msgid "Can assign group role"
 msgstr "Kann Gruppenrolle zuweisen"
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953 aleksis/apps/alsijil/models.py:896
 msgid "Assigned person"
 msgstr "Zugewiesene Person"
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
+#: aleksis/apps/alsijil/models.py:901
 msgid "Groups"
 msgstr "Gruppen"
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965 aleksis/apps/alsijil/models.py:908
 msgid "Can be left empty if end date is not clear yet"
 msgstr "Kann frei gelassen werden, wenn das Enddatum noch nicht feststeht"
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980 aleksis/apps/alsijil/models.py:923
 msgid "Group role assignment"
 msgstr "Zuweisung von Gruppenrollen"
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981 aleksis/apps/alsijil/models.py:924
 msgid "Group role assignments"
 msgstr "Zuweisungen von Gruppenrollen"
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988 aleksis/apps/alsijil/models.py:931
 msgid "Can view lesson overview"
 msgstr "Kann die Stundenübersicht sehen"
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989 aleksis/apps/alsijil/models.py:932
 msgid "Can view week overview"
 msgstr "Kann die Wochenübersicht sehen"
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990 aleksis/apps/alsijil/models.py:933
 msgid "Can view full register"
 msgstr "Kann komplettes Klassenbuch sehen"
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991 aleksis/apps/alsijil/models.py:934
 msgid "Can register absence"
 msgstr "Kann eine Absenz registrieren"
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992 aleksis/apps/alsijil/models.py:935
 msgid "Can list all personal note filters"
 msgstr "Kann alle Filter für persönliche Notizen anzeigen"
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
+#: aleksis/apps/alsijil/preferences.py:9
 msgid "Class register"
 msgstr "Klassenbuch"
 
+#: aleksis/apps/alsijil/preferences.py:24
 #: aleksis/apps/alsijil/preferences.py:17
 msgid "Block adding personal notes for cancelled lessons"
-msgstr "Blockiere das Hinzufügen von persönlichen Notizen für ausgefallene Stunden"
+msgstr ""
+"Blockiere das Hinzufügen von persönlichen Notizen für ausgefallene Stunden"
 
+#: aleksis/apps/alsijil/preferences.py:32
 #: aleksis/apps/alsijil/preferences.py:25
 msgid "Allow users to view their own personal notes"
 msgstr "Erlaube Benutzern, ihre eigenen persönlichen Notizen zu sehen"
 
+#: aleksis/apps/alsijil/preferences.py:41
 #: aleksis/apps/alsijil/preferences.py:34
-msgid "Allow primary group owners to register future absences for students in their groups"
-msgstr "Erlaube Primärgruppeninhabern Absenzen in der Zukunft für Mitglieder ihrer Gruppen zu registrieren"
+msgid ""
+"Allow primary group owners to register future absences for students in their "
+"groups"
+msgstr ""
+"Erlaube Primärgruppeninhabern Absenzen in der Zukunft für Mitglieder ihrer "
+"Gruppen zu registrieren"
 
+#: aleksis/apps/alsijil/preferences.py:51
 #: aleksis/apps/alsijil/preferences.py:44
-msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
-msgstr "Gebe dem Besitzer einer Elterngruppe die gleichen Rechte wie den Besitzern der entsprechenden Kindgruppen"
+msgid ""
+"Grant the owner of a parent group the same privileges as the owners of the "
+"respective child groups"
+msgstr ""
+"Gebe dem Besitzer einer Elterngruppe die gleichen Rechte wie den Besitzern "
+"der entsprechenden Kindgruppen"
 
+#: aleksis/apps/alsijil/preferences.py:61
 #: aleksis/apps/alsijil/preferences.py:54
-msgid "Allow original teachers to edit their lessons although they are substituted"
-msgstr "Erlaube den Ursprungslehrkräften, ihre Stunden zu bearbeiten, obwohl sie vertreten worden sind"
+msgid ""
+"Allow original teachers to edit their lessons although they are substituted"
+msgstr ""
+"Erlaube den Ursprungslehrkräften, ihre Stunden zu bearbeiten, obwohl sie "
+"vertreten worden sind"
 
+#: aleksis/apps/alsijil/preferences.py:70
 #: aleksis/apps/alsijil/preferences.py:63
-msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
+msgid ""
+"Carry over data from first lesson period to the following lesson periods in "
+"lessons over multiple periods"
 msgstr "Daten von der ersten Stunde zu weiteren folgenden Stunden übernehmen"
 
+#: aleksis/apps/alsijil/preferences.py:73
 #: aleksis/apps/alsijil/preferences.py:66
-msgid "This will carry over data only if the data in the following periods are empty."
-msgstr "Dies wird die Daten nur übernehmen, wenn die Daten in den Folgestunden leer sind."
+msgid ""
+"This will carry over data only if the data in the following periods are "
+"empty."
+msgstr ""
+"Dies wird die Daten nur übernehmen, wenn die Daten in den Folgestunden leer "
+"sind."
 
+#: aleksis/apps/alsijil/preferences.py:82
 #: aleksis/apps/alsijil/preferences.py:75
-msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
-msgstr "Erlaube das Übernehmen von Daten von einer Stunde zu allen weiteren Stunden mit dem gleichen Unterricht in der gleichen Woche"
+msgid ""
+"Allow carrying over data from any lesson period to all other "
+"lesson                 periods with the same lesson and in the same week"
+msgstr ""
+"Erlaube das Übernehmen von Daten von einer Stunde zu allen weiteren Stunden "
+"mit dem gleichen Unterricht in der gleichen Woche"
 
+#: aleksis/apps/alsijil/preferences.py:86
 #: aleksis/apps/alsijil/preferences.py:79
-msgid "This will carry over data only if the data in the aforementioned periods are empty."
-msgstr "Dies wird die Daten nur übernehmen, wenn die Daten in den eben genannten Stunden leer sind."
+msgid ""
+"This will carry over data only if the data in the aforementioned periods are "
+"empty."
+msgstr ""
+"Dies wird die Daten nur übernehmen, wenn die Daten in den eben genannten "
+"Stunden leer sind."
 
+#: aleksis/apps/alsijil/preferences.py:95
 #: aleksis/apps/alsijil/preferences.py:88
-msgid "Carry over personal notes to all following lesson periods on the same day."
-msgstr "Persönliche Notizen in alle folgenden Unterrichtsstunden am gleichen Tag übernehmen."
+msgid ""
+"Carry over personal notes to all following lesson periods on the same day."
+msgstr ""
+"Persönliche Notizen in alle folgenden Unterrichtsstunden am gleichen Tag "
+"übernehmen."
 
+#: aleksis/apps/alsijil/preferences.py:104
 #: aleksis/apps/alsijil/preferences.py:97
-msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
-msgstr "Erlaube Lehrkräften, Unterrichtsstunden bereits am gleichen Tag und nicht erst zu Beginn der Stunde zu öffnen"
+msgid ""
+"Allow teachers to open lesson periods on the same day and not just at the "
+"beginning of the period"
+msgstr ""
+"Erlaube Lehrkräften, Unterrichtsstunden bereits am gleichen Tag und nicht "
+"erst zu Beginn der Stunde zu öffnen"
 
+#: aleksis/apps/alsijil/preferences.py:108
 #: aleksis/apps/alsijil/preferences.py:101
-msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
-msgstr "Unterrichtsstunden in der Vergangenheit werden nicht durch diese Einstellung beeinflusst, sie können immer geöffnet werden."
+msgid ""
+"Lessons in the past are not affected by this setting, you can open them "
+"whenever you want."
+msgstr ""
+"Unterrichtsstunden in der Vergangenheit werden nicht durch diese Einstellung "
+"beeinflusst, sie können immer geöffnet werden."
 
+#: aleksis/apps/alsijil/preferences.py:117
 #: aleksis/apps/alsijil/preferences.py:110
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr "Lehrkräften erlauben, Daten für Stunden in den Ferien hinzuzufügen"
 
+#: aleksis/apps/alsijil/preferences.py:126
 #: aleksis/apps/alsijil/preferences.py:119
-msgid "Allow group owners to assign group roles to the parents of the group's members"
-msgstr "Erlaube Gruppenbesitzern, Gruppenrollen für Eltern von Gruppenmitgliedern zuzuweisen"
+msgid ""
+"Allow group owners to assign group roles to the parents of the group's "
+"members"
+msgstr ""
+"Erlaube Gruppenbesitzern, Gruppenrollen für Eltern von Gruppenmitgliedern "
+"zuzuweisen"
 
+#: aleksis/apps/alsijil/preferences.py:135
 #: aleksis/apps/alsijil/preferences.py:128
 msgid "Show assigned group roles in week view"
 msgstr "Zugewiesene Gruppenrollen in der Wochenansicht zeigen"
 
+#: aleksis/apps/alsijil/preferences.py:136
 #: aleksis/apps/alsijil/preferences.py:129
 msgid "Only week view of groups"
 msgstr "Nur Wochenansicht von Gruppen"
 
+#: aleksis/apps/alsijil/preferences.py:144
 #: aleksis/apps/alsijil/preferences.py:137
 msgid "Show assigned group roles in lesson view"
 msgstr "Zugewiesene Gruppenrollen in der Stundenansicht anzeigen"
 
+#: aleksis/apps/alsijil/preferences.py:154
 #: aleksis/apps/alsijil/preferences.py:147
 msgid "Items per page in lessons table"
 msgstr "Einträge pro Seite in der Stundentabelle"
 
+#: aleksis/apps/alsijil/preferences.py:158
 #: aleksis/apps/alsijil/preferences.py:151
 msgid "Each page must show at least one item."
 msgstr "Jede Seite muss mindestens einen Eintrag anzeigen."
 
+#: aleksis/apps/alsijil/preferences.py:166
 #: aleksis/apps/alsijil/preferences.py:159
 msgid "Filter lessons by existence of their lesson documentation on default"
-msgstr "Stunden standardmäßig anhand der Existenz ihrer Stundendokumentation filtern"
+msgstr ""
+"Stunden standardmäßig anhand der Existenz ihrer Stundendokumentation filtern"
 
+#: aleksis/apps/alsijil/preferences.py:177
 #: aleksis/apps/alsijil/preferences.py:170
 msgid "Allow editing of all future documentations"
 msgstr "Bearbeiten von allen zukünftigen Dokumentationen erlauben"
 
+#: aleksis/apps/alsijil/preferences.py:180
 #: aleksis/apps/alsijil/preferences.py:173
-msgid "Allow editing of all documentations up to and including those on the current day"
-msgstr "Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag erlauben"
+msgid ""
+"Allow editing of all documentations up to and including those on the current "
+"day"
+msgstr ""
+"Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag erlauben"
 
+#: aleksis/apps/alsijil/preferences.py:185
 #: aleksis/apps/alsijil/preferences.py:178
-msgid "Allow editing of all documentations up to and including those on the current date and time"
-msgstr "Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag und zur aktuellen Uhrzeit erlauben"
+msgid ""
+"Allow editing of all documentations up to and including those on the current "
+"date and time"
+msgstr ""
+"Bearbeiten von allen Dokumentationen bis inklusive zum aktuellen Tag und zur "
+"aktuellen Uhrzeit erlauben"
 
+#: aleksis/apps/alsijil/preferences.py:190
 #: aleksis/apps/alsijil/preferences.py:183
 msgid "Set time range for which documentations may be edited"
 msgstr "Zeitraum setzen, in dem Dokumentationen bearbeitet werden dürfen"
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid ""
+"User is allowed to register absences for members of groups the user is an "
+"owner of with these group types"
+msgstr "Benutzer*innen dürfen Abwesenheiten für Mitglieder von Gruppen mit diesen Gruppentypen, in denen sie Besitzer*innen sind, registrieren"
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid ""
+"If you leave it empty, all member of groups the user is an owner of will be "
+"shown."
+msgstr "Wenn Sie es leer lassen, werden alle Mitglieder von Gruppen angezeigt, in denen Benutzer*innen Besitzer*innen sind."
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid ""
+"Group type of groups to be shown first in the group select field on the "
+"coursebook overview page"
+msgstr "Gruppentyp von Gruppen, die zuerst im Gruppenauswahlfeld auf der Kursbuchübersichtsseite angezeigt werden"
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr "Wenn Sie es leer lassen, wird kein Gruppentyp benutzt."
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr "Datum"
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr "Stunde"
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr "Verspätung"
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 msgid "Excuse"
 msgstr "Entschuldigen"
 
-#: aleksis/apps/alsijil/tables.py:156
+#: aleksis/apps/alsijil/tables.py:136
 #, python-brace-format
 msgid "{value}' tardiness"
 msgstr "{value}' Verspätung"
@@ -717,11 +868,13 @@ msgstr " %(count)s betroffene Stunden "
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:40
 msgid ""
 "\n"
-"                  There are no affected lessons. Registering this absence won't have any effect.\n"
+"                  There are no affected lessons. Registering this absence "
+"won't have any effect.\n"
 "                "
 msgstr ""
 "\n"
-"                  Es gibt keine betroffenen Stunden. Das Eintragen dieser Abwesenheit wird keinen Effekt haben.\n"
+"                  Es gibt keine betroffenen Stunden. Das Eintragen dieser "
+"Abwesenheit wird keinen Effekt haben.\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:57
@@ -819,7 +972,8 @@ msgid ""
 "                "
 msgstr ""
 "\n"
-"                  Diese Stunde ist in den Ferien und kann somit nicht bearbeitet werden.\n"
+"                  Diese Stunde ist in den Ferien und kann somit nicht "
+"bearbeitet werden.\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:10
@@ -882,7 +1036,7 @@ msgstr "Statistiken zu Fehlzeiten, Verspätungen und Bemerkungen"
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:82
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:291
 msgid "Absences"
-msgstr "Fehlstunden"
+msgstr "Abwesenheiten"
 
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:133
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:139
@@ -958,11 +1112,13 @@ msgstr "Keine Stunden verfügbar"
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:434
 msgid ""
 "\n"
-"            There are no lessons for the selected group or teacher in this week.\n"
+"            There are no lessons for the selected group or teacher in this "
+"week.\n"
 "          "
 msgstr ""
 "\n"
-"            Es gibt keine Stunden für die ausgewählte Gruppe oder Lehrkraft in dieser Woche.\n"
+"            Es gibt keine Stunden für die ausgewählte Gruppe oder Lehrkraft "
+"in dieser Woche.\n"
 "          "
 
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/create.html:6
@@ -980,26 +1136,20 @@ msgstr "Entschuldigungsart bearbeiten"
 #: aleksis/apps/alsijil/templates/alsijil/group_role/warning.html:4
 msgid ""
 "\n"
-"    This function should only be used to define alternatives to the default excuse which also will be counted extra.\n"
-"    Don't use this to create a default excuse or if you don't divide between different types of excuse.\n"
+"    This function should only be used to define alternatives to the default "
+"excuse which also will be counted extra.\n"
+"    Don't use this to create a default excuse or if you don't divide between "
+"different types of excuse.\n"
 "  "
 msgstr ""
 "\n"
-"    Diese Funktion sollte nur benutzt werden, um Alternativen zur normalen Entschuldigung, welche von sich aus extra gezählt wird, zu definieren.\n"
-"Benutzen Sie diese Funktion nicht, um eine Entschuldigungsart für normale Entschuldigungen zu erstellen oder wenn Sie nicht zwischen verschiedenen Entschuldigungsarten unterscheiden möchten.\n"
+"    Diese Funktion sollte nur benutzt werden, um Alternativen zur normalen "
+"Entschuldigung, welche von sich aus extra gezählt wird, zu definieren.\n"
+"Benutzen Sie diese Funktion nicht, um eine Entschuldigungsart für normale "
+"Entschuldigungen zu erstellen oder wenn Sie nicht zwischen verschiedenen "
+"Entschuldigungsarten unterscheiden möchten.\n"
 "  "
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr "Zusätzliche Markierung erstellen"
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr "Zusätzliche Markierung bearbeiten"
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1065,12 +1215,14 @@ msgstr "Niemand zugewiesen."
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assigned_roles.html:41
 msgid ""
 "\n"
-"    You can get some additional actions for each group role assignment if you click on the name of the\n"
+"    You can get some additional actions for each group role assignment if "
+"you click on the name of the\n"
 "    corresponding person.\n"
 "  "
 msgstr ""
 "\n"
-"    Sie können zusätzliche Aktionen für jede Gruppenrollenzuweisung aufrufen, \n"
+"    Sie können zusätzliche Aktionen für jede Gruppenrollenzuweisung "
+"aufrufen, \n"
 "wenn Sie auf den Namen der entsprechenden Person klicken.\n"
 "  "
 
@@ -1079,8 +1231,12 @@ msgid "Stop"
 msgstr "Beenden"
 
 #: aleksis/apps/alsijil/templates/alsijil/notifications/check.html:1
-msgid "Please check if the following class register entries are complete and correct:"
-msgstr "Bitte prüfen Sie, ob die folgenden Klassenbucheinträge komplett und richtig sind:"
+msgid ""
+"Please check if the following class register entries are complete and "
+"correct:"
+msgstr ""
+"Bitte prüfen Sie, ob die folgenden Klassenbucheinträge komplett und richtig "
+"sind:"
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/absences.html:6
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:22
@@ -1240,13 +1396,17 @@ msgstr ""
 #, python-format
 msgid ""
 "\n"
-"            This seating plan is taken from the parent group of %(child_group)s.\n"
-"            If you want, you can take it over for your group and then customize it.\n"
+"            This seating plan is taken from the parent group of "
+"%(child_group)s.\n"
+"            If you want, you can take it over for your group and then "
+"customize it.\n"
 "          "
 msgstr ""
 "\n"
-"            Dieser Sitzplan wurde von der Elterngruppe von %(child_group)s übernommen.\n"
-"Wenn Sie wollen, können Sie ihn für Ihre Gruppe übernehmen und dann anpassen.\n"
+"            Dieser Sitzplan wurde von der Elterngruppe von %(child_group)s "
+"übernommen.\n"
+"Wenn Sie wollen, können Sie ihn für Ihre Gruppe übernehmen und dann "
+"anpassen.\n"
 "          "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:30
@@ -1265,11 +1425,13 @@ msgstr "Es gibt keinen Sitzplan für diese Stunde."
 #, python-format
 msgid ""
 "\n"
-"                  Create a new seating plan for %(group)s (%(subject)s) in %(room)s\n"
+"                  Create a new seating plan for %(group)s (%(subject)s) in "
+"%(room)s\n"
 "                "
 msgstr ""
 "\n"
-"                  Einen neuen Sitzplan für %(group)s (%(subject)s) in %(room)s erstellen\n"
+"                  Einen neuen Sitzplan für %(group)s (%(subject)s) in "
+"%(room)s erstellen\n"
 "                "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:78
@@ -1280,7 +1442,8 @@ msgid ""
 "                  "
 msgstr ""
 "\n"
-"                    Einen neuen Sitzplan für %(group)s in %(room)s erstellen\n"
+"                    Einen neuen Sitzplan für %(group)s in %(room)s "
+"erstellen\n"
 "                  "
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:6
@@ -1387,8 +1550,10 @@ msgid ""
 "      "
 msgstr ""
 "\n"
-"        Kopien des Klassenbuches, sowohl digital als auch als Ausdruck, dürfen\n"
-"            ausschließlich in der Schule und/oder auf von der Schule autorisierten Geräten\n"
+"        Kopien des Klassenbuches, sowohl digital als auch als Ausdruck, "
+"dürfen\n"
+"            ausschließlich in der Schule und/oder auf von der Schule "
+"autorisierten Geräten\n"
 "            gespeichert werden.\n"
 "      "
 
@@ -1400,7 +1565,8 @@ msgid ""
 "      "
 msgstr ""
 "\n"
-"        Die Leitung der Gruppe sowie die Schulleitung bestätigen die obigen Hinweise sowie\n"
+"        Die Leitung der Gruppe sowie die Schulleitung bestätigen die obigen "
+"Hinweise sowie\n"
 "            die Richtigkeit des Ausdrucks.\n"
 "      "
 
@@ -1532,104 +1698,113 @@ msgstr "KW"
 msgid "Notes"
 msgstr "Notizen"
 
-#: aleksis/apps/alsijil/views.py:114
-msgid "You either selected an invalid lesson or there is currently no lesson in progress."
+#: aleksis/apps/alsijil/views.py:112
+msgid ""
+"You either selected an invalid lesson or there is currently no lesson in "
+"progress."
 msgstr ""
 "Sie haben eine ungültige Stunde ausgewählt oder es\n"
 "      läuft momentan keine Stunde."
 
-#: aleksis/apps/alsijil/views.py:147
-msgid "You are not allowed to create a lesson documentation for a lesson in the future."
-msgstr "Ihnen ist es nicht erlaubt, eine Eintragung für eine Unterrichtsstunde in der Zukunft vorzunehmen."
+#: aleksis/apps/alsijil/views.py:145
+msgid ""
+"You are not allowed to create a lesson documentation for a lesson in the "
+"future."
+msgstr ""
+"Ihnen ist es nicht erlaubt, eine Eintragung für eine Unterrichtsstunde in "
+"der Zukunft vorzunehmen."
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
 msgstr "Die Stunden-Dokumentation wurde gespeichert."
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr "Die persönlichen Notizen wurden gespeichert."
 
-#: aleksis/apps/alsijil/views.py:658
+#: aleksis/apps/alsijil/views.py:656
 msgid "Generate full register printout for {}"
 msgstr "Vollständigen Klassenbuchausdruck für {} generieren"
 
-#: aleksis/apps/alsijil/views.py:659
+#: aleksis/apps/alsijil/views.py:657
 msgid "Generate full register printout …"
 msgstr "Vollständigen Klassenbuchausdruck generieren …"
 
-#: aleksis/apps/alsijil/views.py:660
+#: aleksis/apps/alsijil/views.py:658
 msgid "The printout has been generated successfully."
 msgstr "Der Ausdruck wurde erfolgreich generiert."
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
 msgstr "Es ist ein Fehler beim Generieren des Ausdrucks aufgetreten."
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
 msgstr "PDF herunterladen"
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr "Die Abwesenheit wurde gespeichert."
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 msgid "The personal note has been deleted."
 msgstr "Die persönliche Notiz wurde gelöscht."
 
-#: aleksis/apps/alsijil/views.py:1074
-msgid "The extra mark has been created."
-msgstr "Die zusätzliche Markierung wurde erstellt."
-
-#: aleksis/apps/alsijil/views.py:1086
-msgid "The extra mark has been saved."
-msgstr "Die zusätzliche Markierung wurde gespeichert."
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr "Die zusätzliche Markierung wurde gelöscht."
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr "Die Entschuldigungsart wurde erstellt."
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr "Die Entschuldigunsart wurde gespeichert."
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr "Die Entschuldigungsart wurde gelöscht."
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 msgid "The group role has been created."
 msgstr "Die Gruppenrolle wurde erstellt."
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 msgid "The group role has been saved."
 msgstr "Die Gruppenrolle wurde gespeichert."
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 msgid "The group role has been deleted."
 msgstr "Die Gruppenrolle wurde gelöscht."
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 msgid "The group role has been assigned."
 msgstr "Die Gruppenrolle wurde zugewiesen."
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 msgid "The group role assignment has been saved."
 msgstr "Die Gruppenrollenzuweisung wurde gespeichert."
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 msgid "The group role assignment has been stopped."
 msgstr "Die Gruppenrollenzuweisung wurde beendet."
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 msgid "The group role assignment has been deleted."
 msgstr "Die Gruppenrollenzuweisung wurde gelöscht."
 
+#~ msgid "Create extra mark"
+#~ msgstr "Zusätzliche Markierung erstellen"
+
+#~ msgid "Edit extra mark"
+#~ msgstr "Zusätzliche Markierung bearbeiten"
+
+#~ msgid "The extra mark has been created."
+#~ msgstr "Die zusätzliche Markierung wurde erstellt."
+
+#~ msgid "The extra mark has been saved."
+#~ msgstr "Die zusätzliche Markierung wurde gespeichert."
+
+#~ msgid "The extra mark has been deleted."
+#~ msgstr "Die zusätzliche Markierung wurde gelöscht."
+
 #~ msgid "Current lesson"
 #~ msgstr "Aktuelle Unterrichtsstunde"
 
diff --git a/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po
index e283f3797931d09214dbc06954730515b45eda92..37bfd8669e34b3235d3f1de6bbd93d5402b9aacf 100644
--- a/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
 "PO-Revision-Date: 2021-06-16 11:59+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: French <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/fr/>\n"
@@ -33,8 +33,8 @@ msgstr "Injustifié(e)"
 msgid "Mark as {excuse_type.name}"
 msgstr ""
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr ""
@@ -123,7 +123,7 @@ msgid "Before"
 msgstr ""
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
@@ -168,20 +168,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr "Personne"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr "Date de début"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr "Date de fin"
@@ -198,8 +198,8 @@ msgstr "De la période"
 msgid "End period"
 msgstr "De la période"
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -208,7 +208,7 @@ msgstr "De la période"
 msgid "Absent"
 msgstr "Absent(e)"
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -218,8 +218,8 @@ msgstr "Absent(e)"
 msgid "Excused"
 msgstr "Excusé"
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
 #, fuzzy
@@ -244,7 +244,7 @@ msgstr ""
 msgid "Has lesson documentation"
 msgstr "Documentation de cours"
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -252,7 +252,7 @@ msgstr "Documentation de cours"
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 msgid "Event"
 msgstr ""
 
@@ -306,29 +306,29 @@ msgstr ""
 msgid "Can register an absence for a person"
 msgstr "Registre de la classe"
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
 #, fuzzy
 #| msgid "First name"
 msgid "Short name"
 msgstr "Prénom"
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
 msgid "Count as absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:65
+#: aleksis/apps/alsijil/models.py:71
 msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
@@ -337,14 +337,12 @@ msgstr ""
 msgid "Excuse types"
 msgstr "Excusé"
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
 msgid "Year"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
@@ -352,20 +350,20 @@ msgstr ""
 msgid "Extra marks"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300
 #, fuzzy
 #| msgid "Personal notes"
 msgid "Personal note"
 msgstr "Notes personnelles"
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
 msgid "Personal notes"
 msgstr "Notes personnelles"
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
@@ -374,7 +372,7 @@ msgstr "Notes personnelles"
 msgid "Lesson topic"
 msgstr "Sujet de cours"
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
@@ -383,7 +381,7 @@ msgstr "Sujet de cours"
 msgid "Homework"
 msgstr "Devoirs"
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
@@ -393,12 +391,12 @@ msgstr "Devoirs"
 msgid "Group note"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
 msgid "Lesson documentation"
 msgstr "Documentation de cours"
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
 #, fuzzy
@@ -406,118 +404,136 @@ msgstr "Documentation de cours"
 msgid "Lesson documentations"
 msgstr "Documentation de cours"
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
 msgid "Teachers"
 msgstr "Profs"
 
-#: aleksis/apps/alsijil/models.py:482
+#: aleksis/apps/alsijil/models.py:490
 #, fuzzy
 #| msgid "Lesson topic"
 msgid "Lesson Topic"
 msgstr "Sujet de cours"
 
-#: aleksis/apps/alsijil/models.py:484
+#: aleksis/apps/alsijil/models.py:492
 #, fuzzy
 #| msgid "Group"
 msgid "Group Note"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
+#: aleksis/apps/alsijil/models.py:496
+msgid "Participation touched at"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886
 #, fuzzy
 #| msgid "Lesson documentation"
 msgid "Documentation"
 msgstr "Documentation de cours"
 
-#: aleksis/apps/alsijil/models.py:513
+#: aleksis/apps/alsijil/models.py:526
 #, fuzzy
 #| msgid "Lesson documentation"
 msgid "Documentations"
 msgstr "Documentation de cours"
 
-#: aleksis/apps/alsijil/models.py:612
+#: aleksis/apps/alsijil/models.py:791
 #, fuzzy
 #| msgid "Group"
 msgid "Groups of Person"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:625
+#: aleksis/apps/alsijil/models.py:804
 #, fuzzy
 #| msgid "Absences"
 msgid "Absence Reason"
 msgstr "Absences"
 
-#: aleksis/apps/alsijil/models.py:634
+#: aleksis/apps/alsijil/models.py:816
 #, fuzzy
 #| msgid "Absences"
 msgid "Base Absence"
 msgstr "Absences"
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+msgid "Tardiness"
+msgstr "Retard"
+
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:670
+#: aleksis/apps/alsijil/models.py:891
 #, fuzzy
 #| msgid "Notes"
 msgid "Note"
 msgstr "Notes"
 
-#: aleksis/apps/alsijil/models.py:672
+#: aleksis/apps/alsijil/models.py:893
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:679
+#: aleksis/apps/alsijil/models.py:900
 #, fuzzy
 #| msgid "Personal notes"
 msgid "Personal Note"
 msgstr "Notes personnelles"
 
-#: aleksis/apps/alsijil/models.py:680
+#: aleksis/apps/alsijil/models.py:901
 #, fuzzy
 #| msgid "Personal notes"
 msgid "Personal Notes"
 msgstr "Notes personnelles"
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 #, fuzzy
 #| msgid "Group"
 msgid "Group role"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
@@ -526,19 +542,19 @@ msgstr "Groupe"
 msgid "Group roles"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934
 #, fuzzy
 #| msgid "Persons in group"
 msgid "Can assign group role"
 msgstr "Personnes en groupe"
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953
 #, fuzzy
 #| msgid "Absences"
 msgid "Assigned person"
 msgstr "Absences"
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
@@ -547,183 +563,188 @@ msgstr "Absences"
 msgid "Groups"
 msgstr "Groupe"
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988
 #, fuzzy
 #| msgid "Personal overview"
 msgid "Can view lesson overview"
 msgstr "Vue d'ensemble personnelle"
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990
 #, fuzzy
 #| msgid "Class register"
 msgid "Can view full register"
 msgstr "Registre de la classe"
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991
 #, fuzzy
 #| msgid "Register absence"
 msgid "Can register absence"
 msgstr "Registre de Absence"
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992
 #, fuzzy
 #| msgid "List of all personal note filters"
 msgid "Can list all personal note filters"
 msgstr "Liste de filtres de notes personnelles"
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
 msgid "Class register"
 msgstr "Registre de la classe"
 
-#: aleksis/apps/alsijil/preferences.py:17
+#: aleksis/apps/alsijil/preferences.py:24
 msgid "Block adding personal notes for cancelled lessons"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:25
+#: aleksis/apps/alsijil/preferences.py:32
 msgid "Allow users to view their own personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:34
+#: aleksis/apps/alsijil/preferences.py:41
 msgid "Allow primary group owners to register future absences for students in their groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:44
+#: aleksis/apps/alsijil/preferences.py:51
 msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:54
+#: aleksis/apps/alsijil/preferences.py:61
 msgid "Allow original teachers to edit their lessons although they are substituted"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:63
+#: aleksis/apps/alsijil/preferences.py:70
 msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:66
+#: aleksis/apps/alsijil/preferences.py:73
 msgid "This will carry over data only if the data in the following periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:75
+#: aleksis/apps/alsijil/preferences.py:82
 msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:79
+#: aleksis/apps/alsijil/preferences.py:86
 msgid "This will carry over data only if the data in the aforementioned periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:88
+#: aleksis/apps/alsijil/preferences.py:95
 msgid "Carry over personal notes to all following lesson periods on the same day."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:97
+#: aleksis/apps/alsijil/preferences.py:104
 msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:101
+#: aleksis/apps/alsijil/preferences.py:108
 msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:110
+#: aleksis/apps/alsijil/preferences.py:117
 #, fuzzy
 #| msgid "Teachers and lessons in group"
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr "Profs et cours en groupe"
 
-#: aleksis/apps/alsijil/preferences.py:119
+#: aleksis/apps/alsijil/preferences.py:126
 msgid "Allow group owners to assign group roles to the parents of the group's members"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:128
+#: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:129
+#: aleksis/apps/alsijil/preferences.py:136
 msgid "Only week view of groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:137
+#: aleksis/apps/alsijil/preferences.py:144
 msgid "Show assigned group roles in lesson view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:147
+#: aleksis/apps/alsijil/preferences.py:154
 msgid "Items per page in lessons table"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:151
+#: aleksis/apps/alsijil/preferences.py:158
 msgid "Each page must show at least one item."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:159
+#: aleksis/apps/alsijil/preferences.py:166
 msgid "Filter lessons by existence of their lesson documentation on default"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:170
+#: aleksis/apps/alsijil/preferences.py:177
 msgid "Allow editing of all future documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:173
+#: aleksis/apps/alsijil/preferences.py:180
 msgid "Allow editing of all documentations up to and including those on the current day"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:178
+#: aleksis/apps/alsijil/preferences.py:185
 msgid "Allow editing of all documentations up to and including those on the current date and time"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:183
+#: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr ""
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr "Date"
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr "Période"
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr "Retard"
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 #, fuzzy
 #| msgid "Excused"
 msgid "Excuse"
 msgstr "Excusé"
 
-#: aleksis/apps/alsijil/tables.py:156
+#: aleksis/apps/alsijil/tables.py:136
 #, fuzzy, python-brace-format
 #| msgid "Summed up tardiness"
 msgid "{value}' tardiness"
@@ -1101,17 +1122,6 @@ msgid ""
 "  "
 msgstr ""
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr ""
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr ""
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1646,126 +1656,120 @@ msgstr "Vue de semaine"
 msgid "Notes"
 msgstr "Notes"
 
-#: aleksis/apps/alsijil/views.py:114
+#: aleksis/apps/alsijil/views.py:112
 msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:147
+#: aleksis/apps/alsijil/views.py:145
 msgid "You are not allowed to create a lesson documentation for a lesson in the future."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The lesson documentation has been saved."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:658
+#: aleksis/apps/alsijil/views.py:656
 msgid "Generate full register printout for {}"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:659
+#: aleksis/apps/alsijil/views.py:657
 msgid "Generate full register printout …"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:660
+#: aleksis/apps/alsijil/views.py:658
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The printout has been generated successfully."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The personal note has been deleted."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:1074
-#, fuzzy
-#| msgid "Lesson documentation for calendar week"
-msgid "The extra mark has been created."
-msgstr "Documentation de cours pour la semaine calendrier"
-
-#: aleksis/apps/alsijil/views.py:1086
-#, fuzzy
-#| msgid "Lesson documentation for calendar week"
-msgid "The extra mark has been saved."
-msgstr "Documentation de cours pour la semaine calendrier"
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The group role has been created."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The group role has been saved."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The group role has been deleted."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The group role has been assigned."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The group role assignment has been saved."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The group role assignment has been stopped."
 msgstr "Documentation de cours pour la semaine calendrier"
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 #, fuzzy
 #| msgid "Lesson documentation for calendar week"
 msgid "The group role assignment has been deleted."
 msgstr "Documentation de cours pour la semaine calendrier"
 
+#, fuzzy
+#~| msgid "Lesson documentation for calendar week"
+#~ msgid "The extra mark has been created."
+#~ msgstr "Documentation de cours pour la semaine calendrier"
+
+#, fuzzy
+#~| msgid "Lesson documentation for calendar week"
+#~ msgid "The extra mark has been saved."
+#~ msgstr "Documentation de cours pour la semaine calendrier"
+
 #~ msgid "Current lesson"
 #~ msgstr "Lecon actuelle"
 
diff --git a/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po
index a3ca7ecd62424293afd0f457fe4b67c95cbeb5af..ffbc44cdc05e07e5e46c4578ae0e7db6a6fd31fa 100644
--- a/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/la/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
 "PO-Revision-Date: 2020-07-26 14:08+0000\n"
 "Last-Translator: Julian <leuckerj@gmail.com>\n"
 "Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/la/>\n"
@@ -31,8 +31,8 @@ msgstr ""
 msgid "Mark as {excuse_type.name}"
 msgstr ""
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr ""
@@ -111,7 +111,7 @@ msgid "Before"
 msgstr ""
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
@@ -152,20 +152,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr "Persona"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -178,8 +178,8 @@ msgstr ""
 msgid "End period"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -188,7 +188,7 @@ msgstr ""
 msgid "Absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -198,8 +198,8 @@ msgstr ""
 msgid "Excused"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
 msgid "Excuse type"
@@ -220,7 +220,7 @@ msgstr ""
 msgid "Has lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -228,7 +228,7 @@ msgstr ""
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 msgid "Event"
 msgstr ""
 
@@ -272,43 +272,41 @@ msgstr ""
 msgid "Can register an absence for a person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
 #, fuzzy
 #| msgid "First name"
 msgid "Short name"
 msgstr "Primus nomen"
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
 msgid "Count as absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:65
+#: aleksis/apps/alsijil/models.py:71
 msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
 msgid "Excuse types"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
 msgid "Year"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
@@ -316,20 +314,20 @@ msgstr ""
 msgid "Extra marks"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300
 #, fuzzy
 #| msgid "Person"
 msgid "Personal note"
 msgstr "Persona"
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
 msgid "Personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
@@ -338,7 +336,7 @@ msgstr ""
 msgid "Lesson topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
@@ -347,7 +345,7 @@ msgstr ""
 msgid "Homework"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
@@ -357,117 +355,135 @@ msgstr ""
 msgid "Group note"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
 msgid "Lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
 msgid "Lesson documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
 msgid "Teachers"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:482
+#: aleksis/apps/alsijil/models.py:490
 msgid "Lesson Topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:484
+#: aleksis/apps/alsijil/models.py:492
 #, fuzzy
 #| msgid "Group"
 msgid "Group Note"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
+#: aleksis/apps/alsijil/models.py:496
+msgid "Participation touched at"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886
 msgid "Documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:513
+#: aleksis/apps/alsijil/models.py:526
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:612
+#: aleksis/apps/alsijil/models.py:791
 #, fuzzy
 #| msgid "Group"
 msgid "Groups of Person"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:625
+#: aleksis/apps/alsijil/models.py:804
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:634
+#: aleksis/apps/alsijil/models.py:816
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+msgid "Tardiness"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:670
+#: aleksis/apps/alsijil/models.py:891
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:672
+#: aleksis/apps/alsijil/models.py:893
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:679
+#: aleksis/apps/alsijil/models.py:900
 #, fuzzy
 #| msgid "Person"
 msgid "Personal Note"
 msgstr "Persona"
 
-#: aleksis/apps/alsijil/models.py:680
+#: aleksis/apps/alsijil/models.py:901
 #, fuzzy
 #| msgid "Person"
 msgid "Personal Notes"
 msgstr "Persona"
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 #, fuzzy
 #| msgid "Group"
 msgid "Group role"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
@@ -476,15 +492,15 @@ msgstr "Grex"
 msgid "Group roles"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
@@ -493,171 +509,176 @@ msgstr ""
 msgid "Groups"
 msgstr "Grex"
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
 msgid "Class register"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:17
+#: aleksis/apps/alsijil/preferences.py:24
 msgid "Block adding personal notes for cancelled lessons"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:25
+#: aleksis/apps/alsijil/preferences.py:32
 msgid "Allow users to view their own personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:34
+#: aleksis/apps/alsijil/preferences.py:41
 msgid "Allow primary group owners to register future absences for students in their groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:44
+#: aleksis/apps/alsijil/preferences.py:51
 msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:54
+#: aleksis/apps/alsijil/preferences.py:61
 msgid "Allow original teachers to edit their lessons although they are substituted"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:63
+#: aleksis/apps/alsijil/preferences.py:70
 msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:66
+#: aleksis/apps/alsijil/preferences.py:73
 msgid "This will carry over data only if the data in the following periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:75
+#: aleksis/apps/alsijil/preferences.py:82
 msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:79
+#: aleksis/apps/alsijil/preferences.py:86
 msgid "This will carry over data only if the data in the aforementioned periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:88
+#: aleksis/apps/alsijil/preferences.py:95
 msgid "Carry over personal notes to all following lesson periods on the same day."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:97
+#: aleksis/apps/alsijil/preferences.py:104
 msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:101
+#: aleksis/apps/alsijil/preferences.py:108
 msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:110
+#: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:119
+#: aleksis/apps/alsijil/preferences.py:126
 msgid "Allow group owners to assign group roles to the parents of the group's members"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:128
+#: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:129
+#: aleksis/apps/alsijil/preferences.py:136
 msgid "Only week view of groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:137
+#: aleksis/apps/alsijil/preferences.py:144
 msgid "Show assigned group roles in lesson view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:147
+#: aleksis/apps/alsijil/preferences.py:154
 msgid "Items per page in lessons table"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:151
+#: aleksis/apps/alsijil/preferences.py:158
 msgid "Each page must show at least one item."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:159
+#: aleksis/apps/alsijil/preferences.py:166
 msgid "Filter lessons by existence of their lesson documentation on default"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:170
+#: aleksis/apps/alsijil/preferences.py:177
 msgid "Allow editing of all future documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:173
+#: aleksis/apps/alsijil/preferences.py:180
 msgid "Allow editing of all documentations up to and including those on the current day"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:178
+#: aleksis/apps/alsijil/preferences.py:185
 msgid "Allow editing of all documentations up to and including those on the current date and time"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:183
+#: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr ""
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr "dies"
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr ""
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 msgid "Excuse"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:156
+#: aleksis/apps/alsijil/tables.py:136
 #, python-brace-format
 msgid "{value}' tardiness"
 msgstr ""
@@ -992,17 +1013,6 @@ msgid ""
 "  "
 msgstr ""
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr ""
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr ""
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1500,99 +1510,87 @@ msgstr ""
 msgid "Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:114
+#: aleksis/apps/alsijil/views.py:112
 msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:147
+#: aleksis/apps/alsijil/views.py:145
 msgid "You are not allowed to create a lesson documentation for a lesson in the future."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:658
+#: aleksis/apps/alsijil/views.py:656
 msgid "Generate full register printout for {}"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:659
+#: aleksis/apps/alsijil/views.py:657
 msgid "Generate full register printout …"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:660
+#: aleksis/apps/alsijil/views.py:658
 msgid "The printout has been generated successfully."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 msgid "The personal note has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1074
-msgid "The extra mark has been created."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1086
-msgid "The extra mark has been saved."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 msgid "The group role has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 msgid "The group role has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 msgid "The group role has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 msgid "The group role has been assigned."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 msgid "The group role assignment has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 msgid "The group role assignment has been stopped."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 msgid "The group role assignment has been deleted."
 msgstr ""
 
diff --git a/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po
index 4a23675366e43a10b5190e5dfd31b46a442dd86f..40892069d224a5e6ef62ed3a0ce7fe80720ca876 100644
--- a/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/nb_NO/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -30,8 +30,8 @@ msgstr ""
 msgid "Mark as {excuse_type.name}"
 msgstr ""
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr ""
@@ -110,7 +110,7 @@ msgid "Before"
 msgstr ""
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
@@ -151,20 +151,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -177,8 +177,8 @@ msgstr ""
 msgid "End period"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -187,7 +187,7 @@ msgstr ""
 msgid "Absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -197,8 +197,8 @@ msgstr ""
 msgid "Excused"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
 msgid "Excuse type"
@@ -219,7 +219,7 @@ msgstr ""
 msgid "Has lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -227,7 +227,7 @@ msgstr ""
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 msgid "Event"
 msgstr ""
 
@@ -271,41 +271,39 @@ msgstr ""
 msgid "Can register an absence for a person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
 msgid "Short name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
 msgid "Count as absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:65
+#: aleksis/apps/alsijil/models.py:71
 msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
 msgid "Excuse types"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
 msgid "Year"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
@@ -313,18 +311,18 @@ msgstr ""
 msgid "Extra marks"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300
 msgid "Personal note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
 msgid "Personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
@@ -333,7 +331,7 @@ msgstr ""
 msgid "Lesson topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
@@ -342,7 +340,7 @@ msgstr ""
 msgid "Homework"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
@@ -350,293 +348,316 @@ msgstr ""
 msgid "Group note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
 msgid "Lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
 msgid "Lesson documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
 msgid "Teachers"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:482
+#: aleksis/apps/alsijil/models.py:490
 msgid "Lesson Topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:484
+#: aleksis/apps/alsijil/models.py:492
 msgid "Group Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
+#: aleksis/apps/alsijil/models.py:496
+msgid "Participation touched at"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886
 msgid "Documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:513
+#: aleksis/apps/alsijil/models.py:526
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:612
+#: aleksis/apps/alsijil/models.py:791
 msgid "Groups of Person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:625
+#: aleksis/apps/alsijil/models.py:804
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:634
+#: aleksis/apps/alsijil/models.py:816
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+msgid "Tardiness"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:670
+#: aleksis/apps/alsijil/models.py:891
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:672
+#: aleksis/apps/alsijil/models.py:893
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:679
+#: aleksis/apps/alsijil/models.py:900
 msgid "Personal Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:680
+#: aleksis/apps/alsijil/models.py:901
 msgid "Personal Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
 msgid "Class register"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:17
+#: aleksis/apps/alsijil/preferences.py:24
 msgid "Block adding personal notes for cancelled lessons"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:25
+#: aleksis/apps/alsijil/preferences.py:32
 msgid "Allow users to view their own personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:34
+#: aleksis/apps/alsijil/preferences.py:41
 msgid "Allow primary group owners to register future absences for students in their groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:44
+#: aleksis/apps/alsijil/preferences.py:51
 msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:54
+#: aleksis/apps/alsijil/preferences.py:61
 msgid "Allow original teachers to edit their lessons although they are substituted"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:63
+#: aleksis/apps/alsijil/preferences.py:70
 msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:66
+#: aleksis/apps/alsijil/preferences.py:73
 msgid "This will carry over data only if the data in the following periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:75
+#: aleksis/apps/alsijil/preferences.py:82
 msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:79
+#: aleksis/apps/alsijil/preferences.py:86
 msgid "This will carry over data only if the data in the aforementioned periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:88
+#: aleksis/apps/alsijil/preferences.py:95
 msgid "Carry over personal notes to all following lesson periods on the same day."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:97
+#: aleksis/apps/alsijil/preferences.py:104
 msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:101
+#: aleksis/apps/alsijil/preferences.py:108
 msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:110
+#: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:119
+#: aleksis/apps/alsijil/preferences.py:126
 msgid "Allow group owners to assign group roles to the parents of the group's members"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:128
+#: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:129
+#: aleksis/apps/alsijil/preferences.py:136
 msgid "Only week view of groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:137
+#: aleksis/apps/alsijil/preferences.py:144
 msgid "Show assigned group roles in lesson view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:147
+#: aleksis/apps/alsijil/preferences.py:154
 msgid "Items per page in lessons table"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:151
+#: aleksis/apps/alsijil/preferences.py:158
 msgid "Each page must show at least one item."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:159
+#: aleksis/apps/alsijil/preferences.py:166
 msgid "Filter lessons by existence of their lesson documentation on default"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:170
+#: aleksis/apps/alsijil/preferences.py:177
 msgid "Allow editing of all future documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:173
+#: aleksis/apps/alsijil/preferences.py:180
 msgid "Allow editing of all documentations up to and including those on the current day"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:178
+#: aleksis/apps/alsijil/preferences.py:185
 msgid "Allow editing of all documentations up to and including those on the current date and time"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:183
+#: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr ""
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr ""
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 msgid "Excuse"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:156
+#: aleksis/apps/alsijil/tables.py:136
 #, python-brace-format
 msgid "{value}' tardiness"
 msgstr ""
@@ -963,17 +984,6 @@ msgid ""
 "  "
 msgstr ""
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr ""
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr ""
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1467,98 +1477,86 @@ msgstr ""
 msgid "Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:114
+#: aleksis/apps/alsijil/views.py:112
 msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:147
+#: aleksis/apps/alsijil/views.py:145
 msgid "You are not allowed to create a lesson documentation for a lesson in the future."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:658
+#: aleksis/apps/alsijil/views.py:656
 msgid "Generate full register printout for {}"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:659
+#: aleksis/apps/alsijil/views.py:657
 msgid "Generate full register printout …"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:660
+#: aleksis/apps/alsijil/views.py:658
 msgid "The printout has been generated successfully."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 msgid "The personal note has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1074
-msgid "The extra mark has been created."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1086
-msgid "The extra mark has been saved."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 msgid "The group role has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 msgid "The group role has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 msgid "The group role has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 msgid "The group role has been assigned."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 msgid "The group role assignment has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 msgid "The group role assignment has been stopped."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 msgid "The group role assignment has been deleted."
 msgstr ""
diff --git a/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po
index e1467925c5f0b0d1b4d9eebf9a8eea986c3785f3..b5a37983138f9498ff9cea509867a17a8c920ab0 100644
--- a/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
 "PO-Revision-Date: 2023-05-26 04:38+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
 "Language-Team: Russian <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/ru/>\n"
@@ -31,8 +31,8 @@ msgstr "Отметить без уважительной причины"
 msgid "Mark as {excuse_type.name}"
 msgstr "Отметить как {excuse_type.name}"
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr "Удалить"
@@ -111,7 +111,7 @@ msgid "Before"
 msgstr "До"
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
@@ -152,20 +152,20 @@ msgid "You can't select a group and a teacher both."
 msgstr "Вы не можете одновременно выбрать группу и преподавателя."
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr "Физлицо"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr "Дата начала"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr "Дата окончания"
@@ -178,8 +178,8 @@ msgstr "Начало уроков"
 msgid "End period"
 msgstr "Окончание уроков"
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -188,7 +188,7 @@ msgstr "Окончание уроков"
 msgid "Absent"
 msgstr "Отсутствует"
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -198,8 +198,8 @@ msgstr "Отсутствует"
 msgid "Excused"
 msgstr "Уважительно"
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
 msgid "Excuse type"
@@ -220,7 +220,7 @@ msgstr "Учебный год"
 msgid "Has lesson documentation"
 msgstr "Имеет в наличии учебный материал"
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -228,7 +228,7 @@ msgstr "Имеет в наличии учебный материал"
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 msgid "Event"
 msgstr "Событие"
 
@@ -272,41 +272,39 @@ msgstr "Может назначать роль группы для этой гр
 msgid "Can register an absence for a person"
 msgstr "Может регистрировать отсутствия"
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
 msgid "Short name"
 msgstr "Короткое имя"
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr "Полное имя"
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
 msgid "Count as absent"
 msgstr "Количество отсутствующих"
 
-#: aleksis/apps/alsijil/models.py:65
+#: aleksis/apps/alsijil/models.py:71
 msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
 msgstr "Если отмечено, этот тип объяснительной будет засчитан как пропущенный урок. Если не отмечено, то без записи в отчет о пропусках."
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
 msgid "Excuse types"
 msgstr "Типы объяснительных"
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
 msgid "Year"
 msgstr "Год"
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
@@ -314,18 +312,18 @@ msgstr "Год"
 msgid "Extra marks"
 msgstr "Дополнительные отметки"
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300
 msgid "Personal note"
 msgstr "Личная заметка"
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
 msgid "Personal notes"
 msgstr "Личные заметки"
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
@@ -334,7 +332,7 @@ msgstr "Личные заметки"
 msgid "Lesson topic"
 msgstr "Тема урока"
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
@@ -343,7 +341,7 @@ msgstr "Тема урока"
 msgid "Homework"
 msgstr "Домашняя работа"
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
@@ -351,315 +349,338 @@ msgstr "Домашняя работа"
 msgid "Group note"
 msgstr "Групповая заметка"
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
 msgid "Lesson documentation"
 msgstr "Учебный материал"
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
 msgid "Lesson documentations"
 msgstr "Учебные материалы"
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
 msgstr "Дополнительная отметка"
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
 msgid "Teachers"
 msgstr "Преподаватели"
 
-#: aleksis/apps/alsijil/models.py:482
+#: aleksis/apps/alsijil/models.py:490
 #, fuzzy
 #| msgid "Lesson topic"
 msgid "Lesson Topic"
 msgstr "Тема урока"
 
-#: aleksis/apps/alsijil/models.py:484
+#: aleksis/apps/alsijil/models.py:492
 #, fuzzy
 #| msgid "Group note"
 msgid "Group Note"
 msgstr "Групповая заметка"
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
+#: aleksis/apps/alsijil/models.py:496
+msgid "Participation touched at"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886
 #, fuzzy
 #| msgid "Lesson documentation"
 msgid "Documentation"
 msgstr "Учебный материал"
 
-#: aleksis/apps/alsijil/models.py:513
+#: aleksis/apps/alsijil/models.py:526
 #, fuzzy
 #| msgid "Lesson documentations"
 msgid "Documentations"
 msgstr "Учебные материалы"
 
-#: aleksis/apps/alsijil/models.py:612
+#: aleksis/apps/alsijil/models.py:791
 #, fuzzy
 #| msgid "Group roles"
 msgid "Groups of Person"
 msgstr "Роли групп"
 
-#: aleksis/apps/alsijil/models.py:625
+#: aleksis/apps/alsijil/models.py:804
 #, fuzzy
 #| msgid "Absences"
 msgid "Absence Reason"
 msgstr "Пропуски"
 
-#: aleksis/apps/alsijil/models.py:634
+#: aleksis/apps/alsijil/models.py:816
 #, fuzzy
 #| msgid "Absences"
 msgid "Base Absence"
 msgstr "Пропуски"
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+msgid "Tardiness"
+msgstr "Опоздание"
+
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:670
+#: aleksis/apps/alsijil/models.py:891
 #, fuzzy
 #| msgid "Notes"
 msgid "Note"
 msgstr "Заметки"
 
-#: aleksis/apps/alsijil/models.py:672
+#: aleksis/apps/alsijil/models.py:893
 #, fuzzy
 #| msgid "Extra mark"
 msgid "Extra Mark"
 msgstr "Дополнительная отметка"
 
-#: aleksis/apps/alsijil/models.py:679
+#: aleksis/apps/alsijil/models.py:900
 #, fuzzy
 #| msgid "Personal note"
 msgid "Personal Note"
 msgstr "Личная заметка"
 
-#: aleksis/apps/alsijil/models.py:680
+#: aleksis/apps/alsijil/models.py:901
 #, fuzzy
 #| msgid "Personal notes"
 msgid "Personal Notes"
 msgstr "Личные заметки"
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr "Иконка"
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr "Цвет"
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr "Роль группы"
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr "Роли групп"
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr "Может назначать роль группы"
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr "Назначенное физлицо"
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr "Группы"
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr "Если нет точной конечной даты, можно оставить незаполненным"
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr "Назначение роли группы"
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr "Назначение ролей групп"
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr "Может просматривать обзор урока"
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr "Может просматривать недельный обзор"
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr "Может просматривать весь журнал"
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr "Может регистрировать отсутствие"
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr "Может просматривать все фильтры личных заметок"
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
 msgid "Class register"
 msgstr "Классный журнал"
 
-#: aleksis/apps/alsijil/preferences.py:17
+#: aleksis/apps/alsijil/preferences.py:24
 msgid "Block adding personal notes for cancelled lessons"
 msgstr "Блокировать добавление личных заметок к отмененным урокам"
 
-#: aleksis/apps/alsijil/preferences.py:25
+#: aleksis/apps/alsijil/preferences.py:32
 msgid "Allow users to view their own personal notes"
 msgstr "Разрешить пользователям просматривать свои личные заметки"
 
-#: aleksis/apps/alsijil/preferences.py:34
+#: aleksis/apps/alsijil/preferences.py:41
 msgid "Allow primary group owners to register future absences for students in their groups"
 msgstr "Разрешить владельцам основных групп регистрировать будущие пропуски студентов в своих группах"
 
-#: aleksis/apps/alsijil/preferences.py:44
+#: aleksis/apps/alsijil/preferences.py:51
 msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
 msgstr "Наделить владельца родительской группы такими же правами, как у владельца соответствующих дочерних групп"
 
-#: aleksis/apps/alsijil/preferences.py:54
+#: aleksis/apps/alsijil/preferences.py:61
 msgid "Allow original teachers to edit their lessons although they are substituted"
 msgstr "Разрешить изначальным преподавателям редактировать свои уроки даже после их замены"
 
-#: aleksis/apps/alsijil/preferences.py:63
+#: aleksis/apps/alsijil/preferences.py:70
 msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr "Переносить данные с первого урока в расписании на текущие уроки через несколько уроков"
 
-#: aleksis/apps/alsijil/preferences.py:66
+#: aleksis/apps/alsijil/preferences.py:73
 msgid "This will carry over data only if the data in the following periods are empty."
 msgstr "Это перенесёт данные только в случае отсутствия данных в последующих уроках."
 
-#: aleksis/apps/alsijil/preferences.py:75
+#: aleksis/apps/alsijil/preferences.py:82
 msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
 msgstr "Разрешить перенос данных с любого урока на все такие же уроки                 с таким же номером в расписании на той же неделе"
 
-#: aleksis/apps/alsijil/preferences.py:79
+#: aleksis/apps/alsijil/preferences.py:86
 msgid "This will carry over data only if the data in the aforementioned periods are empty."
 msgstr "Это перенесёт данные только если в упомянутых выше уроках данные не заполнены."
 
-#: aleksis/apps/alsijil/preferences.py:88
+#: aleksis/apps/alsijil/preferences.py:95
 msgid "Carry over personal notes to all following lesson periods on the same day."
 msgstr "Переносить личные заметки на все последующие уроки того же дня."
 
-#: aleksis/apps/alsijil/preferences.py:97
+#: aleksis/apps/alsijil/preferences.py:104
 msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
 msgstr "Разрешить преподавателям открывать уроки в тот же день, а не только в начале уроков"
 
-#: aleksis/apps/alsijil/preferences.py:101
+#: aleksis/apps/alsijil/preferences.py:108
 msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
 msgstr "Эти настройки не влияют на прошлые уроки. Вы можете открывать их когда-угодно."
 
-#: aleksis/apps/alsijil/preferences.py:110
+#: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr "Разрешить преподавателям добавлять учебный материал (данные для уроков) на выходных"
 
-#: aleksis/apps/alsijil/preferences.py:119
+#: aleksis/apps/alsijil/preferences.py:126
 msgid "Allow group owners to assign group roles to the parents of the group's members"
 msgstr "Разрешить владельцам групп назначать роли групп родителям участников групп"
 
-#: aleksis/apps/alsijil/preferences.py:128
+#: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
 msgstr "Показать назначенные роли групп в недельном обзоре"
 
-#: aleksis/apps/alsijil/preferences.py:129
+#: aleksis/apps/alsijil/preferences.py:136
 msgid "Only week view of groups"
 msgstr "Только недельные обзоры групп"
 
-#: aleksis/apps/alsijil/preferences.py:137
+#: aleksis/apps/alsijil/preferences.py:144
 msgid "Show assigned group roles in lesson view"
 msgstr "Показать назначенные роли групп в обзоре урока"
 
-#: aleksis/apps/alsijil/preferences.py:147
+#: aleksis/apps/alsijil/preferences.py:154
 msgid "Items per page in lessons table"
 msgstr "Количество записей на страницу в таблице с расписанием"
 
-#: aleksis/apps/alsijil/preferences.py:151
+#: aleksis/apps/alsijil/preferences.py:158
 msgid "Each page must show at least one item."
 msgstr "Каждая страница должна содержать хотя бы одну строку."
 
-#: aleksis/apps/alsijil/preferences.py:159
+#: aleksis/apps/alsijil/preferences.py:166
 msgid "Filter lessons by existence of their lesson documentation on default"
 msgstr "По-умолчанию фильтровать уроки по наличию в них учебного материала"
 
-#: aleksis/apps/alsijil/preferences.py:170
+#: aleksis/apps/alsijil/preferences.py:177
 msgid "Allow editing of all future documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:173
+#: aleksis/apps/alsijil/preferences.py:180
 msgid "Allow editing of all documentations up to and including those on the current day"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:178
+#: aleksis/apps/alsijil/preferences.py:185
 msgid "Allow editing of all documentations up to and including those on the current date and time"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:183
+#: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr ""
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr "Редактировать"
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr "Дата"
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr "Урок"
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr "Опоздание"
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 msgid "Excuse"
 msgstr "Объяснительная"
 
-#: aleksis/apps/alsijil/tables.py:156
+#: aleksis/apps/alsijil/tables.py:136
 #, fuzzy, python-brace-format
 #| msgid "{value}' late"
 msgid "{value}' tardiness"
@@ -1016,17 +1037,6 @@ msgstr ""
 "    Не используйте этот функционал для создания объяснительной по-умолчанию или если не ведёте разделение по типам.\n"
 "  "
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr "Создать дополнительную отметку"
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr "Редактировать дополнительную отметку"
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1559,108 +1569,111 @@ msgstr "Неделя"
 msgid "Notes"
 msgstr "Заметки"
 
-#: aleksis/apps/alsijil/views.py:114
+#: aleksis/apps/alsijil/views.py:112
 msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr "Вы или выбрали неправильный урок, или сейчас урока нет."
 
-#: aleksis/apps/alsijil/views.py:147
+#: aleksis/apps/alsijil/views.py:145
 msgid "You are not allowed to create a lesson documentation for a lesson in the future."
 msgstr "Вам нельзя создавать учебные материалы для уроков в будущем."
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
 msgstr "Учебный материал сохранён."
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr "Личные заметки сохранены."
 
-#: aleksis/apps/alsijil/views.py:658
+#: aleksis/apps/alsijil/views.py:656
 #, fuzzy
 #| msgid "Generate printout"
 msgid "Generate full register printout for {}"
 msgstr "Подготовить к печати"
 
-#: aleksis/apps/alsijil/views.py:659
+#: aleksis/apps/alsijil/views.py:657
 #, fuzzy
 #| msgid "Generate printout"
 msgid "Generate full register printout …"
 msgstr "Подготовить к печати"
 
-#: aleksis/apps/alsijil/views.py:660
+#: aleksis/apps/alsijil/views.py:658
 #, fuzzy
 #| msgid "The personal note has been deleted."
 msgid "The printout has been generated successfully."
 msgstr "Личная заметка удалена."
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr "Отсутствие сохранено."
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 msgid "The personal note has been deleted."
 msgstr "Личная заметка удалена."
 
-#: aleksis/apps/alsijil/views.py:1074
-msgid "The extra mark has been created."
-msgstr "Дополнительная отметка создана."
-
-#: aleksis/apps/alsijil/views.py:1086
-msgid "The extra mark has been saved."
-msgstr "Дополнительная отметка сохранена."
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr "Дополнительная отметка удалена."
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr "Тип объяснительной создан."
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr "Тип объяснительной сохранён."
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr "Тип объяснительной удалён."
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 msgid "The group role has been created."
 msgstr "Роль группы создана."
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 msgid "The group role has been saved."
 msgstr "Роль группы сохранена."
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 msgid "The group role has been deleted."
 msgstr "Роль группы удалена."
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 msgid "The group role has been assigned."
 msgstr "Роль группы назначена."
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 msgid "The group role assignment has been saved."
 msgstr "Назначение роли группы сохранено."
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 msgid "The group role assignment has been stopped."
 msgstr "Назначение роли группы остановлено."
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 msgid "The group role assignment has been deleted."
 msgstr "Назначение роли группы удалено."
 
+#~ msgid "Create extra mark"
+#~ msgstr "Создать дополнительную отметку"
+
+#~ msgid "Edit extra mark"
+#~ msgstr "Редактировать дополнительную отметку"
+
+#~ msgid "The extra mark has been created."
+#~ msgstr "Дополнительная отметка создана."
+
+#~ msgid "The extra mark has been saved."
+#~ msgstr "Дополнительная отметка сохранена."
+
+#~ msgid "The extra mark has been deleted."
+#~ msgstr "Дополнительная отметка удалена."
+
 #~ msgid "Current lesson"
 #~ msgstr "Текущий урок"
 
diff --git a/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po
index 4a23675366e43a10b5190e5dfd31b46a442dd86f..40892069d224a5e6ef62ed3a0ce7fe80720ca876 100644
--- a/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/tr_TR/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -30,8 +30,8 @@ msgstr ""
 msgid "Mark as {excuse_type.name}"
 msgstr ""
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr ""
@@ -110,7 +110,7 @@ msgid "Before"
 msgstr ""
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
@@ -151,20 +151,20 @@ msgid "You can't select a group and a teacher both."
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr ""
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr ""
@@ -177,8 +177,8 @@ msgstr ""
 msgid "End period"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -187,7 +187,7 @@ msgstr ""
 msgid "Absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -197,8 +197,8 @@ msgstr ""
 msgid "Excused"
 msgstr ""
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
 msgid "Excuse type"
@@ -219,7 +219,7 @@ msgstr ""
 msgid "Has lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -227,7 +227,7 @@ msgstr ""
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 msgid "Event"
 msgstr ""
 
@@ -271,41 +271,39 @@ msgstr ""
 msgid "Can register an absence for a person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
 msgid "Short name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
 msgid "Count as absent"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:65
+#: aleksis/apps/alsijil/models.py:71
 msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
 msgid "Excuse types"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
 msgid "Year"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
@@ -313,18 +311,18 @@ msgstr ""
 msgid "Extra marks"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300
 msgid "Personal note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
 msgid "Personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
@@ -333,7 +331,7 @@ msgstr ""
 msgid "Lesson topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
@@ -342,7 +340,7 @@ msgstr ""
 msgid "Homework"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
@@ -350,293 +348,316 @@ msgstr ""
 msgid "Group note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
 msgid "Lesson documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
 msgid "Lesson documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
 msgid "Teachers"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:482
+#: aleksis/apps/alsijil/models.py:490
 msgid "Lesson Topic"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:484
+#: aleksis/apps/alsijil/models.py:492
 msgid "Group Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
+#: aleksis/apps/alsijil/models.py:496
+msgid "Participation touched at"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886
 msgid "Documentation"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:513
+#: aleksis/apps/alsijil/models.py:526
 msgid "Documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:612
+#: aleksis/apps/alsijil/models.py:791
 msgid "Groups of Person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:625
+#: aleksis/apps/alsijil/models.py:804
 msgid "Absence Reason"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:634
+#: aleksis/apps/alsijil/models.py:816
 msgid "Base Absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+msgid "Tardiness"
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:670
+#: aleksis/apps/alsijil/models.py:891
 msgid "Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:672
+#: aleksis/apps/alsijil/models.py:893
 msgid "Extra Mark"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:679
+#: aleksis/apps/alsijil/models.py:900
 msgid "Personal Note"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:680
+#: aleksis/apps/alsijil/models.py:901
 msgid "Personal Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr ""
+
+#: aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr ""
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
 msgid "Class register"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:17
+#: aleksis/apps/alsijil/preferences.py:24
 msgid "Block adding personal notes for cancelled lessons"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:25
+#: aleksis/apps/alsijil/preferences.py:32
 msgid "Allow users to view their own personal notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:34
+#: aleksis/apps/alsijil/preferences.py:41
 msgid "Allow primary group owners to register future absences for students in their groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:44
+#: aleksis/apps/alsijil/preferences.py:51
 msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:54
+#: aleksis/apps/alsijil/preferences.py:61
 msgid "Allow original teachers to edit their lessons although they are substituted"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:63
+#: aleksis/apps/alsijil/preferences.py:70
 msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:66
+#: aleksis/apps/alsijil/preferences.py:73
 msgid "This will carry over data only if the data in the following periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:75
+#: aleksis/apps/alsijil/preferences.py:82
 msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:79
+#: aleksis/apps/alsijil/preferences.py:86
 msgid "This will carry over data only if the data in the aforementioned periods are empty."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:88
+#: aleksis/apps/alsijil/preferences.py:95
 msgid "Carry over personal notes to all following lesson periods on the same day."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:97
+#: aleksis/apps/alsijil/preferences.py:104
 msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:101
+#: aleksis/apps/alsijil/preferences.py:108
 msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:110
+#: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:119
+#: aleksis/apps/alsijil/preferences.py:126
 msgid "Allow group owners to assign group roles to the parents of the group's members"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:128
+#: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:129
+#: aleksis/apps/alsijil/preferences.py:136
 msgid "Only week view of groups"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:137
+#: aleksis/apps/alsijil/preferences.py:144
 msgid "Show assigned group roles in lesson view"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:147
+#: aleksis/apps/alsijil/preferences.py:154
 msgid "Items per page in lessons table"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:151
+#: aleksis/apps/alsijil/preferences.py:158
 msgid "Each page must show at least one item."
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:159
+#: aleksis/apps/alsijil/preferences.py:166
 msgid "Filter lessons by existence of their lesson documentation on default"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:170
+#: aleksis/apps/alsijil/preferences.py:177
 msgid "Allow editing of all future documentations"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:173
+#: aleksis/apps/alsijil/preferences.py:180
 msgid "Allow editing of all documentations up to and including those on the current day"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:178
+#: aleksis/apps/alsijil/preferences.py:185
 msgid "Allow editing of all documentations up to and including those on the current date and time"
 msgstr ""
 
-#: aleksis/apps/alsijil/preferences.py:183
+#: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgstr ""
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr ""
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr ""
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 msgid "Excuse"
 msgstr ""
 
-#: aleksis/apps/alsijil/tables.py:156
+#: aleksis/apps/alsijil/tables.py:136
 #, python-brace-format
 msgid "{value}' tardiness"
 msgstr ""
@@ -963,17 +984,6 @@ msgid ""
 "  "
 msgstr ""
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr ""
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr ""
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1467,98 +1477,86 @@ msgstr ""
 msgid "Notes"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:114
+#: aleksis/apps/alsijil/views.py:112
 msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:147
+#: aleksis/apps/alsijil/views.py:145
 msgid "You are not allowed to create a lesson documentation for a lesson in the future."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:658
+#: aleksis/apps/alsijil/views.py:656
 msgid "Generate full register printout for {}"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:659
+#: aleksis/apps/alsijil/views.py:657
 msgid "Generate full register printout …"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:660
+#: aleksis/apps/alsijil/views.py:658
 msgid "The printout has been generated successfully."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 msgid "The personal note has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1074
-msgid "The extra mark has been created."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1086
-msgid "The extra mark has been saved."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr ""
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 msgid "The group role has been created."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 msgid "The group role has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 msgid "The group role has been deleted."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 msgid "The group role has been assigned."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 msgid "The group role assignment has been saved."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 msgid "The group role assignment has been stopped."
 msgstr ""
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 msgid "The group role assignment has been deleted."
 msgstr ""
diff --git a/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po
index 6353e21e708c593793b756827d3f056ba5edb008..6eeacbadcf3cccf7d38c4e8e09458e8f66ba0338 100644
--- a/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po
+++ b/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-23 21:13+0200\n"
-"PO-Revision-Date: 2023-01-25 05:58+0000\n"
+"POT-Creation-Date: 2024-08-18 16:34+0200\n"
+"PO-Revision-Date: 2024-08-27 15:25+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
 "Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/uk/>\n"
 "Language: uk\n"
@@ -16,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
-"X-Generator: Weblate 4.12.1\n"
+"X-Generator: Weblate 5.0.2\n"
 
 #: aleksis/apps/alsijil/actions.py:18
 msgid "Mark as excused"
@@ -31,8 +31,8 @@ msgstr "Позначити як непояснений"
 msgid "Mark as {excuse_type.name}"
 msgstr "Позначити як {excuse_type.name}"
 
-#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:29
-#: aleksis/apps/alsijil/tables.py:53 aleksis/apps/alsijil/tables.py:78
+#: aleksis/apps/alsijil/actions.py:48 aleksis/apps/alsijil/tables.py:33
+#: aleksis/apps/alsijil/tables.py:58
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:29
 msgid "Delete"
 msgstr "Видалити"
@@ -111,7 +111,7 @@ msgid "Before"
 msgstr "Перед"
 
 #: aleksis/apps/alsijil/filters.py:14 aleksis/apps/alsijil/forms.py:371
-#: aleksis/apps/alsijil/models.py:471 aleksis/apps/alsijil/tables.py:116
+#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:96
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:126
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:223
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:189
@@ -152,20 +152,20 @@ msgid "You can't select a group and a teacher both."
 msgstr "Ви не можете обрати одночасно групу та викладача."
 
 #: aleksis/apps/alsijil/forms.py:193 aleksis/apps/alsijil/forms.py:291
-#: aleksis/apps/alsijil/models.py:609 aleksis/apps/alsijil/models.py:658
+#: aleksis/apps/alsijil/models.py:788 aleksis/apps/alsijil/models.py:879
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:27
 msgid "Person"
 msgstr "Особа"
 
 #: aleksis/apps/alsijil/forms.py:194 aleksis/apps/alsijil/forms.py:372
-#: aleksis/apps/alsijil/models.py:730
+#: aleksis/apps/alsijil/models.py:960
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:64
 msgid "Start date"
 msgstr "Дата початку"
 
 #: aleksis/apps/alsijil/forms.py:195 aleksis/apps/alsijil/forms.py:373
-#: aleksis/apps/alsijil/models.py:734
+#: aleksis/apps/alsijil/models.py:964
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:65
 msgid "End date"
 msgstr "Дата закінчення"
@@ -178,8 +178,8 @@ msgstr "Початок уроків"
 msgid "End period"
 msgstr "Закінчення уроків"
 
-#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/models.py:623
-#: aleksis/apps/alsijil/tables.py:117 aleksis/apps/alsijil/tables.py:138
+#: aleksis/apps/alsijil/forms.py:198 aleksis/apps/alsijil/tables.py:97
+#: aleksis/apps/alsijil/tables.py:118
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:50
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:392
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
@@ -188,7 +188,7 @@ msgstr "Закінчення уроків"
 msgid "Absent"
 msgstr "Відсутній(-я)"
 
-#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:146
+#: aleksis/apps/alsijil/forms.py:199 aleksis/apps/alsijil/tables.py:126
 #: aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html:54
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:135
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:30
@@ -198,8 +198,8 @@ msgstr "Відсутній(-я)"
 msgid "Excused"
 msgstr "Пояснено"
 
-#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:79
-#: aleksis/apps/alsijil/models.py:255
+#: aleksis/apps/alsijil/forms.py:201 aleksis/apps/alsijil/models.py:85
+#: aleksis/apps/alsijil/models.py:261
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:31
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:72
 msgid "Excuse type"
@@ -220,7 +220,7 @@ msgstr "Навчальний рік"
 msgid "Has lesson documentation"
 msgstr "Має учбовий матеріал"
 
-#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:130
+#: aleksis/apps/alsijil/managers.py:91 aleksis/apps/alsijil/tables.py:110
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:172
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:236
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:311
@@ -228,7 +228,7 @@ msgstr "Має учбовий матеріал"
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:21
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:375
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:455
-#: aleksis/apps/alsijil/util/alsijil_helpers.py:330
+#: aleksis/apps/alsijil/util/alsijil_helpers.py:331
 msgid "Event"
 msgstr "Подія"
 
@@ -272,41 +272,39 @@ msgstr "Може призначати роль групи для цієї гру
 msgid "Can register an absence for a person"
 msgstr "Може реєструвати відсутність особи"
 
-#: aleksis/apps/alsijil/models.py:58 aleksis/apps/alsijil/models.py:432
+#: aleksis/apps/alsijil/models.py:64 aleksis/apps/alsijil/models.py:438
 msgid "Short name"
 msgstr "Коротке ім'я"
 
-#: aleksis/apps/alsijil/models.py:59 aleksis/apps/alsijil/models.py:433
-#: aleksis/apps/alsijil/models.py:694
+#: aleksis/apps/alsijil/models.py:65 aleksis/apps/alsijil/models.py:439
+#: aleksis/apps/alsijil/models.py:924
 #: aleksis/apps/alsijil/templates/alsijil/class_register/groups.html:20
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:25
 msgid "Name"
 msgstr "Повне ім'я"
 
-#: aleksis/apps/alsijil/models.py:63 aleksis/apps/alsijil/tables.py:41
+#: aleksis/apps/alsijil/models.py:69 aleksis/apps/alsijil/tables.py:21
 msgid "Count as absent"
 msgstr "Кількість відсутніх"
 
-#: aleksis/apps/alsijil/models.py:65
+#: aleksis/apps/alsijil/models.py:71
 msgid "If checked, this excuse type will be counted as a missed lesson. If not checked,it won't show up in the absence report."
 msgstr "Якщо відмічено, цей тип пояснення буде зарахований як пропущений урок. Якщо не відмічено, то без запису у звіт відсутності."
 
-#: aleksis/apps/alsijil/models.py:80
+#: aleksis/apps/alsijil/models.py:86
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/excuse_type/list.html:9
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:30
 msgid "Excuse types"
 msgstr "Типи пояснень"
 
-#: aleksis/apps/alsijil/models.py:235 aleksis/apps/alsijil/models.py:334
+#: aleksis/apps/alsijil/models.py:241 aleksis/apps/alsijil/models.py:340
 msgid "Year"
 msgstr "Рік"
 
-#: aleksis/apps/alsijil/models.py:260 aleksis/apps/alsijil/models.py:450
-#: aleksis/apps/alsijil/tables.py:120
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:8
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:9
+#: aleksis/apps/alsijil/models.py:266 aleksis/apps/alsijil/models.py:456
+#: aleksis/apps/alsijil/tables.py:100
 #: aleksis/apps/alsijil/templates/alsijil/partials/legend.html:57
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:32
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:19
@@ -314,18 +312,18 @@ msgstr "Рік"
 msgid "Extra marks"
 msgstr "Додаткові відмітки"
 
-#: aleksis/apps/alsijil/models.py:294
+#: aleksis/apps/alsijil/models.py:300
 msgid "Personal note"
 msgstr "Особиста нотатка"
 
-#: aleksis/apps/alsijil/models.py:295
+#: aleksis/apps/alsijil/models.py:301
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:47
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:370
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:21
 msgid "Personal notes"
 msgstr "Особисті нотатки"
 
-#: aleksis/apps/alsijil/models.py:346
+#: aleksis/apps/alsijil/models.py:352
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:259
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:337
@@ -334,7 +332,7 @@ msgstr "Особисті нотатки"
 msgid "Lesson topic"
 msgstr "Тема уроку"
 
-#: aleksis/apps/alsijil/models.py:347 aleksis/apps/alsijil/models.py:483
+#: aleksis/apps/alsijil/models.py:353 aleksis/apps/alsijil/models.py:491
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:129
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:265
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:342
@@ -343,7 +341,7 @@ msgstr "Тема уроку"
 msgid "Homework"
 msgstr "Домашня робота"
 
-#: aleksis/apps/alsijil/models.py:348
+#: aleksis/apps/alsijil/models.py:354
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:130
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:271
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:346
@@ -351,355 +349,351 @@ msgstr "Домашня робота"
 msgid "Group note"
 msgstr "Групова нотатка"
 
-#: aleksis/apps/alsijil/models.py:398
+#: aleksis/apps/alsijil/models.py:404
 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html:16
 msgid "Lesson documentation"
 msgstr "Учбовий матеріал"
 
-#: aleksis/apps/alsijil/models.py:399
+#: aleksis/apps/alsijil/models.py:405
 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:43
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:77
 msgid "Lesson documentations"
 msgstr "Учбові матеріали"
 
-#: aleksis/apps/alsijil/models.py:435
+#: aleksis/apps/alsijil/models.py:441
 msgid "Foreground colour"
-msgstr ""
+msgstr "Колір переднього плану"
 
-#: aleksis/apps/alsijil/models.py:436
+#: aleksis/apps/alsijil/models.py:442
 msgid "Background colour"
-msgstr ""
+msgstr "Колір фону"
 
-#: aleksis/apps/alsijil/models.py:438
+#: aleksis/apps/alsijil/models.py:444
 msgid "Show in coursebook"
-msgstr ""
+msgstr "Показати у курсовій книзі"
 
-#: aleksis/apps/alsijil/models.py:449
+#: aleksis/apps/alsijil/models.py:455
 msgid "Extra mark"
-msgstr "Додаткова відмітка"
+msgstr "Додаткова позначка"
 
-#: aleksis/apps/alsijil/models.py:467
+#: aleksis/apps/alsijil/models.py:475
 msgid "Course"
-msgstr ""
+msgstr "Курс"
 
-#: aleksis/apps/alsijil/models.py:479 aleksis/apps/alsijil/tables.py:111
+#: aleksis/apps/alsijil/models.py:487 aleksis/apps/alsijil/tables.py:91
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:127
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:253
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:331
 msgid "Teachers"
 msgstr "Викладачі"
 
-#: aleksis/apps/alsijil/models.py:482
-#, fuzzy
-#| msgid "Lesson topic"
+#: aleksis/apps/alsijil/models.py:490
 msgid "Lesson Topic"
 msgstr "Тема уроку"
 
-#: aleksis/apps/alsijil/models.py:484
-#, fuzzy
-#| msgid "Group note"
+#: aleksis/apps/alsijil/models.py:492
 msgid "Group Note"
 msgstr "Групова нотатка"
 
-#: aleksis/apps/alsijil/models.py:512 aleksis/apps/alsijil/models.py:619
-#: aleksis/apps/alsijil/models.py:665
-#, fuzzy
-#| msgid "Lesson documentation"
+#: aleksis/apps/alsijil/models.py:496
+msgid "Participation touched at"
+msgstr "Статус участі створений о"
+
+#: aleksis/apps/alsijil/models.py:525 aleksis/apps/alsijil/models.py:798
+#: aleksis/apps/alsijil/models.py:886
 msgid "Documentation"
-msgstr "Учбовий матеріал"
+msgstr "Документація"
 
-#: aleksis/apps/alsijil/models.py:513
-#, fuzzy
-#| msgid "Lesson documentations"
+#: aleksis/apps/alsijil/models.py:526
 msgid "Documentations"
-msgstr "Учбові матеріали"
+msgstr "Документація"
 
-#: aleksis/apps/alsijil/models.py:612
-#, fuzzy
-#| msgid "Group roles"
+#: aleksis/apps/alsijil/models.py:791
 msgid "Groups of Person"
-msgstr "Ролі груп"
+msgstr "Групи осіб"
 
-#: aleksis/apps/alsijil/models.py:625
-#, fuzzy
-#| msgid "Absences"
+#: aleksis/apps/alsijil/models.py:804
 msgid "Absence Reason"
-msgstr "Відсутності"
+msgstr "Причина відсутності"
 
-#: aleksis/apps/alsijil/models.py:634
-#, fuzzy
-#| msgid "Absences"
+#: aleksis/apps/alsijil/models.py:816
 msgid "Base Absence"
-msgstr "Відсутності"
+msgstr "Основна відсутність"
+
+#: aleksis/apps/alsijil/models.py:819 aleksis/apps/alsijil/tables.py:98
+#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
+#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
+#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
+#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
+msgid "Tardiness"
+msgstr "Запізнення"
 
-#: aleksis/apps/alsijil/models.py:641 aleksis/apps/alsijil/models.py:642
+#: aleksis/apps/alsijil/models.py:862 aleksis/apps/alsijil/models.py:863
 msgid "Participation Status"
-msgstr ""
+msgstr "Стан участі"
 
-#: aleksis/apps/alsijil/models.py:670
-#, fuzzy
-#| msgid "Notes"
+#: aleksis/apps/alsijil/models.py:891
 msgid "Note"
-msgstr "Нотатки"
+msgstr "Нотатка"
 
-#: aleksis/apps/alsijil/models.py:672
-#, fuzzy
-#| msgid "Extra mark"
+#: aleksis/apps/alsijil/models.py:893
 msgid "Extra Mark"
-msgstr "Додаткова відмітка"
+msgstr "Додаткова позначка"
 
-#: aleksis/apps/alsijil/models.py:679
-#, fuzzy
-#| msgid "Personal note"
+#: aleksis/apps/alsijil/models.py:900
 msgid "Personal Note"
 msgstr "Особиста нотатка"
 
-#: aleksis/apps/alsijil/models.py:680
-#, fuzzy
-#| msgid "Personal notes"
+#: aleksis/apps/alsijil/models.py:901
 msgid "Personal Notes"
 msgstr "Особисті нотатки"
 
-#: aleksis/apps/alsijil/models.py:695
+#: aleksis/apps/alsijil/models.py:912
+msgid "A person got assigned the same extra mark multiple times per documentation."
+msgstr "Особа отримувала в документації однакову додаткову позначку неодноразово."
+
+#: aleksis/apps/alsijil/models.py:925
 msgid "Icon"
 msgstr "Піктограма"
 
-#: aleksis/apps/alsijil/models.py:696
+#: aleksis/apps/alsijil/models.py:926
 msgid "Colour"
 msgstr "Колір"
 
-#: aleksis/apps/alsijil/models.py:702 aleksis/apps/alsijil/models.py:717
+#: aleksis/apps/alsijil/models.py:932 aleksis/apps/alsijil/models.py:947
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:62
 msgid "Group role"
 msgstr "Роль групи"
 
-#: aleksis/apps/alsijil/models.py:703
+#: aleksis/apps/alsijil/models.py:933
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:90
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:8
 #: aleksis/apps/alsijil/templates/alsijil/group_role/list.html:9
 msgid "Group roles"
 msgstr "Ролі груп"
 
-#: aleksis/apps/alsijil/models.py:704
+#: aleksis/apps/alsijil/models.py:934
 msgid "Can assign group role"
 msgstr "Може призначати роль групи"
 
-#: aleksis/apps/alsijil/models.py:723
+#: aleksis/apps/alsijil/models.py:953
 msgid "Assigned person"
 msgstr "Призначена особа"
 
-#: aleksis/apps/alsijil/models.py:728 aleksis/apps/alsijil/tables.py:105
+#: aleksis/apps/alsijil/models.py:958 aleksis/apps/alsijil/tables.py:85
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:124
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:242
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:321
 msgid "Groups"
 msgstr "Групи"
 
-#: aleksis/apps/alsijil/models.py:735
+#: aleksis/apps/alsijil/models.py:965
 msgid "Can be left empty if end date is not clear yet"
 msgstr "Якщо немає точної кінцевої дати, можна залишити порожнім"
 
-#: aleksis/apps/alsijil/models.py:750
+#: aleksis/apps/alsijil/models.py:980
 msgid "Group role assignment"
 msgstr "Призначення ролі групи"
 
-#: aleksis/apps/alsijil/models.py:751
+#: aleksis/apps/alsijil/models.py:981
 msgid "Group role assignments"
 msgstr "Призначення ролі групи"
 
-#: aleksis/apps/alsijil/models.py:758
+#: aleksis/apps/alsijil/models.py:988
 msgid "Can view lesson overview"
 msgstr "Може бачити огляд уроку"
 
-#: aleksis/apps/alsijil/models.py:759
+#: aleksis/apps/alsijil/models.py:989
 msgid "Can view week overview"
 msgstr "Може бачити огляд тижня"
 
-#: aleksis/apps/alsijil/models.py:760
+#: aleksis/apps/alsijil/models.py:990
 msgid "Can view full register"
 msgstr "Може бачити весь журнал"
 
-#: aleksis/apps/alsijil/models.py:761
+#: aleksis/apps/alsijil/models.py:991
 msgid "Can register absence"
 msgstr "Може реєструвати пропуск"
 
-#: aleksis/apps/alsijil/models.py:762
+#: aleksis/apps/alsijil/models.py:992
 msgid "Can list all personal note filters"
 msgstr "Може бачити усі фільтри особистих нотаток"
 
-#: aleksis/apps/alsijil/preferences.py:9
+#: aleksis/apps/alsijil/preferences.py:16
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:16
 msgid "Class register"
 msgstr "Класний журнал"
 
-#: aleksis/apps/alsijil/preferences.py:17
+#: aleksis/apps/alsijil/preferences.py:24
 msgid "Block adding personal notes for cancelled lessons"
 msgstr "Блокувати додавання особистих нотаток до скасованих уроків"
 
-#: aleksis/apps/alsijil/preferences.py:25
+#: aleksis/apps/alsijil/preferences.py:32
 msgid "Allow users to view their own personal notes"
 msgstr "Дозволити користувачам переглядати власні особисті нотатки"
 
-#: aleksis/apps/alsijil/preferences.py:34
+#: aleksis/apps/alsijil/preferences.py:41
 msgid "Allow primary group owners to register future absences for students in their groups"
 msgstr "Дозволити власникам основних груп реєструвати майбутні пропуски студентів у своїх групах"
 
-#: aleksis/apps/alsijil/preferences.py:44
+#: aleksis/apps/alsijil/preferences.py:51
 msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
 msgstr "Надати власнику батьківської групи такі самі повноваження, як і власникам відповідних підлеглих груп"
 
-#: aleksis/apps/alsijil/preferences.py:54
+#: aleksis/apps/alsijil/preferences.py:61
 msgid "Allow original teachers to edit their lessons although they are substituted"
 msgstr "Дозволити початковим викладачам редагувати свої уроки навіть після їх заміни"
 
-#: aleksis/apps/alsijil/preferences.py:63
+#: aleksis/apps/alsijil/preferences.py:70
 msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
 msgstr "Переносити дані з першого уроку в розкладі на поточні уроки через декілька уроків"
 
-#: aleksis/apps/alsijil/preferences.py:66
+#: aleksis/apps/alsijil/preferences.py:73
 msgid "This will carry over data only if the data in the following periods are empty."
 msgstr "Це перенесе дані лише в тому разі, коли в поточних уроках даних немає."
 
-#: aleksis/apps/alsijil/preferences.py:75
+#: aleksis/apps/alsijil/preferences.py:82
 msgid "Allow carrying over data from any lesson period to all other lesson                 periods with the same lesson and in the same week"
 msgstr "Дозволити переносити дані з будь-якого уроку на усі інші                 уроки з таким самим номером урока на тому самому тижні"
 
-#: aleksis/apps/alsijil/preferences.py:79
+#: aleksis/apps/alsijil/preferences.py:86
 msgid "This will carry over data only if the data in the aforementioned periods are empty."
 msgstr "Це перенесе дані лише в тому разі, коли у вищезгаданих уроках даних немає."
 
-#: aleksis/apps/alsijil/preferences.py:88
+#: aleksis/apps/alsijil/preferences.py:95
 msgid "Carry over personal notes to all following lesson periods on the same day."
 msgstr "Переносити особисті нотатки до всіх наступних уроків того ж дня."
 
-#: aleksis/apps/alsijil/preferences.py:97
+#: aleksis/apps/alsijil/preferences.py:104
 msgid "Allow teachers to open lesson periods on the same day and not just at the beginning of the period"
 msgstr "Дозволити викладачам відкривати уроки того самого дня і не лише на початку уроків"
 
-#: aleksis/apps/alsijil/preferences.py:101
+#: aleksis/apps/alsijil/preferences.py:108
 msgid "Lessons in the past are not affected by this setting, you can open them whenever you want."
 msgstr "Ці налаштування не впливають на минулі уроки. Ви можете відкривати їх будь-коли."
 
-#: aleksis/apps/alsijil/preferences.py:110
+#: aleksis/apps/alsijil/preferences.py:117
 msgid "Allow teachers to add data for lessons in holidays"
 msgstr "Дозволити викладачам додавати навчальний матеріал (дані для уроків) на вихідних"
 
-#: aleksis/apps/alsijil/preferences.py:119
+#: aleksis/apps/alsijil/preferences.py:126
 msgid "Allow group owners to assign group roles to the parents of the group's members"
 msgstr "Дозволити власникам груп призначати ролі груп батькам учасників груп"
 
-#: aleksis/apps/alsijil/preferences.py:128
+#: aleksis/apps/alsijil/preferences.py:135
 msgid "Show assigned group roles in week view"
 msgstr "Показати призначені ролі груп у тижневому огляді"
 
-#: aleksis/apps/alsijil/preferences.py:129
+#: aleksis/apps/alsijil/preferences.py:136
 msgid "Only week view of groups"
 msgstr "Лише тижневі огляди груп"
 
-#: aleksis/apps/alsijil/preferences.py:137
+#: aleksis/apps/alsijil/preferences.py:144
 msgid "Show assigned group roles in lesson view"
 msgstr "Показати призначені ролі груп в огляді уроку"
 
-#: aleksis/apps/alsijil/preferences.py:147
+#: aleksis/apps/alsijil/preferences.py:154
 msgid "Items per page in lessons table"
 msgstr "Кількість записів у розкладі на сторінку"
 
-#: aleksis/apps/alsijil/preferences.py:151
+#: aleksis/apps/alsijil/preferences.py:158
 msgid "Each page must show at least one item."
 msgstr "Кожна сторінка повинна мати принаймні один запис."
 
-#: aleksis/apps/alsijil/preferences.py:159
+#: aleksis/apps/alsijil/preferences.py:166
 msgid "Filter lessons by existence of their lesson documentation on default"
 msgstr "Типово фільтувати уроки за наявністю у них учбового матеріалу"
 
-#: aleksis/apps/alsijil/preferences.py:170
+#: aleksis/apps/alsijil/preferences.py:177
 msgid "Allow editing of all future documentations"
-msgstr ""
+msgstr "Дозволити редагувати усю майбутню документацію"
 
-#: aleksis/apps/alsijil/preferences.py:173
+#: aleksis/apps/alsijil/preferences.py:180
 msgid "Allow editing of all documentations up to and including those on the current day"
-msgstr ""
+msgstr "Дозволити редагувати усю попередню документацію, сьогоднішній день включно"
 
-#: aleksis/apps/alsijil/preferences.py:178
+#: aleksis/apps/alsijil/preferences.py:185
 msgid "Allow editing of all documentations up to and including those on the current date and time"
-msgstr ""
+msgstr "Дозволити редагувати усю попередню документацію, по теперішній час включно"
 
-#: aleksis/apps/alsijil/preferences.py:183
+#: aleksis/apps/alsijil/preferences.py:190
 msgid "Set time range for which documentations may be edited"
-msgstr ""
+msgstr "Встановити інтервал часу, за який можна змінювати документацію"
 
-#: aleksis/apps/alsijil/tables.py:23 aleksis/apps/alsijil/tables.py:47
-#: aleksis/apps/alsijil/tables.py:72
+#: aleksis/apps/alsijil/preferences.py:201
+msgid "User is allowed to register absences for members of groups the user is an owner of with these group types"
+msgstr "Користувачу дозволено реєструвати відсутність для учасників в групах вказаних типів, де він/вона є власником"
+
+#: aleksis/apps/alsijil/preferences.py:205
+msgid "If you leave it empty, all member of groups the user is an owner of will be shown."
+msgstr "Якщо залишити незаповненим, будуть показані усі учасники усіх груп, де користувач є власником."
+
+#: aleksis/apps/alsijil/preferences.py:217
+msgid "Group type of groups to be shown first in the group select field on the coursebook overview page"
+msgstr "Тип груп, який слід показувати першим у полі вибору груп на сторінці огляду курсової книги"
+
+#: aleksis/apps/alsijil/preferences.py:220
+msgid "If you leave it empty, no group type will be used."
+msgstr "Якщо залишити порожнім, жодний тип групи не буде використаний."
+
+#: aleksis/apps/alsijil/tables.py:27 aleksis/apps/alsijil/tables.py:52
 #: aleksis/apps/alsijil/templates/alsijil/group_role/partials/assignment_options.html:13
 msgid "Edit"
 msgstr "Редагувати"
 
-#: aleksis/apps/alsijil/tables.py:96
+#: aleksis/apps/alsijil/tables.py:76
 #: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:348
 msgid "Date"
 msgstr "Дата"
 
-#: aleksis/apps/alsijil/tables.py:99
+#: aleksis/apps/alsijil/tables.py:79
 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:30
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:122
 msgid "Period"
 msgstr "Урок"
 
-#: aleksis/apps/alsijil/tables.py:118
-#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:161
-#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:29
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:21
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:46
-#: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:123
-#: aleksis/apps/alsijil/templates/alsijil/print/full_register.html:323
-msgid "Tardiness"
-msgstr "Запізнення"
-
-#: aleksis/apps/alsijil/tables.py:119
+#: aleksis/apps/alsijil/tables.py:99
 msgid "Excuse"
 msgstr "Пояснення"
 
-#: aleksis/apps/alsijil/tables.py:156
-#, fuzzy, python-brace-format
-#| msgid "{value}' late"
+#: aleksis/apps/alsijil/tables.py:136
+#, python-brace-format
 msgid "{value}' tardiness"
-msgstr "{value}' затримка"
+msgstr "{value}' запізнення"
 
 #: aleksis/apps/alsijil/tasks.py:26
 msgid "Load data ..."
-msgstr ""
+msgstr "Завантаження даних ..."
 
 #: aleksis/apps/alsijil/tasks.py:49
-#, fuzzy
-#| msgid "Start date"
 msgid "Sort data ..."
-msgstr "Дата початку"
+msgstr "Сортування даних ..."
 
 #: aleksis/apps/alsijil/tasks.py:64
 msgid "Load lesson data ..."
-msgstr ""
+msgstr "Завантаження даних уроку ..."
 
 #: aleksis/apps/alsijil/tasks.py:104
 msgid "Sort lesson data ..."
-msgstr ""
+msgstr "Сортування даних уроку ..."
 
 #: aleksis/apps/alsijil/tasks.py:133
-#, fuzzy
-#| msgid "Statistics"
 msgid "Load statistics ..."
-msgstr "Статистика"
+msgstr "Завантаження статистики ..."
 
 #: aleksis/apps/alsijil/tasks.py:173
 msgid "Generate template ..."
-msgstr ""
+msgstr "Створення шаблону ..."
 
 #: aleksis/apps/alsijil/tasks.py:179
 msgid "Generate PDF ..."
-msgstr ""
+msgstr "Створення PDF ..."
 
 #: aleksis/apps/alsijil/tasks.py:185
 msgid "PDF generation failed"
-msgstr ""
+msgstr "Створити PDF не вдалося"
 
 #: aleksis/apps/alsijil/templates/alsijil/absences/register.html:5
 #: aleksis/apps/alsijil/templates/alsijil/absences/register.html:6
@@ -967,7 +961,7 @@ msgstr "Перегляд відвідування уроку"
 
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:393
 msgid "unexcused"
-msgstr "непояснене"
+msgstr "необґрунтоване"
 
 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:396
 msgid "Summed up tardiness"
@@ -1016,17 +1010,6 @@ msgstr ""
 "    Не користуйтеся цим для створення типового пояснення або якщо не розділяєте на типи пояснень.\n"
 "  "
 
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html:7
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html:14
-msgid "Create extra mark"
-msgstr "Створити додаткову позначку"
-
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:6
-#: aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html:7
-msgid "Edit extra mark"
-msgstr "Редагувати додаткову позначку"
-
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:9
 #: aleksis/apps/alsijil/templates/alsijil/group_role/assign.html:16
 #, python-format
@@ -1356,7 +1339,7 @@ msgstr "Немає доступних студентів."
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:13
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:26
 msgid "Primary group"
-msgstr "Основна група"
+msgstr "Первинна група"
 
 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:16
 msgid "Uncounted Absences"
@@ -1559,108 +1542,105 @@ msgstr "Тиждень"
 msgid "Notes"
 msgstr "Нотатки"
 
-#: aleksis/apps/alsijil/views.py:114
+#: aleksis/apps/alsijil/views.py:112
 msgid "You either selected an invalid lesson or there is currently no lesson in progress."
 msgstr "Або Ви обрали неправильний урок, або зараз уроку немає."
 
-#: aleksis/apps/alsijil/views.py:147
+#: aleksis/apps/alsijil/views.py:145
 msgid "You are not allowed to create a lesson documentation for a lesson in the future."
 msgstr "Вам не дозволено створювати учбові матеріали для уроку у майбутньому."
 
-#: aleksis/apps/alsijil/views.py:264
+#: aleksis/apps/alsijil/views.py:262
 msgid "The lesson documentation has been saved."
 msgstr "Учбовий матеріал збережений."
 
-#: aleksis/apps/alsijil/views.py:298
+#: aleksis/apps/alsijil/views.py:296
 msgid "The personal notes have been saved."
 msgstr "Особисті нотатки збережені."
 
-#: aleksis/apps/alsijil/views.py:658
-#, fuzzy
-#| msgid "Generate printout"
+#: aleksis/apps/alsijil/views.py:656
 msgid "Generate full register printout for {}"
-msgstr "Підготувати для друку"
+msgstr "Підготувати до друку класний журнал для {}"
 
-#: aleksis/apps/alsijil/views.py:659
-#, fuzzy
-#| msgid "Generate printout"
+#: aleksis/apps/alsijil/views.py:657
 msgid "Generate full register printout …"
-msgstr "Підготувати для друку"
+msgstr "Готується до друку класний журнал …"
 
-#: aleksis/apps/alsijil/views.py:660
-#, fuzzy
-#| msgid "The personal note has been deleted."
+#: aleksis/apps/alsijil/views.py:658
 msgid "The printout has been generated successfully."
-msgstr "Особиста нотатка видалена."
+msgstr "Друкована форма успішно створена."
 
-#: aleksis/apps/alsijil/views.py:661
+#: aleksis/apps/alsijil/views.py:659
 msgid "There was a problem while generating the printout."
-msgstr ""
+msgstr "Під час створення друкованої форми виникла помилка."
 
-#: aleksis/apps/alsijil/views.py:664
+#: aleksis/apps/alsijil/views.py:662
 msgid "Download PDF"
-msgstr ""
+msgstr "Звантажити PDF"
 
-#: aleksis/apps/alsijil/views.py:1030
+#: aleksis/apps/alsijil/views.py:1028
 msgid "The absence has been saved."
 msgstr "Пропуск збережений."
 
-#: aleksis/apps/alsijil/views.py:1051
+#: aleksis/apps/alsijil/views.py:1049
 msgid "The personal note has been deleted."
 msgstr "Особиста нотатка видалена."
 
-#: aleksis/apps/alsijil/views.py:1074
-msgid "The extra mark has been created."
-msgstr "Додаткова позначка сторена."
-
-#: aleksis/apps/alsijil/views.py:1086
-msgid "The extra mark has been saved."
-msgstr "Додаткова позначка збережена."
-
-#: aleksis/apps/alsijil/views.py:1097
-msgid "The extra mark has been deleted."
-msgstr "Додаткова позначка видалена."
-
-#: aleksis/apps/alsijil/views.py:1119
+#: aleksis/apps/alsijil/views.py:1072
 msgid "The excuse type has been created."
 msgstr "Тип пояснення створений."
 
-#: aleksis/apps/alsijil/views.py:1131
+#: aleksis/apps/alsijil/views.py:1084
 msgid "The excuse type has been saved."
 msgstr "Тип пояснення збережений."
 
-#: aleksis/apps/alsijil/views.py:1142
+#: aleksis/apps/alsijil/views.py:1095
 msgid "The excuse type has been deleted."
 msgstr "Тип пояснення видалений."
 
-#: aleksis/apps/alsijil/views.py:1164
+#: aleksis/apps/alsijil/views.py:1117
 msgid "The group role has been created."
 msgstr "Роль групи створена."
 
-#: aleksis/apps/alsijil/views.py:1176
+#: aleksis/apps/alsijil/views.py:1129
 msgid "The group role has been saved."
 msgstr "Роль групи збережена."
 
-#: aleksis/apps/alsijil/views.py:1187
+#: aleksis/apps/alsijil/views.py:1140
 msgid "The group role has been deleted."
 msgstr "Роль групи видалена."
 
-#: aleksis/apps/alsijil/views.py:1221 aleksis/apps/alsijil/views.py:1253
+#: aleksis/apps/alsijil/views.py:1174 aleksis/apps/alsijil/views.py:1206
 msgid "The group role has been assigned."
 msgstr "Роль групи призначена."
 
-#: aleksis/apps/alsijil/views.py:1272
+#: aleksis/apps/alsijil/views.py:1225
 msgid "The group role assignment has been saved."
 msgstr "Призначення ролі групи збережене."
 
-#: aleksis/apps/alsijil/views.py:1293
+#: aleksis/apps/alsijil/views.py:1246
 msgid "The group role assignment has been stopped."
 msgstr "Призначення ролі групи зупинене."
 
-#: aleksis/apps/alsijil/views.py:1306
+#: aleksis/apps/alsijil/views.py:1259
 msgid "The group role assignment has been deleted."
 msgstr "Призначення ролі групи видалене."
 
+#~ msgid "Create extra mark"
+#~ msgstr "Створити додаткову позначку"
+
+#~ msgid "Edit extra mark"
+#~ msgstr "Редагувати додаткову позначку"
+
+#~ msgid "The extra mark has been created."
+#~ msgstr "Додаткова позначка сторена."
+
+#~ msgid "The extra mark has been saved."
+#~ msgstr "Додаткова позначка збережена."
+
+#~ msgid "The extra mark has been deleted."
+#~ msgstr "Додаткова позначка видалена."
+
 #~ msgid "Current lesson"
 #~ msgstr "Поточний урок"
 
diff --git a/aleksis/apps/alsijil/managers.py b/aleksis/apps/alsijil/managers.py
index 7d0130805275359672d1337133eacd1b0a2b62ad..dc29fae25c189c7c2250279a6e8d86206f52e520 100644
--- a/aleksis/apps/alsijil/managers.py
+++ b/aleksis/apps/alsijil/managers.py
@@ -11,7 +11,7 @@ from django.utils.translation import gettext as _
 from calendarweek import CalendarWeek
 
 from aleksis.apps.chronos.managers import DateRangeQuerySetMixin
-from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations, PolymorphicBaseManager
+from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations, RecurrencePolymorphicManager
 
 if TYPE_CHECKING:
     from aleksis.core.models import Group
@@ -189,7 +189,7 @@ class GroupRoleAssignmentQuerySet(DateRangeQuerySetMixin, QuerySet):
         return self.filter(Q(groups=group) | Q(groups__child_groups=group))
 
 
-class DocumentationManager(PolymorphicBaseManager):
+class DocumentationManager(RecurrencePolymorphicManager):
     """Manager adding specific methods to documentations."""
 
     def get_queryset(self):
@@ -205,9 +205,7 @@ class DocumentationManager(PolymorphicBaseManager):
         )
 
 
-class ParticipationStatusManager(PolymorphicBaseManager):
+class ParticipationStatusManager(RecurrencePolymorphicManager):
     """Manager adding specific methods to participation statuses."""
 
-    def get_queryset(self):
-        """Ensure often used related data are loaded as well."""
-        return super().get_queryset().select_related("person", "absence_reason", "base_absence")
+    pass
diff --git a/aleksis/apps/alsijil/migrations/0023_add_tardiness_and_rework_constraints.py b/aleksis/apps/alsijil/migrations/0023_add_tardiness_and_rework_constraints.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ca24f5865ddf5d3fb02acbd0aa7522bbc8bda13
--- /dev/null
+++ b/aleksis/apps/alsijil/migrations/0023_add_tardiness_and_rework_constraints.py
@@ -0,0 +1,67 @@
+# Generated by Django 4.2.10 on 2024-07-01 13:44
+# Updated for more custom logic
+
+from django.db import migrations, models
+
+
+def forwards__unique_extra_mark_documentation(apps, schema_editor):
+    NewPersonalNote = apps.get_model("alsijil", "NewPersonalNote")  # noqa
+    db_alias = schema_editor.connection.alias
+
+    duplicates = (NewPersonalNote.objects.using(db_alias)
+                  .values("documentation", "extra_mark", "person")
+                  .annotate(count=models.Count("id"))
+                  .filter(count__gt=1, extra_mark__isnull=False))
+
+    # Iterate over duplicates and delete the extra instances
+    for duplicate in duplicates:
+        pks = (NewPersonalNote
+               .objects
+               .using(db_alias)
+               .filter(person=duplicate["person"], documentation=duplicate["documentation"], extra_mark=duplicate["extra_mark"])
+               .values_list("pk", flat=True)
+               )[1:]
+        NewPersonalNote.objects.using(db_alias).filter(pk__in=pks).delete()
+
+
+def reverse__unique_extra_mark_documentation(apps, schema_editor):
+    # Nothing to do, we cannot bring back the deleted objects, but they were duplicate data, so they are not needed anyway.
+    pass
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('alsijil', '0022_documentation_participation_touched_at'),
+    ]
+
+    operations = [
+        migrations.RemoveConstraint(
+            model_name='newpersonalnote',
+            name='unique_absence_per_documentation',
+        ),
+        migrations.AddField(
+            model_name='participationstatus',
+            name='tardiness',
+            field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Tardiness'),
+        ),
+        migrations.AlterField(
+            model_name='newpersonalnote',
+            name='note',
+            field=models.TextField(blank=True, default='', verbose_name='Note'),
+        ),
+        migrations.AddConstraint(
+            model_name='newpersonalnote',
+            constraint=models.CheckConstraint(
+                check=models.Q(models.Q(('note', ''), _negated=True), ('extra_mark__isnull', False), _connector='OR'),
+                name='either_note_or_extra_mark_per_note'),
+        ),
+        migrations.RunPython(forwards__unique_extra_mark_documentation, reverse__unique_extra_mark_documentation),
+        migrations.AddConstraint(
+            model_name='newpersonalnote',
+            constraint=models.UniqueConstraint(
+                condition=models.Q(('extra_mark', None), _negated=True),
+                fields=('person', 'documentation', 'extra_mark'),
+                name='unique_person_documentation_extra_mark',
+                violation_error_message='A person got assigned the same extra mark multiple times per documentation.'),
+        ),
+    ]
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 9e68ccd06773dc8b0f6ee65ccd16c81319b94a8b..5303a40e57e3bf7ac6bbeba98da2f6e206be6007 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -8,6 +8,7 @@ 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
 from django.utils import timezone
 from django.utils.formats import date_format
@@ -529,8 +530,12 @@ class Documentation(CalendarEvent):
     @classmethod
     def get_documentations_for_events(
         cls,
+        datetime_start: datetime,
+        datetime_end: datetime,
         events: list,
         incomplete: Optional[bool] = False,
+        absences_exist: Optional[bool] = False,
+        request: Optional[HttpRequest] = None,
     ) -> tuple:
         """Get all the documentations for the events.
         Create dummy documentations if none exist.
@@ -538,22 +543,51 @@ class Documentation(CalendarEvent):
         """
         docs = []
         dummies = []
+
+        # Prefetch existing documentations to speed things up
+        existing_documentations = Documentation.objects.filter(
+            datetime_start__lte=datetime_end,
+            datetime_end__gte=datetime_start,
+            amends__in=[e["REFERENCE_OBJECT"] for e in events],
+        ).prefetch_related("participations")
+
         for event in events:
             if incomplete and event["STATUS"] == "CANCELLED":
                 continue
 
             event_reference_obj = event["REFERENCE_OBJECT"]
-            existing_documentations = event_reference_obj.amended_by.filter(
-                datetime_start=event["DTSTART"].dt,
-                datetime_end=event["DTEND"].dt,
+            existing_documentations_event = filter(
+                lambda d: (
+                    d.datetime_start == event["DTSTART"].dt
+                    and d.datetime_end == event["DTEND"].dt
+                    and d.amends.id == event_reference_obj.id
+                ),
+                existing_documentations,
             )
 
-            if existing_documentations.exists():
-                doc = existing_documentations.first()
-                if incomplete and doc.topic:
+            doc = next(existing_documentations_event, None)
+            if doc:
+                if (
+                    (incomplete and doc.topic)
+                    or (
+                        not request.user.has_perm(
+                            "alsijil.edit_participation_status_for_documentation_rule", doc
+                        )
+                        and not doc.participations.filter(
+                            person__pk=request.user.person.pk, absence_reason__isnull=False
+                        ).exists()
+                    )
+                    or (
+                        absences_exist
+                        and (
+                            not doc.participations.all()
+                            or not [d for d in doc.participations.all() if d.absence_reason]
+                        )
+                    )
+                ):
                     continue
                 docs.append(doc)
-            else:
+            elif not absences_exist:
                 if event_reference_obj.amends:
                     if event_reference_obj.course:
                         course = event_reference_obj.course
@@ -578,7 +612,7 @@ class Documentation(CalendarEvent):
                     )
                 )
 
-        return (docs, dummies)
+        return docs, dummies
 
     @classmethod
     def get_documentations_for_person(
@@ -587,6 +621,7 @@ class Documentation(CalendarEvent):
         start: datetime,
         end: datetime,
         incomplete: Optional[bool] = False,
+        request: Optional[HttpRequest] = None,
     ) -> tuple:
         """Get all the documentations for the person from start to end datetime.
         Create dummy documentations if none exist.
@@ -594,7 +629,7 @@ class Documentation(CalendarEvent):
         """
         event_params = {
             "type": "PARTICIPANT",
-            "obj_id": person,
+            "id": person,
         }
 
         events = LessonEvent.get_single_events(
@@ -605,7 +640,7 @@ class Documentation(CalendarEvent):
             with_reference_object=True,
         )
 
-        return Documentation.get_documentations_for_events(events, incomplete)
+        return Documentation.get_documentations_for_events(start, end, events, incomplete, request)
 
     @classmethod
     def parse_dummy(
@@ -687,7 +722,10 @@ class Documentation(CalendarEvent):
                 *cls.parse_dummy(_id),
             ), True
 
-        return cls.objects.get(id=_id), False
+        obj = cls.objects.get(id=_id)
+        if not user.has_perm("alsijil.edit_documentation_rule", obj):
+            raise PermissionDenied()
+        return obj, False
 
     def touch(self):
         """Ensure that participation statuses are created for this documentation."""
@@ -695,6 +733,7 @@ class Documentation(CalendarEvent):
             self.participation_touched_at
             or not self.amends
             or self.value_start_datetime(self) > now()
+            or self.amends.cancelled
         ):
             # There is no source to update from or it's too early
             return
@@ -790,6 +829,40 @@ class ParticipationStatus(CalendarEvent):
         verbose_name=_("Base Absence"),
     )
 
+    tardiness = models.PositiveSmallIntegerField(blank=True, null=True, verbose_name=_("Tardiness"))
+
+    @classmethod
+    def get_objects(
+        cls, request: HttpRequest | None = None, params: dict[str, any] | None = None, **kwargs
+    ) -> QuerySet:
+        qs = (
+            super()
+            .get_objects(request, params, **kwargs)
+            .select_related("person", "absence_reason")
+        )
+        if params:
+            if params.get("person"):
+                qs = qs.filter(person=params["person"])
+            elif params.get("persons"):
+                qs = qs.filter(person__in=params["persons"])
+            elif params.get("group"):
+                qs = qs.filter(groups_of_person__in=params.get("group"))
+        return qs
+
+    @classmethod
+    def value_title(
+        cls, reference_object: "ParticipationStatus", request: HttpRequest | None = None
+    ) -> str:
+        """Return the title of the calendar event."""
+        return f"{reference_object.person} ({reference_object.absence_reason})"
+
+    @classmethod
+    def value_description(
+        cls, reference_object: "ParticipationStatus", request: HttpRequest | None = None
+    ) -> str:
+        """Return the title of the calendar event."""
+        return ""
+
     def fill_from_kolego(self, kolego_absence: KolegoAbsence):
         """Take over data from a Kolego absence."""
         self.base_absence = kolego_absence
@@ -828,7 +901,7 @@ class NewPersonalNote(ExtensibleModel):
         null=True,
     )
 
-    note = models.TextField(blank=True, verbose_name=_("Note"))
+    note = models.TextField(blank=True, default="", verbose_name=_("Note"))
     extra_mark = models.ForeignKey(
         ExtraMark, on_delete=models.PROTECT, blank=True, null=True, verbose_name=_("Extra Mark")
     )
@@ -840,9 +913,18 @@ class NewPersonalNote(ExtensibleModel):
         verbose_name = _("Personal Note")
         verbose_name_plural = _("Personal Notes")
         constraints = [
+            # This constraint could be dropped in future scenarios
             models.CheckConstraint(
                 check=~Q(note="") | Q(extra_mark__isnull=False),
-                name="unique_absence_per_documentation",
+                name="either_note_or_extra_mark_per_note",
+            ),
+            models.UniqueConstraint(
+                fields=["person", "documentation", "extra_mark"],
+                name="unique_person_documentation_extra_mark",
+                violation_error_message=_(
+                    "A person got assigned the same extra mark multiple times per documentation."
+                ),
+                condition=~Q(extra_mark=None),
             ),
         ]
 
diff --git a/aleksis/apps/alsijil/preferences.py b/aleksis/apps/alsijil/preferences.py
index b00d9277e507e284130340b84cbbca8c07597d1f..74ed22802286eaeccc4d92b6c1e255364c3503b2 100644
--- a/aleksis/apps/alsijil/preferences.py
+++ b/aleksis/apps/alsijil/preferences.py
@@ -2,8 +2,15 @@ from django.core.exceptions import ValidationError
 from django.utils.translation import gettext_lazy as _
 
 from dynamic_preferences.preferences import Section
-from dynamic_preferences.types import BooleanPreference, ChoicePreference, IntegerPreference
-
+from dynamic_preferences.types import (
+    BooleanPreference,
+    ChoicePreference,
+    IntegerPreference,
+    ModelChoicePreference,
+    ModelMultipleChoicePreference,
+)
+
+from aleksis.core.models import GroupType
 from aleksis.core.registries import person_preferences_registry, site_preferences_registry
 
 alsijil = Section("alsijil", verbose_name=_("Class register"))
@@ -181,3 +188,36 @@ class AllowEditFutureDocumentations(ChoicePreference):
         ),
     )
     verbose_name = _("Set time range for which documentations may be edited")
+
+
+@site_preferences_registry.register
+class GroupTypesRegisterAbsence(ModelMultipleChoicePreference):
+    section = alsijil
+    name = "group_types_register_absence"
+    required = False
+    default = []
+    model = GroupType
+    verbose_name = _(
+        "User is allowed to register absences for members "
+        "of groups the user is an owner of with these group types"
+    )
+    help_text = _(
+        "If you leave it empty, all member of groups the user is an owner of will be shown."
+    )
+
+
+@site_preferences_registry.register
+class GroupTypePriorityCoursebook(ModelChoicePreference):
+    section = alsijil
+    name = "group_type_priority_coursebook"
+    required = False
+    default = None
+    model = GroupType
+    verbose_name = _(
+        "Group type of groups to be shown first in the group "
+        "select field on the coursebook overview page"
+    )
+    help_text = _("If you leave it empty, no group type will be used.")
+
+    def get_queryset(self):
+        return GroupType.objects.managed_and_unmanaged()
diff --git a/aleksis/apps/alsijil/rules.py b/aleksis/apps/alsijil/rules.py
index 9045598fdf362f0bedb57424fd73bb450d80d669..171a0765737f8b68164af78b3823c96481eef044 100644
--- a/aleksis/apps/alsijil/rules.py
+++ b/aleksis/apps/alsijil/rules.py
@@ -13,9 +13,13 @@ from aleksis.core.util.predicates import (
 from .util.predicates import (
     can_edit_documentation,
     can_edit_participation_status,
+    can_edit_personal_note,
+    can_register_absence_for_at_least_one_group,
+    can_register_absence_for_person,
     can_view_any_documentation,
     can_view_documentation,
     can_view_participation_status,
+    can_view_personal_note,
     has_lesson_group_object_perm,
     has_person_group_object_perm,
     has_personal_note_group_perm,
@@ -169,19 +173,16 @@ add_perm("alsijil.view_week_personalnote_rule", view_week_personal_notes_predica
 
 # Register absence
 view_register_absence_predicate = has_person & (
-    (
-        is_person_group_owner
-        & is_site_preference_set("alsijil", "register_absence_as_primary_group_owner")
-    )
-    | has_global_perm("alsijil.register_absence")
+    can_register_absence_for_at_least_one_group | has_global_perm("alsijil.register_absence")
 )
+add_perm("alsijil.view_register_absence_rule", view_register_absence_predicate)
 
 register_absence_predicate = has_person & (
-    view_register_absence_predicate
+    can_register_absence_for_person
+    | has_global_perm("alsijil.register_absence")
     | has_object_perm("core.register_absence_person")
     | has_person_group_object_perm("core.register_absence_group")
 )
-add_perm("alsijil.view_register_absence_rule", view_register_absence_predicate)
 add_perm("alsijil.register_absence_rule", register_absence_predicate)
 
 # View full register for group
@@ -281,6 +282,10 @@ add_perm("alsijil.delete_excusetype_rule", delete_excusetype_predicate)
 view_extramarks_predicate = has_person & has_global_perm("alsijil.view_extramark")
 add_perm("alsijil.view_extramarks_rule", view_extramarks_predicate)
 
+# Fetch all extra marks
+fetch_extramarks_predicate = has_person
+add_perm("alsijil.fetch_extramarks_rule", fetch_extramarks_predicate)
+
 # Add extra mark
 add_extramark_predicate = view_extramarks_predicate & has_global_perm("alsijil.add_extramark")
 add_perm("alsijil.add_extramark_rule", add_extramark_predicate)
@@ -426,12 +431,38 @@ add_perm(
     view_participation_status_for_documentation_predicate,
 )
 
-edit_participation_status_for_documentation_predicate = (
+edit_participation_status_for_documentation_with_time_range_predicate = (
     has_person
     & (has_global_perm("alsijil.change_participationstatus") | can_edit_participation_status)
     & is_in_allowed_time_range_for_participation_status
 )
+add_perm(
+    "alsijil.edit_participation_status_for_documentation_with_time_range_rule",
+    edit_participation_status_for_documentation_with_time_range_predicate,
+)
+
+edit_participation_status_for_documentation_predicate = has_person & (
+    has_global_perm("alsijil.change_participationstatus") | can_edit_participation_status
+)
 add_perm(
     "alsijil.edit_participation_status_for_documentation_rule",
     edit_participation_status_for_documentation_predicate,
 )
+
+view_personal_note_predicate = has_person & (
+    has_global_perm("alsijil.change_newpersonalnote") | can_view_personal_note
+)
+add_perm(
+    "alsijil.view_personal_note_rule",
+    view_personal_note_predicate,
+)
+
+edit_personal_note_predicate = (
+    has_person
+    & (has_global_perm("alsijil.change_newpersonalnote") | can_edit_personal_note)
+    & is_in_allowed_time_range
+)
+add_perm(
+    "alsijil.edit_personal_note_rule",
+    edit_personal_note_predicate,
+)
diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py
index 35cee89b0621978d03461d6a92f686924cb69712..d17fe665c35fd074d5c1f369f1942e5e8604408d 100644
--- a/aleksis/apps/alsijil/schema/__init__.py
+++ b/aleksis/apps/alsijil/schema/__init__.py
@@ -1,22 +1,25 @@
 from datetime import datetime
 
 from django.core.exceptions import PermissionDenied
-from django.db.models.query_utils import Q
+from django.db.models import BooleanField, ExpressionWrapper, Q
 
 import graphene
 
 from aleksis.apps.chronos.models import LessonEvent
 from aleksis.apps.cursus.models import Course
 from aleksis.apps.cursus.schema import CourseType
+from aleksis.apps.kolego.models import AbsenceReason
+from aleksis.apps.kolego.schema.absence import AbsenceReasonType
 from aleksis.core.models import Group, Person, SchoolTerm
 from aleksis.core.schema.base import FilterOrderList
 from aleksis.core.schema.group import GroupType
-from aleksis.core.util.core_helpers import has_person
+from aleksis.core.schema.person import PersonType
+from aleksis.core.util.core_helpers import get_site_preferences, has_person
 
 from ..model_extensions import annotate_person_statistics_for_school_term
 from ..models import Documentation
 from .absences import (
-    AbsencesBatchCreateMutation,
+    AbsencesForPersonsCreateMutation,
 )
 from .documentation import (
     DocumentationBatchCreateOrUpdateMutation,
@@ -24,6 +27,21 @@ from .documentation import (
     LessonsForPersonType,
     TouchDocumentationMutation,
 )
+from .extra_marks import (
+    ExtraMarkBatchCreateMutation,
+    ExtraMarkBatchDeleteMutation,
+    ExtraMarkBatchPatchMutation,
+    ExtraMarkType,
+)
+from .participation_status import (
+    ExtendParticipationStatusToAbsenceBatchMutation,
+    ParticipationStatusBatchPatchMutation,
+)
+from .personal_note import (
+    PersonalNoteBatchCreateMutation,
+    PersonalNoteBatchDeleteMutation,
+    PersonalNoteBatchPatchMutation,
+)
 from .participation_status import ParticipationStatusBatchPatchMutation
 from .statistics import (
     DocumentationByPersonType,
@@ -44,18 +62,24 @@ class Query(graphene.ObjectType):
         date_start=graphene.Date(required=True),
         date_end=graphene.Date(required=True),
         incomplete=graphene.Boolean(required=False),
+        absences_exist=graphene.Boolean(required=False),
     )
 
     groups_by_person = FilterOrderList(GroupType, person=graphene.ID())
     courses_of_person = FilterOrderList(CourseType, person=graphene.ID())
 
+    absence_creation_persons = graphene.List(PersonType)
     lessons_for_persons = graphene.List(
         LessonsForPersonType,
         persons=graphene.List(graphene.ID, required=True),
-        start=graphene.Date(required=True),
-        end=graphene.Date(required=True),
+        start=graphene.DateTime(required=True),
+        end=graphene.DateTime(required=True),
     )
 
+    extra_marks = FilterOrderList(ExtraMarkType)
+
+    coursebook_absence_reasons = FilterOrderList(AbsenceReasonType)
+
     statistics_by_person = graphene.Field(
         StatisticsByPersonType,
         person=graphene.ID(required=True),
@@ -87,6 +111,7 @@ class Query(graphene.ObjectType):
         obj_type=None,
         obj_id=None,
         incomplete=False,
+        absences_exist=False,
         **kwargs,
     ):
         if (
@@ -132,7 +157,14 @@ class Query(graphene.ObjectType):
         )
 
         # Lookup or create documentations and return them all.
-        docs, dummies = Documentation.get_documentations_for_events(events, incomplete)
+        docs, dummies = Documentation.get_documentations_for_events(
+            datetime.combine(date_start, datetime.min.time()),
+            datetime.combine(date_end, datetime.max.time()),
+            events,
+            incomplete,
+            absences_exist,
+            info.context,
+        )
         return docs + dummies
 
     @staticmethod
@@ -146,8 +178,17 @@ class Query(graphene.ObjectType):
         else:
             raise PermissionDenied()
 
-        return Group.objects.filter(
-            Q(members=person) | Q(owners=person) | Q(parent_groups__owners=person)
+        return (
+            Group.objects.for_current_school_term_or_all()
+            .filter(Q(members=person) | Q(owners=person) | Q(parent_groups__owners=person))
+            .distinct()
+            .annotate(
+                is_priority=ExpressionWrapper(
+                    Q(group_type=get_site_preferences()["alsijil__group_type_priority_coursebook"]),
+                    output_field=BooleanField(),
+                )
+            )
+            .order_by("is_priority")
         )
 
     @staticmethod
@@ -160,13 +201,29 @@ class Query(graphene.ObjectType):
             person = info.context.user.person
         else:
             raise PermissionDenied()
-
         return Course.objects.filter(
-            Q(teachers=person)
-            | Q(groups__members=person)
-            | Q(groups__owners=person)
-            | Q(groups__parent_groups__owners=person)
-        )
+            (
+                Q(teachers=person)
+                | Q(groups__members=person)
+                | Q(groups__owners=person)
+                | Q(groups__parent_groups__owners=person)
+            )
+            & Q(groups__in=Group.objects.for_current_school_term_or_all())
+        ).distinct()
+
+    @staticmethod
+    def resolve_absence_creation_persons(root, info, **kwargs):
+        if not info.context.user.has_perm("alsijil.register_absence"):
+            group_types = get_site_preferences()["alsijil__group_types_register_absence"]
+            if group_types:
+                return Person.objects.filter(
+                    member_of__in=Group.objects.filter(
+                        owners=info.context.user.person, group_type__in=group_types
+                    )
+                )
+            else:
+                return Person.objects.filter(member_of__owners=info.context.user.person)
+        return Person.objects.all()
 
     @staticmethod
     def resolve_lessons_for_persons(
@@ -182,14 +239,21 @@ class Query(graphene.ObjectType):
         for person in persons:
             docs, dummies = Documentation.get_documentations_for_person(
                 person,
-                datetime.combine(start, datetime.min.time()),
-                datetime.combine(end, datetime.max.time()),
+                start,
+                end,
+                info.context,
             )
 
-            lessons_for_person.append(id=person, lessons=docs + dummies)
+            lessons_for_person.append(LessonsForPersonType(id=person, lessons=docs + dummies))
 
         return lessons_for_person
 
+    @staticmethod
+    def resolve_coursebook_absence_reasons(root, info, **kwargs):
+        if not info.context.user.has_perm("kolego.fetch_absencereasons_rule"):
+            return []
+        return AbsenceReason.objects.filter(tags__short_name="class_register")
+
     @staticmethod
     def resolve_statistics_by_person(root, info, person, term):
         school_term = SchoolTerm.objects.get(id=term)
@@ -214,4 +278,13 @@ class Mutation(graphene.ObjectType):
     create_or_update_documentations = DocumentationBatchCreateOrUpdateMutation.Field()
     touch_documentation = TouchDocumentationMutation.Field()
     update_participation_statuses = ParticipationStatusBatchPatchMutation.Field()
-    create_absences = AbsencesBatchCreateMutation.Field()
+    create_absences_for_persons = AbsencesForPersonsCreateMutation.Field()
+    extend_participation_statuses = ExtendParticipationStatusToAbsenceBatchMutation.Field()
+
+    create_extra_marks = ExtraMarkBatchCreateMutation.Field()
+    update_extra_marks = ExtraMarkBatchPatchMutation.Field()
+    delete_extra_marks = ExtraMarkBatchDeleteMutation.Field()
+
+    create_personal_notes = PersonalNoteBatchCreateMutation.Field()
+    update_personal_notes = PersonalNoteBatchPatchMutation.Field()
+    delete_personal_notes = PersonalNoteBatchDeleteMutation.Field()
diff --git a/aleksis/apps/alsijil/schema/absences.py b/aleksis/apps/alsijil/schema/absences.py
index cd79f6863a688b763993e556d1651937576508a6..ab006e1d875e053e061d04c9c800ae0c3ef15f2e 100644
--- a/aleksis/apps/alsijil/schema/absences.py
+++ b/aleksis/apps/alsijil/schema/absences.py
@@ -1,78 +1,83 @@
-from datetime import datetime
+import datetime
+from typing import List
+
+from django.core.exceptions import PermissionDenied
+from django.db.models import Q
 
 import graphene
 
 from aleksis.apps.kolego.models import Absence
+from aleksis.core.models import Person
 
-from ..models import Documentation, ParticipationStatus
+from ..models import ParticipationStatus
+from .participation_status import ParticipationStatusType
 
 
-class AbsencesBatchCreateMutation(graphene.Mutation):
+class AbsencesForPersonsCreateMutation(graphene.Mutation):
     class Arguments:
-        persons = graphene.List(graphene.ID)
-        start = graphene.Date()
-        end = graphene.Date()
-        comment = graphene.String()
-        reason = graphene.ID()
+        persons = graphene.List(graphene.ID, required=True)
+        start = graphene.DateTime(required=True)
+        end = graphene.DateTime(required=True)
+        comment = graphene.String(required=False)
+        reason = graphene.ID(required=True)
 
     ok = graphene.Boolean()
+    participation_statuses = graphene.List(ParticipationStatusType)
 
     @classmethod
-    def mutate(cls, root, info, persons, start, end, comment, reason):  # noqa
-        # TODO: Check permissions for ParticipationStatus & KolegoAbsence
-        #       See MR 356
+    def mutate(
+        cls,
+        root,
+        info,
+        persons: List[str | int],
+        start: datetime.datetime,
+        end: datetime.datetime,
+        comment: str,
+        reason: str | int,
+    ):
+        participation_statuses = []
 
-        # DocumentationBatchCreateOrUpdateMutation.create_or_update
-        # at least already checks permissions.
+        persons = Person.objects.filter(pk__in=persons)
 
         for person in persons:
-            # Get all documentations for this person between start & end
-            docs, dummies = Documentation.get_documentations_for_person(
-                person,
-                datetime.combine(start, datetime.min.time()),
-                datetime.combine(end, datetime.max.time()),
-            )
-
-            # Create doc for dummies that are already in the past
-            future = False
-            for dummy in dummies:
-                lesson_event, dummy_start, dummy_end = Documentation.parse_dummy(dummy.id)
+            if not info.context.user.has_perm("alsijil.register_absence_rule", person):
+                raise PermissionDenied()
 
-                if dummy_start < datetime.now():
-                    # In the past -> Create a Documentation
-                    docs.append(
-                        Documentation.create_from_lesson_event(
-                            info.context.user,
-                            lesson_event,
-                            dummy_start,
-                            dummy_end,
-                        )
-                    )
-                else:
-                    future = True
-
-            # Create a ParticipationStatus for each documentation
-            for doc in docs:
-                # Set person & absence_reason directly from id
-                ParticipationStatus.objects.create(
-                    person_id=person,
-                    related_documentation=doc,
-                    absence_reason_id=reason,
-                )
+            # Check if there is an existing absence with overlapping datetime
+            absences = Absence.objects.filter(
+                Q(datetime_start__lte=start) | Q(date_start__lte=start.date()),
+                Q(datetime_end__gte=end) | Q(date_end__gte=end.date()),
+                reason_id=reason,
+                person=person,
+            )
 
-            # If there are still dummy documentations in the future
-            # create a Kolego Absence
-            if future:
-                # TODO: Are date_start & date_end from CalendarEvent enough
-                #       or more needed?
-                # Set reason & person directly from id
-                Absence.objects.create(
-                    date_start=datetime.now().date(),
-                    date_end=end,
+            if len(absences) > 0:
+                kolego_absence = absences.first()
+            else:
+                # Check for same times and create otherwise
+                kolego_absence, __ = Absence.objects.get_or_create(
+                    datetime_start=start,
+                    datetime_end=end,
                     reason_id=reason,
-                    person_id=person,
-                    comment=comment,
+                    person=person,
+                    defaults={"comment": comment},
                 )
 
-        # Return ok=True if everything went well.
-        return AbsencesBatchCreateMutation(ok=True)
+            events = ParticipationStatus.get_single_events(
+                start,
+                end,
+                None,
+                {"person": person},
+                with_reference_object=True,
+            )
+
+            for event in events:
+                participation_status = event["REFERENCE_OBJECT"]
+                participation_status.absence_reason_id = reason
+                participation_status.base_absence = kolego_absence
+                participation_status.save()
+                participation_statuses.append(participation_status)
+
+        return AbsencesForPersonsCreateMutation(
+            ok=True, participation_statuses=participation_statuses
+        )
diff --git a/aleksis/apps/alsijil/schema/documentation.py b/aleksis/apps/alsijil/schema/documentation.py
index 4a0055064abd0a9cc1bb79850390da318a808b4a..cdb2eaca122330f70bd75023a463f2dc1dc0e8e7 100644
--- a/aleksis/apps/alsijil/schema/documentation.py
+++ b/aleksis/apps/alsijil/schema/documentation.py
@@ -4,7 +4,14 @@ import graphene
 from graphene_django.types import DjangoObjectType
 from reversion import create_revision, set_comment, set_user
 
-from aleksis.apps.alsijil.util.predicates import can_edit_documentation, is_in_allowed_time_range
+from aleksis.apps.alsijil.util.predicates import (
+    can_edit_documentation,
+    is_in_allowed_time_range,
+    is_in_allowed_time_range_for_participation_status,
+)
+from aleksis.apps.alsijil.util.predicates import (
+    can_edit_participation_status as can_edit_participation_status_predicate,
+)
 from aleksis.apps.chronos.schema import LessonEventType
 from aleksis.apps.cursus.models import Subject
 from aleksis.apps.cursus.schema import CourseType, SubjectType
@@ -13,6 +20,7 @@ from aleksis.core.schema.base import (
     DjangoFilterMixin,
     PermissionsTypeMixin,
 )
+from aleksis.core.util.core_helpers import has_person
 
 from ..models import Documentation
 from .participation_status import ParticipationStatusType
@@ -34,7 +42,6 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
             "date_start",
             "date_end",
             "teachers",
-            "participations",
         )
         filter_fields = {
             "id": ["exact", "lte", "gte"],
@@ -47,6 +54,10 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
     participations = graphene.List(ParticipationStatusType, required=False)
 
     future_notice = graphene.Boolean(required=False)
+    future_notice_participation_status = graphene.Boolean(required=False)
+
+    can_edit_participation_status = graphene.Boolean(required=False)
+    can_view_participation_status = graphene.Boolean(required=False)
 
     old_id = graphene.ID(required=False)
 
@@ -69,16 +80,37 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
         )
 
     @staticmethod
-    def resolve_participations(root: Documentation, info, **kwargs):
-        if not info.context.user.has_perm(
-            "alsijil.view_participation_status_for_documentation", root
-        ):
-            return []
+    def resolve_future_notice_participation_status(root: Documentation, info, **kwargs):
+        """Shows whether the user can edit all participation statuses based on the current time.
+
+        This checks whether the documentation is in the future.
+        """
+        return not is_in_allowed_time_range_for_participation_status(info.context.user, root)
+
+    @staticmethod
+    def resolve_can_edit_participation_status(root: Documentation, info, **kwargs):
+        """Shows whether the user can edit all participation statuses of the documentation"""
+        return can_edit_participation_status_predicate(info.context.user, root)
 
+    @staticmethod
+    def resolve_can_view_participation_status(root: Documentation, info, **kwargs):
+        """Shows whether the user can view all participation statuses of the documentation"""
+        return info.context.user.has_perm(
+            "alsijil.view_participation_status_for_documentation_rule", root
+        )
+
+    @staticmethod
+    def resolve_participations(root: Documentation, info, **kwargs):
         # A dummy documentation will not have any participations
         if str(root.pk).startswith("DUMMY") or not hasattr(root, "participations"):
             return []
-        return root.participations.select_related("absence_reason", "base_absence").all()
+        elif not info.context.user.has_perm(
+            "alsijil.view_participation_status_for_documentation_rule", root
+        ):
+            if has_person(info.context.user):
+                return root.participations.filter(person=info.context.user.person)
+            return []
+        return root.participations.all()
 
 
 class DocumentationInputType(graphene.InputObjectType):
@@ -111,9 +143,6 @@ class DocumentationBatchCreateOrUpdateMutation(graphene.Mutation):
         # is only introduced in Django 5.0
         obj, __ = Documentation.get_or_create_by_id(_id, info.context.user)
 
-        if not info.context.user.has_perm("alsijil.edit_documentation_rule", obj):
-            raise PermissionDenied()
-
         if doc.topic is not None:
             obj.topic = doc.topic
         if doc.homework is not None:
@@ -151,7 +180,8 @@ class TouchDocumentationMutation(graphene.Mutation):
         )
 
         if not info.context.user.has_perm(
-            "alsijil.edit_participation_status_for_documentation_rule", documentation
+            "alsijil.edit_participation_status_for_documentation_with_time_range_rule",
+            documentation,
         ):
             raise PermissionDenied()
 
diff --git a/aleksis/apps/alsijil/schema/extra_marks.py b/aleksis/apps/alsijil/schema/extra_marks.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b1e3723d2c559e70ddd6793f5f2a89d9c1e6abe
--- /dev/null
+++ b/aleksis/apps/alsijil/schema/extra_marks.py
@@ -0,0 +1,66 @@
+from django.core.exceptions import PermissionDenied
+
+from graphene_django import DjangoObjectType
+
+from aleksis.apps.alsijil.models import ExtraMark
+from aleksis.core.schema.base import (
+    BaseBatchCreateMutation,
+    BaseBatchDeleteMutation,
+    BaseBatchPatchMutation,
+    DjangoFilterMixin,
+    OptimisticResponseTypeMixin,
+    PermissionsTypeMixin,
+)
+
+
+class ExtraMarkType(
+    OptimisticResponseTypeMixin,
+    PermissionsTypeMixin,
+    DjangoFilterMixin,
+    DjangoObjectType,
+):
+    class Meta:
+        model = ExtraMark
+        fields = ("id", "short_name", "name", "colour_fg", "colour_bg", "show_in_coursebook")
+
+    @classmethod
+    def get_queryset(cls, queryset, info):
+        if info.context.user.has_perm("alsijil.fetch_extramarks_rule"):
+            return queryset
+        raise PermissionDenied()
+
+
+class ExtraMarkBatchCreateMutation(BaseBatchCreateMutation):
+    class Meta:
+        model = ExtraMark
+        fields = ("short_name", "name", "colour_fg", "colour_bg", "show_in_coursebook")
+        optional_fields = ("name",)
+
+    @classmethod
+    def check_permissions(cls, root, info, input):  # noqa
+        if info.context.user.has_perm("alsijil.create_extramark_rule"):
+            return
+        raise PermissionDenied()
+
+
+class ExtraMarkBatchDeleteMutation(BaseBatchDeleteMutation):
+    class Meta:
+        model = ExtraMark
+
+    @classmethod
+    def check_permissions(cls, root, info, input):  # noqa
+        if info.context.user.has_perm("alsijil.delete_extramark_rule"):
+            return
+        raise PermissionDenied()
+
+
+class ExtraMarkBatchPatchMutation(BaseBatchPatchMutation):
+    class Meta:
+        model = ExtraMark
+        fields = ("id", "short_name", "name", "colour_fg", "colour_bg", "show_in_coursebook")
+
+    @classmethod
+    def check_permissions(cls, root, info, input):  # noqa
+        if info.context.user.has_perm("alsijil.edit_extramark_rule"):
+            return
+        raise PermissionDenied()
diff --git a/aleksis/apps/alsijil/schema/participation_status.py b/aleksis/apps/alsijil/schema/participation_status.py
index 246ae52a0aab7e7bf57f102ccd7e6558d4639496..fcf15df815619fcc808770116347458d8f7daf02 100644
--- a/aleksis/apps/alsijil/schema/participation_status.py
+++ b/aleksis/apps/alsijil/schema/participation_status.py
@@ -1,8 +1,16 @@
+import datetime
+
 from django.core.exceptions import PermissionDenied
+from django.utils.translation import gettext_lazy as _
 
+import graphene
 from graphene_django import DjangoObjectType
+from reversion import create_revision, set_comment, set_user
 
-from aleksis.apps.alsijil.models import ParticipationStatus
+from aleksis.apps.alsijil.models import NewPersonalNote, ParticipationStatus
+from aleksis.apps.alsijil.schema.personal_note import PersonalNoteType
+from aleksis.apps.kolego.models import Absence
+from aleksis.apps.kolego.schema.absence import AbsenceType
 from aleksis.core.schema.base import (
     BaseBatchPatchMutation,
     DjangoFilterMixin,
@@ -25,13 +33,37 @@ class ParticipationStatusType(
             "absence_reason",
             "related_documentation",
             "base_absence",
+            "tardiness",
+        )
+
+    notes_with_extra_mark = graphene.List(PersonalNoteType)
+    notes_with_note = graphene.List(PersonalNoteType)
+
+    @staticmethod
+    def resolve_notes_with_extra_mark(root: ParticipationStatus, info, **kwargs):
+        return NewPersonalNote.objects.filter(
+            person=root.person,
+            documentation=root.related_documentation,
+            extra_mark__isnull=False,
         )
 
+    @staticmethod
+    def resolve_notes_with_note(root: ParticipationStatus, info, **kwargs):
+        return NewPersonalNote.objects.filter(
+            person=root.person,
+            documentation=root.related_documentation,
+            note__isnull=False,
+        ).exclude(note="")
+
 
 class ParticipationStatusBatchPatchMutation(BaseBatchPatchMutation):
     class Meta:
         model = ParticipationStatus
-        fields = ("id", "absence_reason")  # Only the reason can be updated after creation
+        fields = (
+            "id",
+            "absence_reason",
+            "tardiness",
+        )  # Only the reason and tardiness can be updated after creation
         return_field_name = "participationStatuses"
 
     @classmethod
@@ -41,6 +73,81 @@ class ParticipationStatusBatchPatchMutation(BaseBatchPatchMutation):
     @classmethod
     def after_update_obj(cls, root, info, input, obj, full_input):  # noqa: A002
         if not info.context.user.has_perm(
-            "alsijil.edit_participation_status_for_documentation_rule", obj.related_documentation
+            "alsijil.edit_participation_status_for_documentation_with_time_range_rule",
+            obj.related_documentation,
         ):
             raise PermissionDenied()
+
+
+class ExtendParticipationStatusToAbsenceBatchMutation(graphene.Mutation):
+    class Arguments:
+        input = graphene.List(graphene.ID, description=_("List of ParticipationStatus IDs"))
+
+    participations = graphene.List(ParticipationStatusType)
+    absences = graphene.List(AbsenceType)
+
+    @classmethod
+    def create_absence(cls, info, participation_id):
+        participation = ParticipationStatus.objects.get(pk=participation_id)
+
+        if participation.date_end:
+            end_date = participation.date_end
+        else:
+            end_date = ParticipationStatus.value_end_datetime(participation).date()
+
+        end_datetime = datetime.datetime.combine(
+            end_date, datetime.time.max, participation.timezone
+        )
+
+        if participation.base_absence:
+            # Update the base absence to increase length if needed
+            absence = participation.base_absence
+
+            if absence.date_end:
+                if absence.date_end < end_date:
+                    absence.date_end = end_date
+                    absence.save()
+
+                return participation, absence
+
+            # Absence uses a datetime
+            if absence.datetime_end.astimezone(absence.timezone) < end_datetime:
+                # The end date ends after the previous absence end
+                absence.datetime_end = end_datetime
+                absence.save()
+
+            return participation, absence
+
+        else:
+            # No base absence, simply create one
+            data = dict(
+                reason_id=participation.absence_reason.id,
+                person=participation.person,
+            )
+
+            if participation.date_start:
+                data["date_start"] = participation.date_start
+                data["date_end"] = end_date
+            else:
+                data["datetime_start"] = ParticipationStatus.value_start_datetime(participation)
+                data["datetime_end"] = end_datetime
+
+            absence, __ = Absence.objects.get_or_create(**data)
+
+            participation.base_absence = absence
+            participation.save()
+
+            return participation, absence
+
+    @classmethod
+    def mutate(cls, root, info, input):  # noqa
+        with create_revision():
+            set_user(info.context.user)
+            set_comment(_("Extended absence reason from coursebook."))
+            participations, absences = zip(
+                *[cls.create_absence(info, participation_id) for participation_id in input]
+            )
+
+        return ExtendParticipationStatusToAbsenceBatchMutation(
+            participations=participations, absences=absences
+        )
diff --git a/aleksis/apps/alsijil/schema/personal_note.py b/aleksis/apps/alsijil/schema/personal_note.py
index afd44904e69ac9998fc49444c9d1422fae74e68e..dfe36359d87345a0b51992e689627065638fcd4e 100644
--- a/aleksis/apps/alsijil/schema/personal_note.py
+++ b/aleksis/apps/alsijil/schema/personal_note.py
@@ -1,23 +1,50 @@
-from graphene_django.types import DjangoObjectType
-from guardian.shortcuts import get_objects_for_user
+from graphene_django import DjangoObjectType
 
+from aleksis.apps.alsijil.models import NewPersonalNote
 from aleksis.core.schema.base import (
+    BaseBatchCreateMutation,
+    BaseBatchDeleteMutation,
+    BaseBatchPatchMutation,
     DjangoFilterMixin,
+    OptimisticResponseTypeMixin,
     PermissionsTypeMixin,
 )
 
-from ..models import ExtraMark
+
+class PersonalNoteType(
+    OptimisticResponseTypeMixin,
+    PermissionsTypeMixin,
+    DjangoFilterMixin,
+    DjangoObjectType,
+):
+    class Meta:
+        model = NewPersonalNote
+        fields = (
+            "id",
+            "note",
+            "extra_mark",
+        )
+
+
+class PersonalNoteBatchCreateMutation(BaseBatchCreateMutation):
+    class Meta:
+        model = NewPersonalNote
+        type_name = "BatchCreatePersonalNoteInput"
+        return_field_name = "personalNotes"
+        fields = ("note", "extra_mark", "documentation", "person")
+        permissions = ("alsijil.edit_personal_note_rule",)
+
+
+class PersonalNoteBatchPatchMutation(BaseBatchPatchMutation):
+    class Meta:
+        model = NewPersonalNote
+        type_name = "BatchPatchPersonalNoteInput"
+        return_field_name = "personalNotes"
+        fields = ("id", "note", "extra_mark", "documentation", "person")
+        permissions = ("alsijil.edit_personal_note_rule",)
 
 
-class ExtraMarkType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType):
+class PersonalNoteBatchDeleteMutation(BaseBatchDeleteMutation):
     class Meta:
-        model = ExtraMark
-        fields = ("id", "short_name", "name", "colour_fg", "colour_bg", "show_in_coursebook")
-        filter_fields = {
-            "short_name": ["icontains", "exact"],
-            "name": ["icontains", "exact"],
-        }
-
-    @classmethod
-    def get_queryset(cls, queryset, info):
-        return get_objects_for_user(info.context.user, "alsijil.view_extramark", queryset)
+        model = NewPersonalNote
+        permissions = ("alsijil.edit_personal_note_rule",)
diff --git a/aleksis/apps/alsijil/schema/statistics.py b/aleksis/apps/alsijil/schema/statistics.py
index d7f89e0f96f14477cc6fb9ac5a603e3bb030fb91..2788fcfebb9f7a863d8809bfc3b2694bde751f18 100644
--- a/aleksis/apps/alsijil/schema/statistics.py
+++ b/aleksis/apps/alsijil/schema/statistics.py
@@ -8,7 +8,7 @@ from aleksis.core.models import Person
 from aleksis.core.schema.person import PersonType
 
 from ..models import ExtraMark
-from .personal_note import ExtraMarkType
+from .extra_marks import ExtraMarkType
 
 
 class AbsenceReasonWithCountType(graphene.ObjectType):
diff --git a/aleksis/apps/alsijil/tables.py b/aleksis/apps/alsijil/tables.py
index a0ef1734e5eea326fd9a8fd0f858b71a89089082..f17a214930f2d7f2e40a1662b491eab587bda0ad 100644
--- a/aleksis/apps/alsijil/tables.py
+++ b/aleksis/apps/alsijil/tables.py
@@ -11,26 +11,6 @@ from aleksis.core.util.tables import SelectColumn
 from .models import PersonalNote
 
 
-class ExtraMarkTable(tables.Table):
-    class Meta:
-        attrs = {"class": "highlight"}
-
-    name = tables.LinkColumn("edit_extra_mark", args=[A("id")])
-    short_name = tables.Column()
-    edit = tables.LinkColumn(
-        "edit_extra_mark",
-        args=[A("id")],
-        text=_("Edit"),
-        attrs={"a": {"class": "btn-flat waves-effect waves-orange orange-text"}},
-    )
-    delete = tables.LinkColumn(
-        "delete_extra_mark",
-        args=[A("id")],
-        text=_("Delete"),
-        attrs={"a": {"class": "btn-flat waves-effect waves-red red-text"}},
-    )
-
-
 class ExcuseTypeTable(tables.Table):
     class Meta:
         attrs = {"class": "highlight"}
diff --git a/aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html b/aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html
deleted file mode 100644
index d0ee3a9055561df1f468692f79d794404a60c1d1..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html
+++ /dev/null
@@ -1,17 +0,0 @@
-	{# -*- engine:django -*- #}
-
-	{% extends "core/base.html" %}
-	{% load material_form i18n %}
-
-	{% block browser_title %}{% blocktrans %}Create extra mark{% endblocktrans %}{% endblock %}
-	{% block page_title %}{% blocktrans %}Create extra mark{% endblocktrans %}{% endblock %}
-
-	{% block content %}
-
-	  <form method="post">
-	    {% csrf_token %}
-	    {% form form=form %}{% endform %}
-	    {% include "core/partials/save_button.html" %}
-	  </form>
-
-	{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html b/aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html
deleted file mode 100644
index 7adee30a1cfd30256d70b2a823827384de331e05..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{# -*- engine:django -*- #}
-
-{% extends "core/base.html" %}
-{% load material_form i18n %}
-
-{% block browser_title %}{% blocktrans %}Edit extra mark{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Edit extra mark{% endblocktrans %}{% endblock %}
-
-{% block content %}
-
-  <form method="post">
-    {% csrf_token %}
-    {% form form=form %}{% endform %}
-    {% include "core/partials/save_button.html" %}
-  </form>
-
-{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html b/aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html
deleted file mode 100644
index 9eeb63b1a81162e6490072ca7184059be7a5193a..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{# -*- engine:django -*- #}
-
-{% extends "core/base.html" %}
-
-{% load i18n %}
-{% load render_table from django_tables2 %}
-
-{% block browser_title %}{% blocktrans %}Extra marks{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Extra marks{% endblocktrans %}{% endblock %}
-
-{% block content %}
-  <a class="btn green waves-effect waves-light" href="{% url 'create_extra_mark' %}">
-    <i class="material-icons iconify left" data-icon="mdi:plus"></i>
-    {% trans "Create extra mark" %}
-  </a>
-
-  {% render_table table %}
-{% endblock %}
diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py
index edc0b4fa81cfb50e1f268d9dd21b3d9dda2c033a..cd7367ce84973bb4eb62791e9ce5b2c7eb3a5f85 100644
--- a/aleksis/apps/alsijil/urls.py
+++ b/aleksis/apps/alsijil/urls.py
@@ -3,22 +3,6 @@ from django.urls import path
 from . import views
 
 urlpatterns = [
-    path("extra_marks/", views.ExtraMarkListView.as_view(), name="extra_marks"),
-    path(
-        "extra_marks/create/",
-        views.ExtraMarkCreateView.as_view(),
-        name="create_extra_mark",
-    ),
-    path(
-        "extra_marks/<int:pk>/edit/",
-        views.ExtraMarkEditView.as_view(),
-        name="edit_extra_mark",
-    ),
-    path(
-        "extra_marks/<int:pk>/delete/",
-        views.ExtraMarkDeleteView.as_view(),
-        name="delete_extra_mark",
-    ),
     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/util/alsijil_helpers.py b/aleksis/apps/alsijil/util/alsijil_helpers.py
index 8e203e8f61f023af2c812efe0f21cf9a34511c60..118b70af8e1a71f9d5e56a8d251c5a319cae0e02 100644
--- a/aleksis/apps/alsijil/util/alsijil_helpers.py
+++ b/aleksis/apps/alsijil/util/alsijil_helpers.py
@@ -15,6 +15,7 @@ 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
 
@@ -401,3 +402,11 @@ def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[D
         register_objects = sorted(register_objects, key=itemgetter("date_sort", "period_sort"))
         return register_objects
     return []
+
+
+def get_absence_reason_tag():
+    return AbsenceReasonTag.objects.managed_by_app("alsijil").get_or_create(
+        managed_by_app_label="alsijil",
+        short_name="class_register",
+        defaults={"name": "Class Register"},
+    )
diff --git a/aleksis/apps/alsijil/util/predicates.py b/aleksis/apps/alsijil/util/predicates.py
index 9f06195e279b6e9bc9564731146d33ea7995498d..5966a7459bc8a32f7e7468f20de2e7da0044441e 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, PersonalNote
+from ..models import Documentation, NewPersonalNote, PersonalNote
 
 
 @predicate
@@ -93,19 +93,34 @@ def is_group_owner(user: User, obj: Union[Group, Person]) -> bool:
 
 
 @predicate
-def is_person_group_owner(user: User, obj: Person) -> bool:
+def is_person_group_owner(user: User, obj) -> bool:
     """
     Predicate for group owners of any group.
 
     Checks whether the person linked to the user is
     the owner of any group of the given person.
     """
-    if obj:
-        for group in use_prefetched(obj, "member_of"):
-            if user.person in use_prefetched(group, "owners"):
-                return True
-        return False
-    return False
+    return Group.objects.filter(owners=user.person).exists()
+
+
+@predicate
+def can_register_absence_for_at_least_one_group(user: User, obj) -> bool:
+    """Predicate for registering absence for at least one group."""
+    group_types = get_site_preferences()["alsijil__group_types_register_absence"]
+    qs = Group.objects.filter(owners=user.person)
+    if not group_types:
+        return qs.exists()
+    return qs.filter(group_type__in=group_types).exists()
+
+
+@predicate
+def can_register_absence_for_person(user: User, obj: Person) -> bool:
+    """Predicate for registering absence for person."""
+    group_types = get_site_preferences()["alsijil__group_types_register_absence"]
+    qs = obj.member_of.filter(owners=user.person)
+    if not group_types:
+        return qs.exists()
+    return qs.filter(group_type__in=group_types).exists()
 
 
 def use_prefetched(obj, attr):
@@ -450,6 +465,8 @@ def can_edit_documentation(user: User, obj: Documentation):
 def can_view_participation_status(user: User, obj: Documentation):
     """Predicate which checks if the user is allowed to view participation for a documentation."""
     if obj:
+        if obj.amends and obj.amends.cancelled:
+            return False
         if is_documentation_teacher(user, obj):
             return True
         if obj.amends:
@@ -465,6 +482,8 @@ def can_view_participation_status(user: User, obj: Documentation):
 def can_edit_participation_status(user: User, obj: Documentation):
     """Predicate which checks if the user is allowed to edit participation for a documentation."""
     if obj:
+        if obj.amends and obj.amends.cancelled:
+            return False
         if is_documentation_teacher(user, obj):
             return True
         if obj.amends:
@@ -475,8 +494,14 @@ def can_edit_participation_status(user: User, obj: Documentation):
 
 
 @predicate
-def is_in_allowed_time_range(user: User, obj: Documentation):
-    """Predicate which checks if the documentation is in the allowed time range for editing."""
+def is_in_allowed_time_range(user: User, obj: Union[Documentation, NewPersonalNote]):
+    """Predicate for documentations or new personal notes with linked documentation.
+
+    Predicate which checks if the given documentation or the documentation linked
+    to the given NewPersonalNote is in the allowed time range for editing.
+    """
+    if isinstance(obj, NewPersonalNote):
+        obj = obj.documentation
     if obj and (
         get_site_preferences()["alsijil__allow_edit_future_documentations"] == "all"
         or (
@@ -498,3 +523,31 @@ def is_in_allowed_time_range_for_participation_status(user: User, obj: Documenta
     if obj and obj.value_start_datetime(obj) <= now():
         return True
     return False
+
+
+@predicate
+def can_view_personal_note(user: User, obj: NewPersonalNote):
+    """Predicate which checks if the user is allowed to view a personal note."""
+    if obj.documentation:
+        if is_documentation_teacher(user, obj.documentation):
+            return True
+        if obj.documentation.amends:
+            return is_lesson_event_teacher(
+                user, obj.documentation.amends
+            ) | is_lesson_event_group_owner(user, obj.documentation.amends)
+        if obj.documentation.course:
+            return is_course_teacher(user, obj.documentation.course)
+    return False
+
+
+@predicate
+def can_edit_personal_note(user: User, obj: NewPersonalNote):
+    """Predicate which checks if the user is allowed to edit a personal note."""
+    if obj.documentation:
+        if is_documentation_teacher(user, obj.documentation):
+            return True
+        if obj.documentation.amends:
+            return is_lesson_event_teacher(
+                user, obj.documentation.amends
+            ) | is_lesson_event_group_owner(user, obj.documentation.amends)
+    return False
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index dd010383b83ae7e28c6d98fd7de34bf9aa5432ba..0395a8bfbc54fcab81dfea092f431045d15ae134 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -48,7 +48,6 @@ from .filters import PersonalNoteFilter
 from .forms import (
     AssignGroupRoleForm,
     ExcuseTypeForm,
-    ExtraMarkForm,
     FilterRegisterObjectForm,
     GroupRoleAssignmentEditForm,
     GroupRoleForm,
@@ -62,7 +61,6 @@ from .forms import (
 from .models import ExcuseType, ExtraMark, GroupRole, GroupRoleAssignment, PersonalNote
 from .tables import (
     ExcuseTypeTable,
-    ExtraMarkTable,
     GroupRoleTable,
     PersonalNoteTable,
     RegisterObjectSelectTable,
@@ -1052,51 +1050,6 @@ class DeletePersonalNoteView(PermissionRequiredMixin, DetailView):
         return redirect("overview_person", note.person.pk)
 
 
-@method_decorator(pwa_cache, "dispatch")
-class ExtraMarkListView(PermissionRequiredMixin, SingleTableView):
-    """Table of all extra marks."""
-
-    model = ExtraMark
-    table_class = ExtraMarkTable
-    permission_required = "alsijil.view_extramarks_rule"
-    template_name = "alsijil/extra_mark/list.html"
-
-
-@method_decorator(never_cache, name="dispatch")
-class ExtraMarkCreateView(PermissionRequiredMixin, AdvancedCreateView):
-    """Create view for extra marks."""
-
-    model = ExtraMark
-    form_class = ExtraMarkForm
-    permission_required = "alsijil.add_extramark_rule"
-    template_name = "alsijil/extra_mark/create.html"
-    success_url = reverse_lazy("extra_marks")
-    success_message = _("The extra mark has been created.")
-
-
-@method_decorator(never_cache, name="dispatch")
-class ExtraMarkEditView(PermissionRequiredMixin, AdvancedEditView):
-    """Edit view for extra marks."""
-
-    model = ExtraMark
-    form_class = ExtraMarkForm
-    permission_required = "alsijil.edit_extramark_rule"
-    template_name = "alsijil/extra_mark/edit.html"
-    success_url = reverse_lazy("extra_marks")
-    success_message = _("The extra mark has been saved.")
-
-
-@method_decorator(never_cache, name="dispatch")
-class ExtraMarkDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDeleteView):
-    """Delete view for extra marks."""
-
-    model = ExtraMark
-    permission_required = "alsijil.delete_extramark_rule"
-    template_name = "core/pages/delete.html"
-    success_url = reverse_lazy("extra_marks")
-    success_message = _("The extra mark has been deleted.")
-
-
 @method_decorator(pwa_cache, "dispatch")
 class ExcuseTypeListView(PermissionRequiredMixin, SingleTableView):
     """Table of all excuse types."""
diff --git a/pyproject.toml b/pyproject.toml
index 03a0772bee8aa34ffc35dc565a1762b87745abbb..3bbbbc8096e676c9522ab1308502dae104dc50c4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "AlekSIS-App-Alsijil"
-version = "4.0.0.dev1"
+version = "4.0.0.dev5"
 packages = [
     { include = "aleksis" }
 ]
@@ -49,10 +49,10 @@ priority = "supplemental"
 
 [tool.poetry.dependencies]
 python = "^3.10"
-aleksis-core = "^4.0.0.dev7"
-aleksis-app-chronos = "^4.0.0.dev3"
+aleksis-core = "^4.0.0.dev11"
+aleksis-app-chronos = "^4.0.0.dev5"
 aleksis-app-stoelindeling = { version = "^3.0.dev1", optional = true }
-aleksis-app-kolego = "^0.1.0.dev2"
+aleksis-app-kolego = "^0.1.0.dev3"
 
 [tool.poetry.extras]
 seatingplans = ["aleksis-app-stoelindeling"]