diff --git a/aleksis/apps/alsijil/checks.py b/aleksis/apps/alsijil/checks.py
index 310e5d2dbc17cbe95d89f81a4aa83fb0ef7f25b0..a440f52da0888a3a97f752affd038c0fff583244 100644
--- a/aleksis/apps/alsijil/checks.py
+++ b/aleksis/apps/alsijil/checks.py
@@ -98,7 +98,7 @@ class DocumentationOnHolidaysDataCheck(DataCheck):
 
     @classmethod
     def check_data(cls):
-        from aleksis.apps.chronos.models import Holiday
+        from aleksis.core.models import Holiday
 
         from .models import Documentation
 
@@ -132,9 +132,9 @@ class ParticipationStatusPersonalNoteOnHolidaysDataCheck(DataCheck):
 
     @classmethod
     def check_data(cls):
-        from aleksis.apps.chronos.models import Holiday
+        from aleksis.core.models import Holiday
 
-        from .models import NewPersonalNote, ParticipationStatus
+        from .models import ParticipationStatus
 
         holidays = Holiday.objects.all()
 
@@ -146,12 +146,7 @@ class ParticipationStatusPersonalNoteOnHolidaysDataCheck(DataCheck):
             )
 
         participation_statuses = ParticipationStatus.objects.filter(q)
-        personal_notes = NewPersonalNote.objects.filter(q)
 
         for status in participation_statuses:
             logging.info(f"Participation status {status} is on holidays")
             cls.register_result(status)
-
-        for note in personal_notes:
-            logging.info(f"Personal note {note} is on holidays")
-            cls.register_result(note)
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
index 3248becfa4ddeaaaf998794f5398d5845f38d21f..2c1479f73a0de45c1c687fea455514e3ba5af93a 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
@@ -288,6 +288,25 @@ export default {
               <span v-t="'actions.back_to_overview'" />
             </v-tooltip>
             {{ item.person.fullName }}
+            <v-spacer />
+            <v-tooltip bottom>
+              <template #activator="{ on, attrs }">
+                <v-btn
+                  v-bind="attrs"
+                  v-on="on"
+                  icon
+                  :to="{
+                    name: 'core.personById',
+                    params: {
+                      id: item.person.id,
+                    },
+                  }"
+                >
+                  <v-icon>mdi-open-in-new</v-icon>
+                </v-btn>
+              </template>
+              {{ $t("actions.open_person_page", item.person) }}
+            </v-tooltip>
           </v-card-title>
           <v-card-text>
             <absence-reason-group-select
@@ -301,7 +320,7 @@ export default {
               v-bind="documentationPartProps"
               :loading="loading"
               :disabled="loading"
-              :participation="item"
+              :participations="[item]"
               :value="item.tardiness"
               @input="sendToServer([item], 'tardiness', $event)"
             />
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForGroupTab.vue b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForGroupTab.vue
index e5eb12c62ccadcde90e51fa9b3f24865660bb830..f97e7b7f62b4ccee537ccb11a73bfb976c8c48d3 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForGroupTab.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForGroupTab.vue
@@ -10,11 +10,6 @@
     :show-select="false"
     @items="items = $event"
   >
-    <!--    <template #header.person="header">-->
-    <!--      Hello world-->
-    <!--      {{ header }}-->
-    <!--    </template>-->
-
     <template
       v-for="(extraMark, index) in extraMarks"
       #[`extraMarks.${index}.count`]="{ item }"
@@ -55,11 +50,13 @@
     <template #tardinessCount="{ item }">
       <v-chip dense outlined class="me-2">
         <v-icon left>mdi-chart-line-variant</v-icon>
-        {{ item.tardinessCount }}×
+        {{ $tc("alsijil.personal_notes.times_late", item.tardinessCount) }}
       </v-chip>
-      <v-chip dense outlined>
+      <v-chip dense outlined v-if="item.tardinessSum">
         <v-icon left>mdi-sigma</v-icon>
-        {{ $tc("time.minutes_n", item.tardinessSum, { n: $n(item.tardinessSum) }) }}
+        {{
+          $tc("time.minutes_n", item.tardinessSum, { n: $n(item.tardinessSum) })
+        }}
       </v-chip>
     </template>
 
@@ -67,6 +64,13 @@
       <secondary-action-button
         i18n-key="alsijil.personal_notes.statistics.person_view_details"
         icon-text="mdi-open-in-new"
+        :to="{
+          name: 'alsijil.coursebook_statistics',
+          params: {
+            personId: item.person.id,
+            schoolTermId: schoolTerm.id,
+          },
+        }"
       />
     </template>
   </c-r-u-d-list>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonCard.vue b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonCard.vue
index 751e6b90d15599ac113544b3698a30ac736e80ce..b857a1ba59b7a61263bf2cfc6f2c7ac6e8af102a 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonCard.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonCard.vue
@@ -1,6 +1,9 @@
 <template>
   <v-card>
-    <v-skeleton-loader v-if="$apollo.loading" type="card-heading" />
+    <v-skeleton-loader
+      v-if="$apollo.queries.statistics.loading"
+      type="card-heading"
+    />
     <v-card-title v-else-if="compact">
       {{ $t("alsijil.coursebook.statistics.person_compact.title") }}
       <v-spacer />
@@ -8,11 +11,17 @@
         :icon="true"
         icon-text="mdi-open-in-new"
         i18n-key=""
-        @click="switchToOwnPage"
+        :to="{
+          name: 'alsijil.coursebook_statistics',
+          params: {
+            personId: person.id,
+            schoolTermId: schoolTerm.id,
+          },
+        }"
       />
     </v-card-title>
     <v-card-title v-else>
-      {{ $t("alsijil.coursebook.statistics.person_page.title") }}
+      {{ $t("alsijil.coursebook.statistics.title_plural") }}
     </v-card-title>
 
     <v-card-text>
@@ -26,17 +35,12 @@
           style="grid-area: tardinesses"
           :tardiness-sum="statistics.tardinessSum"
           :tardiness-count="statistics.tardinessCount"
-          :loading="$apollo.loading"
+          :loading="$apollo.queries.statistics.loading"
         />
         <statistics-extra-marks-card
           style="grid-area: extra_marks"
           :extra-marks="statistics.extraMarks"
-          :loading="$apollo.loading"
-        />
-        <statistics-personal-notes-list
-          v-if="compact"
-          style="grid-area: personal_notes"
-          :loading="$apollo.loading"
+          :loading="$apollo.queries.statistics.loading"
         />
       </div>
     </v-card-text>
@@ -49,7 +53,6 @@ import BaseButton from "aleksis.core/components/generic/buttons/BaseButton.vue";
 import StatisticsAbsencesCard from "./StatisticsAbsencesCard.vue";
 import StatisticsTardinessCard from "./StatisticsTardinessCard.vue";
 import StatisticsExtraMarksCard from "./StatisticsExtraMarksCard.vue";
-import StatisticsPersonalNotesList from "./StatisticsPersonalNotesList.vue";
 
 import { statisticsByPerson } from "./statistics.graphql";
 
@@ -61,7 +64,6 @@ export default {
     StatisticsAbsencesCard,
     StatisticsTardinessCard,
     StatisticsExtraMarksCard,
-    StatisticsPersonalNotesList,
   },
   props: {
     compact: {
@@ -96,28 +98,14 @@ export default {
       },
     },
   },
-  methods: {
-    switchToOwnPage() {
-      this.$router.push({
-        name: "alsijil.coursebook_statistics",
-        params: {
-          personId: this.person.id,
-          schoolTermId: this.schoolTerm.id,
-        },
-        // TODO: Add where we came from as get parameter if
-        //       meeting decided that own page.
-      });
-    },
-  },
   computed: {
     gridTemplateAreas() {
       return this.compact
-        ? `"absences tardinesses extra_marks"
-  "personal_notes personal_notes personal_notes"`
+        ? `"absences extra_marks" "tardinesses tardinesses"`
         : `"absences" "tardinesses" "extra_marks"`;
     },
     gridTemplateColumnsNum() {
-      return this.compact ? 3 : 1;
+      return this.compact ? 2 : 1;
     },
   },
 };
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue
index e2f68e81e54072dc8bf90913b4941cae8dcab99d..7df07c24db4ea90f66268135272a5fe8e634b9e4 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue
@@ -1,98 +1,174 @@
 <template>
-  <div class="d-flex" style="gap: 4em">
-    <!-- TODO: header (close, title, print) -->
-    <!-- TODO: flex-grow-1 does a little & flex-shrink-1 does nothing -->
-    <div class="flex-grow-1">
-      <!-- school-term-select -->
-      <school-term-field v-model="schoolTerm" :enable-create="false" />
-      <!-- documentations for person list -->
-      <c-r-u-d-iterator
-        i18n-key="TODO"
-        :gql-query="gqlQuery"
-        :gql-additional-query-args="gqlQueryArgs"
-        :enable-create="false"
-        :enable-edit="false"
-        :elevated="false"
-      >
-        <template #additionalActions>
-          <v-btn-toggle v-model="mode" mandatory color="secondary" rounded dense>
-            <v-btn outlined :value="MODE.PARTICIPATIONS">
-              {{ $t("alsijil.coursebook.absences.absences") }}
+  <fullscreen-dialog-page
+    :fallback-url="{ name: 'core.personById', props: { id: personId } }"
+  >
+    <div class="d-flex" style="gap: 4em">
+      <!-- TODO: header (close, title, print) -->
+      <!-- TODO: flex-grow-1 does a little & flex-shrink-1 does nothing -->
+      <div class="flex-grow-1" style="max-width: 100%">
+        <!-- school-term-select -->
+        <school-term-field v-model="schoolTerm" :enable-create="false" />
+        <!-- documentations for person list -->
+        <c-r-u-d-iterator
+          i18n-key="alsijil.coursebook.statistics"
+          :gql-query="gqlQuery"
+          :gql-additional-query-args="gqlQueryArgs"
+          :enable-create="false"
+          :enable-edit="false"
+          :elevated="false"
+        >
+          <template #additionalActions>
+            <v-btn-toggle
+              v-model="mode"
+              mandatory
+              color="secondary"
+              rounded
+              dense
+            >
+              <v-btn outlined :value="MODE.PARTICIPATIONS">
+                {{ $t("alsijil.coursebook.absences.absences") }}
+              </v-btn>
+              <v-btn outlined :value="MODE.PERSONAL_NOTES">
+                {{ $t("alsijil.personal_notes.personal_notes") }}
+              </v-btn>
+            </v-btn-toggle>
+            <v-btn
+              v-if="$vuetify.breakpoint.mobile"
+              rounded
+              dense
+              outlined
+              text
+              @click="statisticsBottomSheet = !statisticsBottomSheet"
+            >
+              {{ $t("alsijil.personal_notes.statistics.person_page.summary") }}
             </v-btn>
-            <v-btn outlined :value="MODE.PERSONAL_NOTES">
-              {{ $t("alsijil.personal_notes.personal_notes") }}
-            </v-btn>
-          </v-btn-toggle>
-        </template>
-        <template #default="{ items }">
-          <v-list-item v-for="item in items" :key="item.id" ripple>
-            <v-list-item-content>
-              <v-list-item-title>
-                <!-- date & timeslot -->
-                <time :datetime="item.relatedDocumentation.datetimeStart" class="text-no-wrap">
-                  {{ $d($parseISODate(item.relatedDocumentation.datetimeStart), "short") }}
-                </time>
+          </template>
+          <template #default="{ items }">
+            <v-list>
+              <v-list-item v-for="item in items" :key="item.id" ripple>
+                <v-list-item-content>
+                  <v-list-item-title>
+                    <!-- date & timeslot -->
+                    <time
+                      :datetime="item.relatedDocumentation.datetimeStart"
+                      class="text-no-wrap"
+                    >
+                      {{
+                        $d(
+                          $parseISODate(
+                            item.relatedDocumentation.datetimeStart,
+                          ),
+                          "short",
+                        )
+                      }}
+                    </time>
 
-                <time :datetime="item.relatedDocumentation.datetimeStart" class="text-no-wrap">
-                  {{ $d($parseISODate(item.relatedDocumentation.datetimeStart), "shortTime") }}
-                </time>
-                <span>-</span>
-                <time :datetime="item.relatedDocumentation.datetimeEnd" class="text-no-wrap">
-                  {{ $d($parseISODate(item.relatedDocumentation.datetimeEnd), "shortTime") }}
-                </time>
-                <!-- teacher -->
-                <person-chip
-                  v-for="teacher in item.relatedDocumentation.teachers"
-                  :person="teacher"
-                  no-link
-                  small
-                />
-                <!-- group -->
-                <span>
-                  {{ item.groupShortName }}
-                </span>
-                <!-- subject -->
-                <subject-chip :subject="item.relatedDocumentation.subject" small />
-              </v-list-item-title>
-              <v-list-item-subtitle>
-                {{ item.note }}
-              </v-list-item-subtitle>
-            </v-list-item-content>
-            <v-list-item-action>
-              <!-- chips: absences & extraMarks -->
-              <absence-reason-chip
-                v-if="item.absenceReason"
-                :absenceReason="item.absenceReason"
-              />
-              <extra-mark-chip
-                v-if="item.extraMark"
-                :extra-mark="item.extraMark"
-              />
-            </v-list-item-action>
-          </v-list-item>
-          <v-divider></v-divider>
-        </template>
-      </c-r-u-d-iterator>
+                    <time
+                      :datetime="item.relatedDocumentation.datetimeStart"
+                      class="text-no-wrap"
+                    >
+                      {{
+                        $d(
+                          $parseISODate(
+                            item.relatedDocumentation.datetimeStart,
+                          ),
+                          "shortTime",
+                        )
+                      }}
+                    </time>
+                    <span>-</span>
+                    <time
+                      :datetime="item.relatedDocumentation.datetimeEnd"
+                      class="text-no-wrap"
+                    >
+                      {{
+                        $d(
+                          $parseISODate(item.relatedDocumentation.datetimeEnd),
+                          "shortTime",
+                        )
+                      }}
+                    </time>
+                  </v-list-item-title>
+                  <v-list-item-subtitle class="overflow-scroll">
+                    <!-- teacher -->
+                    <person-chip
+                      v-for="teacher in item.relatedDocumentation.teachers"
+                      :key="teacher.id"
+                      :person="teacher"
+                      no-link
+                      small
+                    />
+                    <!-- group -->
+                    <span>
+                      {{ item.groupShortName }}
+                    </span>
+                    <!-- subject -->
+                    <subject-chip
+                      :subject="item.relatedDocumentation.subject"
+                      small
+                    />
+                  </v-list-item-subtitle>
+                </v-list-item-content>
+                <v-list-item-action>
+                  <!-- chips: absences & extraMarks -->
+                  <absence-reason-chip
+                    v-if="item.absenceReason"
+                    :absence-reason="item.absenceReason"
+                  />
+                  <extra-mark-chip
+                    v-if="item.extraMark"
+                    :extra-mark="item.extraMark"
+                  />
+                  <div v-if="item.note">
+                    {{ item.note }}
+                  </div>
+                </v-list-item-action>
+              </v-list-item>
+            </v-list>
+            <v-divider></v-divider>
+          </template>
+        </c-r-u-d-iterator>
+      </div>
+      <statistics-for-person-card
+        v-if="!$vuetify.breakpoint.mobile"
+        class="flex-shrink-1"
+        :compact="false"
+        :person="{ id: personId }"
+        :school-term="{ id: schoolTermId }"
+      />
+      <v-bottom-sheet v-model="statisticsBottomSheet" v-else>
+        <statistics-for-person-card
+          :compact="false"
+          :person="{ id: personId }"
+          :school-term="{ id: schoolTermId }"
+        />
+      </v-bottom-sheet>
     </div>
-    <statistics-for-person-card
-      class="flex-shrink-1"
-      :compact="false"
-      :person="{ id: personId }"
-      :school-term="{ id: schoolTermId }"
-    />
-  </div>
+    <template #actions="{ toolbar }">
+      <!-- TODO: add functionality -->
+      <v-btn v-if="toolbar" icon color="primary" disabled>
+        <v-icon>$print</v-icon>
+      </v-btn>
+      <FabButton v-else icon-text="$print" i18n-key="actions.print" disabled />
+    </template>
+  </fullscreen-dialog-page>
 </template>
 
 <script>
 import AbsenceReasonChip from "aleksis.apps.kolego/components/AbsenceReasonChip.vue";
 import SchoolTermField from "aleksis.core/components/school_term/SchoolTermField.vue";
 import CRUDIterator from "aleksis.core/components/generic/CRUDIterator.vue";
+import FabButton from "aleksis.core/components/generic/buttons/FabButton.vue";
+import FullscreenDialogPage from "aleksis.core/components/generic/dialogs/FullscreenDialogPage.vue";
 import PersonChip from "aleksis.core/components/person/PersonChip.vue";
 import SubjectChip from "aleksis.apps.cursus/components/SubjectChip.vue";
 import StatisticsForPersonCard from "./StatisticsForPersonCard.vue";
 
-import { participationsOfPerson, personalNotesForPerson } from "./statistics.graphql";
-import { DateTime } from "luxon";
+import {
+  participationsOfPerson,
+  personalNotesForPerson,
+  personName,
+} from "./statistics.graphql";
 import ExtraMarkChip from "../../extra_marks/ExtraMarkChip.vue";
 
 const MODE = {
@@ -107,6 +183,8 @@ export default {
     AbsenceReasonChip,
     SchoolTermField,
     CRUDIterator,
+    FabButton,
+    FullscreenDialogPage,
     PersonChip,
     SubjectChip,
     StatisticsForPersonCard,
@@ -122,9 +200,27 @@ export default {
       required: true,
     },
   },
+  apollo: {
+    personName: {
+      query: personName,
+      variables() {
+        return {
+          person: this.personId,
+        };
+      },
+      result({ data }) {
+        this.$setToolBarTitle(
+          this.$t("alsijil.coursebook.statistics.person_page.title", {
+            fullName: data.personName.fullName || "???",
+          }),
+        );
+      },
+    },
+  },
   data() {
     return {
       mode: MODE.PARTICIPATIONS,
+      statisticsBottomSheet: false,
     };
   },
   computed: {
@@ -143,12 +239,14 @@ export default {
       },
       set(value) {
         console.log("New SchoolTerm:", value);
-      }
+      },
     },
   },
   methods: {
     gqlQuery() {
-      return this.mode === MODE.PERSONAL_NOTES ? personalNotesForPerson : participationsOfPerson;
+      return this.mode === MODE.PERSONAL_NOTES
+        ? personalNotesForPerson
+        : participationsOfPerson;
     },
   },
 };
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsPersonalNotesList.vue b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsPersonalNotesList.vue
deleted file mode 100644
index 5bb820d8c0a7d2b24d6ce7c7754cf51c1c1bb310..0000000000000000000000000000000000000000
--- a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsPersonalNotesList.vue
+++ /dev/null
@@ -1,54 +0,0 @@
-<template>
-  <v-skeleton-loader v-if="loading" type="card" />
-  <v-card v-else>
-    <v-virtual-scroll :items="personalNotes" height="150" item-height="75">
-      <template v-slot:default="{ item }">
-        <v-list-item :key="item">
-          <v-list-item-content>
-            <v-list-item-title class="d-flex">
-              <!-- new_personal_note.documentation.course.groups.FORALL.shortName -->
-              <div>5a</div>
-              <!-- new_personal_note.documentation.subject/amends.subject -->
-              <!-- TODO: In subject-chip -->
-              <div>Ma</div>
-              <v-spacer />
-              <!-- new_personal_note.documentation.datetimeStart.toDate() -->
-              <div>
-                <v-list-item-subtitle> 01.01.2031 </v-list-item-subtitle>
-              </div>
-            </v-list-item-title>
-            <v-list-item-subtitle>
-              <!-- new_personal_note.note -->
-              Hier hat der Lehrer was notiert.
-            </v-list-item-subtitle>
-          </v-list-item-content>
-        </v-list-item>
-        <v-divider></v-divider>
-      </template>
-    </v-virtual-scroll>
-  </v-card>
-</template>
-
-<script>
-export default {
-  name: "StatisticsPersonalNotesList",
-  props: {
-    loading: {
-      type: Boolean,
-      required: false,
-      default: false,
-    },
-  },
-  data() {
-    return {
-      personalNotes: [1, 2, 3, 4],
-    };
-  },
-  // Mock for personalNotesQuery
-  // apollo: {
-  //   personalNotes: {
-  //     query: XXXX,
-  //   },
-  // },
-};
-</script>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsTardinessCard.vue b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsTardinessCard.vue
index 275515f0f1b822d9b1da1dfe5fcbef264e242e6f..3103208fb39190ccb61096bfc4940a53d7271be7 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsTardinessCard.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsTardinessCard.vue
@@ -1,28 +1,36 @@
 <template>
   <v-skeleton-loader v-if="loading" type="card" />
-  <v-card v-else class="text-center">
-    <v-card-text
-      class="d-flex flex-column align-center justify-center fill-height"
-    >
-      <div class="text-h2">
-        {{ $n(tardinessCount) }}
-      </div>
-      <div class="text-subtitle-2">
-        {{ $tc("alsijil.personal_notes.tardiness_n", tardinessCount) }}
-      </div>
-      <div class="text-caption">
-        <!-- TODO: Show average tardiness instead of sum like mock-up? -->
-        <div>
-          <v-icon small>mdi-sigma</v-icon>
-          {{ $tc("time.minutes_n", tardinessSum, { n: $n(tardinessSum) }) }}
-        </div>
-        <div>
-          <v-icon small>mdi-diameter-variant</v-icon>
-          {{
-            $tc("time.minutes_n", tardinessSum / tardinessCount, { n: $n(tardinessSum/tardinessCount) })
-          }}
-        </div>
-      </div>
+  <v-card v-else>
+    <v-card-text>
+      <v-row>
+        <v-col class="text-center">
+          <div class="text-h2">
+            {{ $n(tardinessCount) }}
+          </div>
+          <div class="text-subtitle-2">
+            {{ $tc("alsijil.personal_notes.tardiness_n", tardinessCount) }}
+          </div>
+        </v-col>
+
+        <v-col
+          class="text-caption d-flex flex-column justify-center align-center"
+        >
+          <div>
+            <div>
+              <v-icon small>mdi-sigma</v-icon>
+              {{ $tc("time.minutes_n", tardinessSum, { n: $n(tardinessSum) }) }}
+            </div>
+            <div>
+              <v-icon small>mdi-diameter-variant</v-icon>
+              {{
+                $tc("time.minutes_n", tardinessSum / tardinessCount, {
+                  n: $n(tardinessSum / tardinessCount),
+                })
+              }}
+            </div>
+          </div>
+        </v-col>
+      </v-row>
     </v-card-text>
   </v-card>
 </template>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/statistics.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/statistics.graphql
index 38d8817b83e1a673f38d26352a682a407e5b3aa3..68e0a08ca3804bf841fc25c57c99d4e4dc3f66ee 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/statistics.graphql
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/statistics.graphql
@@ -110,3 +110,10 @@ query statisticsByGroup($group: ID!, $term: ID) {
     #    }
   }
 }
+
+query personName($person: ID!) {
+  personName: personById(id: $person) {
+    id
+    fullName
+  }
+}
diff --git a/aleksis/apps/alsijil/frontend/components/injectables/group_actions/OpenCoursebook.vue b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/OpenCoursebook.vue
new file mode 100644
index 0000000000000000000000000000000000000000..c52dedfc1f94497ba9a82dbd342a5b47dead2821
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/OpenCoursebook.vue
@@ -0,0 +1,30 @@
+<script>
+import groupActionsMixin from "aleksis.core/components/group/actions/groupActionsMixin.js";
+export default {
+  name: "OpenCoursebook",
+  mixins: [groupActionsMixin],
+};
+</script>
+
+<template>
+  <v-list-item
+    :to="{
+      name: 'alsijil.coursebook',
+      params: {
+        filterType: 'all',
+        pageType: 'documentations',
+        objType: 'group',
+        objId: group.id,
+      },
+    }"
+  >
+    <v-list-item-icon>
+      <v-icon>mdi-book-education-outline</v-icon>
+    </v-list-item-icon>
+    <v-list-item-content>
+      <v-list-item-title>
+        {{ $t("actions.open_in_coursebook") }}
+      </v-list-item-title>
+    </v-list-item-content>
+  </v-list-item>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/components/injectables/group_actions/ShowAbsences.vue b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/ShowAbsences.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e582968e3c84d2e5afbd334233ee93138ef98540
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/injectables/group_actions/ShowAbsences.vue
@@ -0,0 +1,30 @@
+<script>
+import groupActionsMixin from "aleksis.core/components/group/actions/groupActionsMixin.js";
+export default {
+  name: "ShowAbsences",
+  mixins: [groupActionsMixin],
+};
+</script>
+
+<template>
+  <v-list-item
+    :to="{
+      name: 'alsijil.coursebook',
+      params: {
+        filterType: 'all',
+        pageType: 'absences',
+        objType: 'group',
+        objId: group.id,
+      },
+    }"
+  >
+    <v-list-item-icon>
+      <v-icon>mdi-account-details-outline</v-icon>
+    </v-list-item-icon>
+    <v-list-item-content>
+      <v-list-item-title>
+        {{ $t("actions.show_absences") }}
+      </v-list-item-title>
+    </v-list-item-content>
+  </v-list-item>
+</template>
diff --git a/aleksis/apps/alsijil/frontend/index.js b/aleksis/apps/alsijil/frontend/index.js
index 03b8dec4843fa8b3a1f123399b6f3eb86ed5d058..6e16ad0a1873ad9be17ee842283c3283bd74e8b6 100644
--- a/aleksis/apps/alsijil/frontend/index.js
+++ b/aleksis/apps/alsijil/frontend/index.js
@@ -2,6 +2,20 @@ import { hasPersonValidator } from "aleksis.core/routeValidators";
 import { DateTime } from "luxon";
 
 export const collectionItems = {
+  coreGroupActions: [
+    {
+      key: "alsijil-open-coursebook",
+      component: () =>
+        import("./components/injectables/group_actions/OpenCoursebook.vue"),
+      isActive: () => true,
+    },
+    {
+      key: "alsijil-show-absences",
+      component: () =>
+        import("./components/injectables/group_actions/ShowAbsences.vue"),
+      isActive: () => true,
+    },
+  ],
   coreGroupOverview: [
     {
       tab: {
diff --git a/aleksis/apps/alsijil/frontend/messages/de.json b/aleksis/apps/alsijil/frontend/messages/de.json
index facb3b725571374274276671bdbcc3abbe32ebeb..8464a348cae51618341441d18639e00cbea0a066 100644
--- a/aleksis/apps/alsijil/frontend/messages/de.json
+++ b/aleksis/apps/alsijil/frontend/messages/de.json
@@ -1,6 +1,7 @@
 {
   "actions": {
-    "back_to_overview": "Zurück zur Übersicht"
+    "back_to_overview": "Zurück zur Übersicht",
+    "open_person_page": "Detailansicht für {fullName} aufrufen"
   },
   "alsijil": {
     "absence": {
@@ -95,7 +96,8 @@
           "title": "Kursbuch · Statistiken"
         },
         "person_page": {
-          "title": "Statistiken"
+          "title": "Kursbuch · Statistiken · {fullName}",
+          "summary": "Zusammenfassung"
         }
       },
       "print": {
@@ -152,6 +154,7 @@
       "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)",
+      "times_late": "nie | 1× | {n}×",
       "note": "Notiz",
       "tardiness": "Verspätung",
       "tardiness_plural": "Verspätungen",
diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json
index f9d685a6fc8d31f5c5047c7668bc34f65b978b73..4b273e3a7374637ae796c7523a270f4aff4fd4bb 100644
--- a/aleksis/apps/alsijil/frontend/messages/en.json
+++ b/aleksis/apps/alsijil/frontend/messages/en.json
@@ -155,6 +155,7 @@
       "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)",
+      "times_late": "never | 1× | {n}×",
       "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.",
@@ -165,15 +166,20 @@
           "title": "Coursebook · Statistics"
         },
         "person_page": {
-          "title": "Statistics"
+          "title": "Coursebook · Statistics · {fullName}",
+          "summary": "Summary"
         },
-        "person_view_details": "Details"
+        "person_view_details": "Details",
+        "title_plural": "Statistics"
       },
       "personal_notes": "Personal Notes"
     }
   },
   "actions": {
-    "back_to_overview": "Back to overview"
+    "back_to_overview": "Back to overview",
+    "open_person_page": "Open detail view for {fullName}",
+    "open_in_coursebook": "View Coursebook",
+    "show_absences": "Open absence list"
   },
   "time": {
     "minutes": "minutes",
diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py
index a43e54b952f13525b80dd51fe0c6f96bcf5d93de..4ad59b387532598ac392795a143e2acf90717fdb 100644
--- a/aleksis/apps/alsijil/model_extensions.py
+++ b/aleksis/apps/alsijil/model_extensions.py
@@ -57,7 +57,7 @@ def annotate_person_statistics(
             filter=Q(filtered_participation_statuses__absence_reason__count_as_absent=True),
             distinct=True,
         ),
-        tardiness_sum=Sum("filtered_participation_statuses__tardiness"),
+        tardiness_sum=Sum("filtered_participation_statuses__tardiness", distinct=True),
         tardiness_count=Count(
             "filtered_participation_statuses",
             filter=Q(filtered_participation_statuses__tardiness__gt=0),
@@ -115,5 +115,13 @@ def annotate_person_statistics_for_school_term(
         datetime_end__date__lte=school_term.date_end,
     )
     if group:
-        documentations.filter(Q(course__groups=group) | Q(course__groups__parent_groups=group))
+        documentations = documentations.filter(
+            pk__in=Documentation.objects.filter(course__groups=group)
+            .values_list("pk", flat=True)
+            .union(
+                Documentation.objects.filter(course__groups__parent_groups=group).values_list(
+                    "pk", flat=True
+                )
+            )
+        )
     return annotate_person_statistics_from_documentations(persons, documentations)
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index b39b0d4b9a8ddadd23bcd7548ae178951d7ed0c5..48c7d1aca9d177fd54f21e844684aad25655f352 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -4,8 +4,7 @@ from typing import Optional, List
 from django.contrib.auth.models import User
 from django.core.exceptions import PermissionDenied
 from django.db import models
-from django.db.models import QuerySet
-from django.db.models.query_utils import Q
+from django.db.models import Q, QuerySet
 from django.http import HttpRequest
 from django.urls import reverse
 from django.utils import timezone
@@ -175,22 +174,11 @@ class Documentation(CalendarEvent):
 
             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]
-                        )
+                if (incomplete and doc.topic) or (
+                    absences_exist
+                    and (
+                        not doc.participations.all()
+                        or not [d for d in doc.participations.all() if d.absence_reason]
                     )
                 ):
                     continue
@@ -441,20 +429,28 @@ class ParticipationStatus(CalendarEvent):
 
     @classmethod
     def get_objects(
-        cls, request: HttpRequest | None = None, params: dict[str, any] | None = None, **kwargs
+        cls,
+        request: HttpRequest | None = None,
+        params: dict[str, any] | None = None,
+        additional_filter: Q | None = None,
+        **kwargs,
     ) -> QuerySet:
-        qs = (
-            super()
-            .get_objects(request, params, **kwargs)
-            .select_related("person", "absence_reason")
-        )
+        q = additional_filter or Q()
         if params:
             if params.get("person"):
-                qs = qs.filter(person=params["person"])
+                q = q & Q(person=params["person"])
             elif params.get("persons"):
-                qs = qs.filter(person__in=params["persons"])
+                q = q & Q(person__in=params["persons"])
             elif params.get("group"):
-                qs = qs.filter(groups_of_person__in=params.get("group"))
+                q = q & Q(groups_of_person__in=params.get("group"))
+        qs = super().get_objects(
+            request,
+            params,
+            additional_filter=q,
+            select_related=["person", "absence_reason"],
+            **kwargs,
+        )
+
         return qs
 
     @classmethod
diff --git a/aleksis/apps/alsijil/preferences.py b/aleksis/apps/alsijil/preferences.py
index b0d8fbc42c9227e32c227bf3ac10ef9c1635fba6..0f3b25b24b70a5823830574cf724bc7cc8cfc9f6 100644
--- a/aleksis/apps/alsijil/preferences.py
+++ b/aleksis/apps/alsijil/preferences.py
@@ -14,6 +14,19 @@ from aleksis.core.registries import site_preferences_registry
 alsijil = Section("alsijil", verbose_name=_("Class register"))
 
 
+@site_preferences_registry.register
+class InheritPrivilegesFromParentGroup(BooleanPreference):
+    section = alsijil
+    name = "inherit_privileges_from_parent_group"
+    default = True
+    verbose_name = _(
+        "Grant the owner of a parent group the same privileges "
+        "as the owners of the respective child groups "
+        "in regard to group role management and generating "
+        "full printouts of class registers."
+    )
+
+
 @site_preferences_registry.register
 class GroupOwnersCanAssignRolesToParents(BooleanPreference):
     section = alsijil
@@ -64,6 +77,19 @@ class GroupTypesRegisterAbsence(ModelMultipleChoicePreference):
     )
 
 
+@site_preferences_registry.register
+class GroupTypesViewPersonStatistics(ModelMultipleChoicePreference):
+    section = alsijil
+    name = "group_types_view_person_statistics"
+    required = False
+    default = []
+    model = GroupType
+    verbose_name = _(
+        "User is allowed to view coursebook statistics for members "
+        "of groups the user is an owner of with these group types"
+    )
+
+
 @site_preferences_registry.register
 class GroupTypePriorityCoursebook(ModelChoicePreference):
     section = alsijil
diff --git a/aleksis/apps/alsijil/rules.py b/aleksis/apps/alsijil/rules.py
index 602663b519a0680ca88de3813ecbc38df64579a8..05466938856daa44b3d38933894f12583b8ba301 100644
--- a/aleksis/apps/alsijil/rules.py
+++ b/aleksis/apps/alsijil/rules.py
@@ -16,10 +16,10 @@ from .util.predicates import (
     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,
+    can_view_statistics_for_person,
     has_person_group_object_perm,
     is_course_group_owner,
     is_course_member,
@@ -165,9 +165,7 @@ view_documentations_for_group_predicate = has_person & (
 )
 add_perm("alsijil.view_documentations_for_group_rule", view_documentations_for_group_predicate)
 
-view_documentations_menu_predicate = has_person & (
-    has_global_perm("alsijil.view_documentation") | can_view_any_documentation
-)
+view_documentations_menu_predicate = has_person
 add_perm("alsijil.view_documentations_menu_rule", view_documentations_menu_predicate)
 
 view_documentations_for_teacher_predicate = has_person & (
@@ -241,6 +239,24 @@ add_perm(
     edit_personal_note_predicate,
 )
 
+view_group_statistics_predicate = has_person & (
+    has_global_perm("alsijil.view_participationstatus") | is_group_owner
+)
+add_perm(
+    "alsijil.view_group_statistics_rule",
+    view_group_statistics_predicate,
+)
+
+view_person_statistics_predicate = has_person & (
+    is_current_person
+    | has_global_perm("alsijil.view_participationstatus")
+    | can_view_statistics_for_person
+)
+add_perm(
+    "alsijil.view_person_statistics_rule",
+    view_person_statistics_predicate,
+)
+
 # View parent menu entry
 view_menu_predicate = has_person & (view_documentations_menu_predicate | view_extramarks_predicate)
 add_perm(
diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py
index ab2df1d2d6a63a116ce1bc53038b0ce049c2f75d..740aa1ff271e9e4af374f89c7b96c0e57c06a842 100644
--- a/aleksis/apps/alsijil/schema/__init__.py
+++ b/aleksis/apps/alsijil/schema/__init__.py
@@ -1,6 +1,5 @@
 from datetime import datetime
 
-from django.core.exceptions import PermissionDenied
 from django.db.models import BooleanField, ExpressionWrapper, Q
 
 import graphene
@@ -17,7 +16,7 @@ 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, ParticipationStatus, NewPersonalNote
+from ..models import Documentation, NewPersonalNote, ParticipationStatus
 from .absences import (
     AbsencesForPersonsCreateMutation,
 )
@@ -101,7 +100,13 @@ class Query(graphene.ObjectType):
 
     def resolve_documentations_by_course_id(root, info, course_id, **kwargs):
         documentations = Documentation.objects.filter(
-            Q(course__pk=course_id) | Q(amends__course__pk=course_id)
+            pk__in=Documentation.objects.filter(course_id=course_id)
+            .values_list("id", flat=True)
+            .union(
+                Documentation.objects.filter(amends__course_id=course_id).values_list(
+                    "id", flat=True
+                )
+            )
         )
         return documentations
 
@@ -137,7 +142,7 @@ class Query(graphene.ObjectType):
                 )
             )
         ):
-            raise PermissionDenied()
+            return []
 
         # Find all LessonEvents for all Lessons of this Course in this date range
         event_params = {
@@ -175,16 +180,22 @@ class Query(graphene.ObjectType):
         if person:
             person = Person.objects.get(pk=person)
             if not info.context.user.has_perm("core.view_person_rule", person):
-                raise PermissionDenied()
+                return []
         elif has_person(info.context.user):
             person = info.context.user.person
         else:
-            raise PermissionDenied()
+            return []
 
         return (
             Group.objects.for_current_school_term_or_all()
-            .filter(Q(members=person) | Q(owners=person) | Q(parent_groups__owners=person))
-            .distinct()
+            .filter(
+                pk__in=Group.objects.filter(members=person)
+                .values_list("id", flat=True)
+                .union(Group.objects.filter(owners=person).values_list("id", flat=True))
+                .union(
+                    Group.objects.filter(parent_groups__owners=person).values_list("id", flat=True)
+                )
+            )
             .annotate(
                 is_priority=ExpressionWrapper(
                     Q(group_type=get_site_preferences()["alsijil__group_type_priority_coursebook"]),
@@ -199,20 +210,24 @@ class Query(graphene.ObjectType):
         if person:
             person = Person.objects.get(pk=person)
             if not info.context.user.has_perm("core.view_person_rule", person):
-                raise PermissionDenied()
+                return []
         elif has_person(info.context.user):
             person = info.context.user.person
         else:
-            raise PermissionDenied()
+            return []
         return Course.objects.filter(
-            (
-                Q(teachers=person)
-                | Q(groups__members=person)
-                | Q(groups__owners=person)
-                | Q(groups__parent_groups__owners=person)
+            pk__in=(
+                Course.objects.filter(teachers=person)
+                .values_list("id", flat=True)
+                .union(Course.objects.filter(groups__members=person).values_list("id", flat=True))
+                .union(Course.objects.filter(groups__owners=person).values_list("id", flat=True))
+                .union(
+                    Course.objects.filter(groups__parent_groups__owners=person).values_list(
+                        "id", flat=True
+                    )
+                )
             )
-            & Q(groups__in=Group.objects.for_current_school_term_or_all())
-        ).distinct()
+        ).filter(groups__in=Group.objects.for_current_school_term_or_all())
 
     @staticmethod
     def resolve_absence_creation_persons(root, info, **kwargs):
@@ -259,28 +274,51 @@ class Query(graphene.ObjectType):
 
     @staticmethod
     def resolve_statistics_by_person(root, info, person, term):
+        person = Person.objects.get(pk=person)
+        if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
+            return None
         school_term = SchoolTerm.objects.get(id=term)
         return annotate_person_statistics_for_school_term(
-            Person.objects.filter(id=person), school_term
+            Person.objects.filter(id=person.id), school_term
         ).first()
 
     @staticmethod
     def resolve_participations_of_person(root, info, person, term=None):
-        # TODO: only current term
-        return ParticipationStatus.objects.filter(person=person, absence_reason__isnull=False)
+        person = Person.objects.get(pk=person)
+        if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
+            return []
+        school_term = SchoolTerm.objects.get(id=term)
+        return ParticipationStatus.objects.filter(
+            person=person,
+            absence_reason__isnull=False,
+            datetime_start__date__gte=school_term.date_start,
+            datetime_end__date__lte=school_term.date_end,
+        ).order_by("-related_documentation__datetime_start")
 
     @staticmethod
     def resolve_personal_notes_for_person(root, info, person, term=None):
-        # TODO: only current term
-        return NewPersonalNote.objects.filter(person=person)
+        person = Person.objects.get(pk=person)
+        if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
+            return []
+        school_term = SchoolTerm.objects.get(id=term)
+        return NewPersonalNote.objects.filter(
+            person=person,
+            documentation__in=Documentation.objects.filter(
+                datetime_start__date__gte=school_term.date_start,
+                datetime_end__date__lte=school_term.date_end,
+            ),
+        ).order_by("-documentation__datetime_start")
 
     @staticmethod
     def resolve_statistics_by_group(root, info, group, term=None):
+        group = Group.objects.get(pk=group)
+        if not info.context.user.has_perm("alsijil.view_group_statistics_rule", group):
+            return []
         school_term = (
             SchoolTerm.objects.get(id=term) if term is not None else SchoolTerm.get_current()
         )
 
-        members = Group.objects.get(id=group).members.all()
+        members = group.members.all()
         return annotate_person_statistics_for_school_term(members, school_term, group=group)
 
 
diff --git a/aleksis/apps/alsijil/schema/documentation.py b/aleksis/apps/alsijil/schema/documentation.py
index cdb2eaca122330f70bd75023a463f2dc1dc0e8e7..b833de336b2a2add64ac5f0e0cf6dd7fdd8ae0c5 100644
--- a/aleksis/apps/alsijil/schema/documentation.py
+++ b/aleksis/apps/alsijil/schema/documentation.py
@@ -108,7 +108,9 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
             "alsijil.view_participation_status_for_documentation_rule", root
         ):
             if has_person(info.context.user):
-                return root.participations.filter(person=info.context.user.person)
+                return [
+                    p for p in root.participations.all() if p.person == info.context.user.person
+                ]
             return []
         return root.participations.all()
 
diff --git a/aleksis/apps/alsijil/schema/participation_status.py b/aleksis/apps/alsijil/schema/participation_status.py
index fcf15df815619fcc808770116347458d8f7daf02..22e5820594994b2d1bb4c81ae4f5a83f615bea0b 100644
--- a/aleksis/apps/alsijil/schema/participation_status.py
+++ b/aleksis/apps/alsijil/schema/participation_status.py
@@ -119,9 +119,9 @@ class ExtendParticipationStatusToAbsenceBatchMutation(graphene.Mutation):
             return participation, absence
 
         else:
-            # No base absence, simply create one
+            # No base absence, simply create one if absence reason is given
             data = dict(
-                reason_id=participation.absence_reason.id,
+                reason_id=participation.absence_reason.id if participation.absence_reason else None,
                 person=participation.person,
             )
 
diff --git a/aleksis/apps/alsijil/schema/personal_note.py b/aleksis/apps/alsijil/schema/personal_note.py
index dfe36359d87345a0b51992e689627065638fcd4e..e37e5f0580add9255c460a7089d1e877247273eb 100644
--- a/aleksis/apps/alsijil/schema/personal_note.py
+++ b/aleksis/apps/alsijil/schema/personal_note.py
@@ -23,6 +23,8 @@ class PersonalNoteType(
             "id",
             "note",
             "extra_mark",
+            # TODO: permissions?
+            "documentation",
         )
 
 
diff --git a/aleksis/apps/alsijil/schema/statistics.py b/aleksis/apps/alsijil/schema/statistics.py
index 976f25ebb32b50c9152fb86d23acf17011d7a89f..091afdae8b705d660413fe53b6b35328459a16cb 100644
--- a/aleksis/apps/alsijil/schema/statistics.py
+++ b/aleksis/apps/alsijil/schema/statistics.py
@@ -1,9 +1,5 @@
-from django.utils import timezone
-
 import graphene
 
-from aleksis.apps.cursus.models import Subject
-from aleksis.apps.cursus.schema import SubjectType
 from aleksis.apps.kolego.models.absence import AbsenceReason
 from aleksis.apps.kolego.schema.absence import AbsenceReasonType
 from aleksis.core.models import Person
diff --git a/aleksis/apps/alsijil/util/predicates.py b/aleksis/apps/alsijil/util/predicates.py
index 2a27bca7e2260a13d12199027d3501add3494b60..7177034281562b2aae4d9983f70def95f4a1c75a 100644
--- a/aleksis/apps/alsijil/util/predicates.py
+++ b/aleksis/apps/alsijil/util/predicates.py
@@ -365,3 +365,13 @@ def can_edit_personal_note(user: User, obj: NewPersonalNote):
                 user, obj.documentation.amends
             ) | is_lesson_event_group_owner(user, obj.documentation.amends)
     return False
+
+
+@predicate
+def can_view_statistics_for_person(user: User, obj: Person) -> bool:
+    """Predicate for registering absence for person."""
+    group_types = get_site_preferences()["alsijil__group_types_view_person_statistics"]
+    qs = obj.member_of.filter(owners=user.person)
+    if not group_types.exists():
+        return False
+    return qs.filter(group_type__in=group_types).exists()
diff --git a/pyproject.toml b/pyproject.toml
index e0880f83bec1e0011e9b79f84567558598f90300..2f82bc3ed9291cd3d7a9b04d725ff6b67216942b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "AlekSIS-App-Alsijil"
-version = "4.0.0.dev7"
+version = "4.0.0.dev8"
 packages = [
     { include = "aleksis" }
 ]
@@ -51,7 +51,6 @@ priority = "supplemental"
 python = "^3.10"
 aleksis-core = "^4.0.0.dev11"
 aleksis-app-chronos = "^4.0.0.dev7"
-aleksis-app-stoelindeling = { version = "^3.0.dev1", optional = true }
 aleksis-app-kolego = "^0.1.0.dev3"
 
 [tool.poetry.extras]