diff --git a/.coverage b/.coverage
new file mode 100644
index 0000000000000000000000000000000000000000..ab74d43bf595f02267eb612f3a4a14a6682b7f19
Binary files /dev/null and b/.coverage differ
diff --git a/aleksis/apps/chronos/frontend/components/AmendLesson.vue b/aleksis/apps/chronos/frontend/components/AmendLesson.vue
new file mode 100644
index 0000000000000000000000000000000000000000..587ccdf093bdab1eae7bc197954607fdb56ad18e
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/AmendLesson.vue
@@ -0,0 +1,193 @@
+<template>
+  <v-card-actions v-if="checkPermission('chronos.edit_substitution_rule')">
+    <edit-button
+      i18n-key="chronos.event.amend.edit_button"
+      @click="edit = true"
+      />
+    <delete-button
+      v-if="selectedEvent.meta.amended"
+      i18n-key="chronos.event.amend.delete_button"
+      @click="deleteEvent = true"
+      />
+    <dialog-object-form
+      v-model="edit"
+      :fields="fields"
+      :is-create="!selectedEvent.meta.amended"
+      createItemI18nKey="chronos.event.amend.title"
+      :gql-create-mutation="gqlCreateMutation"
+      :get-create-data="transformCreateData"
+      :default-item="defaultItem"
+      editItemI18nKey="chronos.event.amend.title"
+      :gql-patch-mutation="gqlPatchMutation"
+      :get-patch-data="transformPatchData"
+      :edit-item="initPatchData"
+      @cancel="open = false"
+      @save="updateOnSave()"
+      >
+      <template #subject.field="{ attrs, on, item }">
+        <v-autocomplete
+          :disabled="item.cancelled"
+          :items="amendableSubjects"
+          item-text="name"
+          item-value="id"
+          v-bind="attrs"
+          v-on="on"
+          />
+      </template>
+      <template #teachers.field="{ attrs, on, item }">
+        <v-autocomplete
+          :disabled="item.cancelled"
+          multiple
+          :items="amendableTeachers"
+          item-text="fullName"
+          item-value="id"
+          v-bind="attrs"
+          v-on="on"
+          chips
+          deletable-chips
+          />
+      </template>
+      <template #rooms.field="{ attrs, on, item }">
+        <v-autocomplete
+          :disabled="item.cancelled"
+          multiple
+          :items="amendableRooms"
+          item-text="name"
+          item-value="id"
+          v-bind="attrs"
+          v-on="on"
+          chips
+          deletable-chips
+          />
+      </template>
+      <template #cancelled.field="{ attrs, on }">
+        <v-checkbox v-bind="attrs" v-on="on" />
+      </template>
+      <template #comment.field="{ attrs, on }">
+        <v-textarea v-bind="attrs" v-on="on" />
+      </template>
+    </dialog-object-form>
+    <delete-dialog
+      deleteSuccessMessageI18nKey="chronos.event.amend.delete_success"
+      :gql-mutation="gqlDeleteMutation"
+      v-model="deleteEvent"
+      :item="selectedEvent.meta"
+      @success="updateOnSave()"
+      >
+      <template #title>
+        {{ $t("chronos.event.amend.delete_dialog") }}
+      </template>
+    </delete-dialog>
+  </v-card-actions>
+</template>
+
+<script>
+import permissionsMixin from "aleksis.core/mixins/permissions.js";
+import EditButton from "aleksis.core/components/generic/buttons/EditButton.vue";
+import DialogObjectForm from "aleksis.core/components/generic/dialogs/DialogObjectForm.vue";
+import DeleteButton from "aleksis.core/components/generic/buttons/DeleteButton.vue";
+import DeleteDialog from "aleksis.core/components/generic/dialogs/DeleteDialog.vue";
+import {
+  gqlSubjects,
+  gqlPersons,
+  gqlRooms,
+  createAmendLesson,
+  patchAmendLesson,
+  deleteAmendLesson,
+} from "./amendLesson.graphql";
+
+export default {
+  name: "AmendLesson",
+  components: {
+    EditButton,
+    DialogObjectForm,
+    DeleteButton,
+    DeleteDialog,
+  },
+  mixins: [permissionsMixin],
+  props: {
+    selectedEvent: {
+      type: Object,
+      required: true,
+    }
+  },
+  data() {
+    return {
+      edit: false,
+      fields: [
+        {
+          text: this.$t("chronos.event.amend.subject"),
+          value: "subject",
+        },
+        {
+          text: this.$t("chronos.event.amend.teachers"),
+          value: "teachers",
+        },
+        {
+          text: this.$t("chronos.event.amend.rooms"),
+          value: "rooms",
+        },
+        {
+          text: this.$t("chronos.event.amend.cancelled"),
+          value: "cancelled",
+        },
+        {
+          text: this.$t("chronos.event.amend.comment"),
+          value: "comment",
+        },
+      ],
+      defaultItem: {
+        cancelled: this.selectedEvent.meta.cancelled,
+        comment: this.selectedEvent.meta.comment,
+      },
+      gqlCreateMutation: createAmendLesson,
+      gqlPatchMutation: patchAmendLesson,
+      deleteEvent: false,
+      gqlDeleteMutation: deleteAmendLesson,
+    };
+  },
+  methods: {
+    transformCreateData(item) {
+      return {
+        ...item,
+        amends: this.selectedEvent.meta.id,
+        datetimeStart: this.selectedEvent.startDateTime.toUTC().toISO(),
+        datetimeEnd: this.selectedEvent.endDateTime.toUTC().toISO(),
+      };
+    },
+    transformPatchData(item) {
+      let { id, __typename, cancelled, ...patchItem } = item;
+      return {
+        ...patchItem,
+        // Normalize cancelled, v-checkbox returns null & does not
+        // honor false-value.
+        cancelled: cancelled ? true : false,
+      };
+    },
+    updateOnSave() {
+      this.$emit('refreshCalendar');
+      this.model = false;
+    },
+  },
+  computed: {
+    initPatchData() {
+      return {
+        id: this.selectedEvent.meta.id,
+        subject: this.selectedEvent.meta.subject?.id.toString(),
+        teachers: this.selectedEvent.meta.teachers.map((teacher) => teacher.id.toString()),
+        rooms: this.selectedEvent.meta.rooms.map((room) => room.id.toString()),
+        cancelled: this.selectedEvent.meta.cancelled,
+        comment: this.selectedEvent.meta.comment,
+      };
+    },
+  },
+  apollo: {
+    amendableSubjects: gqlSubjects,
+    amendableTeachers: gqlPersons,
+    amendableRooms: gqlRooms,
+  },
+  mounted() {
+    this.addPermissions(["chronos.edit_substitution_rule"]);
+  },
+};
+</script>
diff --git a/aleksis/apps/chronos/frontend/components/LessonEventLinkIterator.vue b/aleksis/apps/chronos/frontend/components/LessonEventLinkIterator.vue
new file mode 100644
index 0000000000000000000000000000000000000000..a6a737298d496c3a8b7acdcd7fe4d485c77cace2
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/LessonEventLinkIterator.vue
@@ -0,0 +1,25 @@
+<script>
+export default {
+  name: "LessonEventLinkIterator",
+  props: {
+    items: {
+      type: Array,
+      required: true,
+    },
+    attr: {
+      type: String,
+      required: false,
+      default: "name",
+    },
+  },
+};
+</script>
+
+<template>
+  <span v-bind="$attrs">
+    <span v-for="(item, idx) in items" :key="idx">
+      <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
+      {{ item[attr] }}{{ idx + 1 < items.length ? "," : "" }}
+    </span>
+  </span>
+</template>
diff --git a/aleksis/apps/chronos/frontend/components/LessonEventOldNew.vue b/aleksis/apps/chronos/frontend/components/LessonEventOldNew.vue
new file mode 100644
index 0000000000000000000000000000000000000000..774dfb8aa3ce47a76da45d0dfc43d3b31208e32e
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/LessonEventOldNew.vue
@@ -0,0 +1,42 @@
+<script>
+import LessonEventLinkIterator from "./LessonEventLinkIterator.vue";
+
+export default {
+  name: "LessonEventOldNew",
+  components: { LessonEventLinkIterator },
+  props: {
+    oldItems: {
+      type: Array,
+      required: true,
+    },
+    newItems: {
+      type: Array,
+      required: true,
+    },
+    attr: {
+      type: String,
+      required: false,
+      default: "name",
+    },
+  },
+};
+</script>
+
+<template>
+  <span v-bind="$attrs">
+    <span v-if="oldItems.length > 0 && newItems.length > 0">
+      <span class="text-decoration-line-through"
+        ><lesson-event-link-iterator :items="oldItems" :attr="attr"
+      /></span>
+      <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
+      <span> → </span>
+      <lesson-event-link-iterator :items="newItems" :attr="attr" />
+    </span>
+    <span v-else-if="newItems.length > 0">
+      <lesson-event-link-iterator :items="newItems" :attr="attr" />
+    </span>
+    <span v-else>
+      <lesson-event-link-iterator :items="oldItems" :attr="attr" />
+    </span>
+  </span>
+</template>
diff --git a/aleksis/apps/chronos/frontend/components/LessonEventSubject.vue b/aleksis/apps/chronos/frontend/components/LessonEventSubject.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9a739936f497ad6f63b1d2780d244ab3b5b74092
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/LessonEventSubject.vue
@@ -0,0 +1,42 @@
+<script>
+export default {
+  name: "LessonEventSubject",
+  props: {
+    event: {
+      type: Object,
+      required: true,
+    },
+    attr: {
+      type: String,
+      required: false,
+      default: "name",
+    },
+  },
+};
+</script>
+
+<template>
+  <span v-bind="$attrs">
+    <span
+      v-if="
+        event.meta.subject && event.meta.amended && event.meta.amends.subject
+      "
+    >
+      <span class="text-decoration-line-through">
+        {{ event.meta.amends.subject[attr] }}</span
+      >
+      <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
+      <span> → </span>
+      <span>{{ event.meta.subject[attr] }}</span>
+    </span>
+    <span v-else-if="event.meta.subject">
+      {{ event.meta.subject[attr] }}
+    </span>
+    <span v-else-if="event.meta.amended && event.meta.amends.subject">
+      {{ event.meta.amends.subject[attr] }}
+    </span>
+    <span v-else>
+      {{ event[attr] }}
+    </span>
+  </span>
+</template>
diff --git a/aleksis/apps/chronos/frontend/components/LessonRelatedObjectChip.vue b/aleksis/apps/chronos/frontend/components/LessonRelatedObjectChip.vue
new file mode 100644
index 0000000000000000000000000000000000000000..657cfafd6d27c5da2e890581b53bddcf9841b777
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/LessonRelatedObjectChip.vue
@@ -0,0 +1,32 @@
+<script>
+export default {
+  name: "LessonRelatedObjectChip",
+  props: {
+    status: {
+      type: String,
+      default: "regular",
+      validator: (value) => ["new", "removed", "regular"].includes(value),
+    },
+    newIcon: {
+      type: String,
+      default: "mdi-plus",
+    },
+  },
+};
+</script>
+
+<template>
+  <v-chip
+    label
+    outlined
+    :class="{
+      'mr-2': true,
+      'text-decoration-line-through text--secondary': status === 'removed',
+    }"
+    :color="status === 'new' ? 'warning' : ''"
+  >
+    <v-icon left v-if="status === 'new'">{{ newIcon }}</v-icon>
+
+    <slot></slot>
+  </v-chip>
+</template>
diff --git a/aleksis/apps/chronos/frontend/components/NoTimetableCard.vue b/aleksis/apps/chronos/frontend/components/NoTimetableCard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..84cf21c8498e54de13efd2056117fe478212aae1
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/NoTimetableCard.vue
@@ -0,0 +1,42 @@
+<script>
+export default {
+  name: "NoTimetableCard",
+  props: {
+    titleKey: {
+      type: String,
+      required: false,
+      default: "chronos.timetable.no_timetable_selected.title",
+    },
+    descriptionKey: {
+      type: String,
+      required: false,
+      default: "chronos.timetable.no_timetable_selected.description",
+    },
+  },
+};
+</script>
+
+<template>
+  <v-card
+    class="full-height d-flex align-center justify-center py-10"
+    v-bind="$attrs"
+  >
+    <div class="text-center">
+      <v-icon color="secondary" size="60" class="mb-4"> mdi-grid-off </v-icon>
+      <div class="text-h5 grey--text text--darken-2 mb-2">
+        {{ $t(titleKey) }}
+      </div>
+      <div
+        class="text-body-2 grey--text text--darken-2"
+        v-if="$vuetify.breakpoint.lgAndUp"
+      >
+        {{ $t(descriptionKey) }}
+      </div>
+      <div v-if="$vuetify.breakpoint.mdAndDown">
+        <v-btn color="primary" @click="$emit('selectTimetable')" class="mt-4">
+          {{ $t("chronos.timetable.select") }}
+        </v-btn>
+      </div>
+    </div>
+  </v-card>
+</template>
diff --git a/aleksis/apps/chronos/frontend/components/SelectTimetable.vue b/aleksis/apps/chronos/frontend/components/SelectTimetable.vue
new file mode 100644
index 0000000000000000000000000000000000000000..fb98d1f56c51d4261502df5d8ec2889e39e6445c
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/SelectTimetable.vue
@@ -0,0 +1,118 @@
+<script>
+import timetableTypes from "./timetableTypes";
+
+export default {
+  name: "SelectTimetable",
+  props: {
+    value: {
+      type: String,
+      required: false,
+      default: null,
+    },
+    availableTimetables: {
+      type: Array,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      selected: null,
+      selectedFull: null,
+      search: "",
+      selectedTypes: ["GROUP", "TEACHER", "ROOM"],
+      types: timetableTypes,
+    };
+  },
+  watch: {
+    value(val) {
+      this.selectedFull = val;
+      this.selected = val.id;
+    },
+    selectedFull(val) {
+      this.$emit("input", val);
+    },
+  },
+  computed: {
+    availableTimetablesFiltered() {
+      // Filter timetables by selected types
+      return this.availableTimetables.filter((timetable) => {
+        return this.selectedTypes.indexOf(timetable.type) !== -1;
+      });
+    },
+  },
+};
+</script>
+
+<template>
+  <div>
+    <v-card-text class="mb-0">
+      <!-- Search field for timetables -->
+      <v-text-field
+        search
+        filled
+        rounded
+        clearable
+        autofocus
+        v-model="search"
+        :placeholder="$t('chronos.timetable.search')"
+        prepend-inner-icon="mdi-magnify"
+        hide-details="auto"
+        class="mb-2"
+      />
+
+      <!-- Filter by timetable types -->
+      <v-btn-toggle v-model="selectedTypes" dense block multiple class="d-flex">
+        <v-btn
+          v-for="type in types"
+          :key="type.id"
+          class="flex-grow-1"
+          :value="type.id"
+        >
+          {{ type.name }}
+        </v-btn>
+      </v-btn-toggle>
+    </v-card-text>
+
+    <!-- Select of available timetables -->
+    <v-data-iterator
+      :items="availableTimetablesFiltered"
+      item-key="id"
+      :search="search"
+      single-expand
+      disable-pagination
+    >
+      <template #default="{ items, isExpanded, expand }">
+        <v-list class="scrollable-list">
+          <v-list-item-group v-model="selected">
+            <v-list-item
+              v-for="item in items"
+              @click="selectedFull = item"
+              :value="item.id"
+              :key="item.id"
+            >
+              <v-list-item-icon color="primary">
+                <v-icon v-if="item.type in types" color="secondary">
+                  {{ types[item.type].icon }}
+                </v-icon>
+                <v-icon v-else color="secondary">mdi-grid</v-icon>
+              </v-list-item-icon>
+              <v-list-item-content>
+                <v-list-item-title>{{ item.name }}</v-list-item-title>
+              </v-list-item-content>
+              <v-list-item-action>
+                <v-icon>mdi-chevron-right</v-icon>
+              </v-list-item-action>
+            </v-list-item>
+          </v-list-item-group>
+        </v-list>
+      </template>
+    </v-data-iterator>
+  </div>
+</template>
+
+<style scoped>
+.scrollable-list {
+  height: 100%;
+  overflow-y: scroll;
+}
+</style>
diff --git a/aleksis/apps/chronos/frontend/components/Timetable.vue b/aleksis/apps/chronos/frontend/components/Timetable.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e4fca5dfb63f26597cd534315fed8f32b9b8a2de
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/Timetable.vue
@@ -0,0 +1,203 @@
+<script>
+import { gqlAvailableTimetables } from "./timetables.graphql";
+import NoTimetableCard from "./NoTimetableCard.vue";
+import SelectTimetable from "./SelectTimetable.vue";
+import timetableTypes from "./timetableTypes";
+
+export default {
+  name: "Timetable",
+  components: { NoTimetableCard, SelectTimetable },
+  apollo: {
+    availableTimetables: {
+      query: gqlAvailableTimetables,
+      result() {
+        if (
+          !this.selected &&
+          this.$route.params.id &&
+          this.$route.params.type
+        ) {
+          this.selectTimetable(
+            this.availableTimetables.find(
+              (t) =>
+                t.objId === this.$route.params.id &&
+                t.type.toLowerCase() === this.$route.params.type
+            )
+          );
+        }
+      },
+    },
+  },
+  data() {
+    return {
+      availableTimetables: [],
+      selected: null,
+      search: "",
+      selectedTypes: ["GROUP", "TEACHER", "ROOM"],
+      types: timetableTypes,
+      selectDialog: false,
+    };
+  },
+  watch: {
+    selected(selected) {
+      // Align navigation with currently selected timetable
+      if (!selected) {
+        this.$router.push({ name: "chronos.timetable" });
+      } else if (
+        selected.objId !== this.$route.params.id ||
+        selected.type.toLowerCase() !== this.$route.params.type
+      ) {
+        this.$router.push({
+          name: "chronos.timetableWithId",
+          params: {
+            type: selected.type.toLowerCase(),
+            id: selected.objId,
+          },
+        });
+      }
+    },
+  },
+  methods: {
+    findNextTimetable(offset = 1) {
+      const currentIndex = this.availableTimetablesIds.indexOf(
+        this.selected.id
+      );
+      const newIndex = currentIndex + offset;
+      if (newIndex < 0 || newIndex >= this.availableTimetablesIds.length) {
+        return null;
+      }
+      return this.availableTimetables[newIndex];
+    },
+    selectTimetable(timetable) {
+      this.selected = timetable;
+    },
+  },
+  computed: {
+    selectedTypesFull() {
+      return this.selectedTypes.map((type) => {
+        return this.types[type];
+      });
+    },
+    availableTimetablesFiltered() {
+      // Filter timetables by selected types
+      return this.availableTimetables.filter((timetable) => {
+        return this.selectedTypes.indexOf(timetable.type) !== -1;
+      });
+    },
+    availableTimetablesIds() {
+      return this.availableTimetables.map((timetable) => timetable.id);
+    },
+    prevTimetable() {
+      return this.findNextTimetable(-1);
+    },
+    nextTimetable() {
+      return this.findNextTimetable(1);
+    },
+  },
+};
+</script>
+
+<template>
+  <div>
+    <v-row>
+      <v-dialog
+        v-model="selectDialog"
+        fullscreen
+        hide-overlay
+        transition="dialog-bottom-transition"
+      >
+        <v-card>
+          <v-toolbar dark color="primary">
+            <v-toolbar-title>{{
+              $t("chronos.timetable.select")
+            }}</v-toolbar-title>
+            <v-spacer></v-spacer>
+          </v-toolbar>
+          <select-timetable
+            v-model="selected"
+            @input="selectDialog = false"
+            :available-timetables="availableTimetables"
+          />
+        </v-card>
+      </v-dialog>
+
+      <v-col md="3" lg="3" xl="3" v-if="$vuetify.breakpoint.lgAndUp">
+        <v-card>
+          <select-timetable
+            v-model="selected"
+            :available-timetables="availableTimetables"
+          />
+        </v-card>
+      </v-col>
+      <v-col sm="12" md="12" lg="9" xl="9" class="full-height">
+        <!-- No timetable card-->
+        <no-timetable-card
+          v-if="selected == null"
+          @selectTimetable="selectDialog = true"
+        />
+
+        <!-- Calendar card-->
+        <v-card v-else>
+          <div class="d-flex flex-column" v-if="$vuetify.breakpoint.smAndDown">
+            <v-card-title class="pt-2">
+              <v-btn
+                icon
+                :disabled="!prevTimetable"
+                @click="selectTimetable(prevTimetable)"
+                :title="$t('chronos.timetable.prev')"
+                class="mr-1"
+              >
+                <v-icon>mdi-chevron-left</v-icon>
+              </v-btn>
+              <v-spacer />
+              <v-chip outlined color="secondary" @click="selectDialog = true">
+                {{ selected.name }}
+                <v-icon right>mdi-chevron-down</v-icon>
+              </v-chip>
+              <v-spacer />
+              <v-btn
+                icon
+                :disabled="!nextTimetable"
+                @click="selectTimetable(nextTimetable)"
+                :title="$t('chronos.timetable.next')"
+                class="ml-1 float-right"
+              >
+                <v-icon>mdi-chevron-right</v-icon>
+              </v-btn>
+            </v-card-title>
+          </div>
+
+          <div class="d-flex flex-wrap justify-space-between mb-2" v-else>
+            <v-card-title>
+              {{ selected.name }}
+            </v-card-title>
+            <div class="pa-2 mt-1">
+              <v-btn
+                icon
+                :disabled="!prevTimetable"
+                @click="selectTimetable(prevTimetable)"
+                :title="$t('chronos.timetable.prev')"
+              >
+                <v-icon>mdi-chevron-left</v-icon>
+              </v-btn>
+              <v-chip label color="secondary" outlined class="mx-1">{{
+                selected.shortName
+              }}</v-chip>
+              <v-btn
+                icon
+                :disabled="!nextTimetable"
+                @click="selectTimetable(nextTimetable)"
+                :title="$t('chronos.timetable.next')"
+              >
+                <v-icon>mdi-chevron-right</v-icon>
+              </v-btn>
+            </div>
+          </div>
+          <calendar-with-controls
+            :calendar-feeds="[{ name: 'lesson' }, { name: 'supervision' }]"
+            :params="{ type: selected.type, id: selected.objId }"
+          />
+        </v-card>
+      </v-col>
+    </v-row>
+  </div>
+</template>
diff --git a/aleksis/apps/chronos/frontend/components/amendLesson.graphql b/aleksis/apps/chronos/frontend/components/amendLesson.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..a93e89186df05efdfa77473b7f1a1751eb614c24
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/amendLesson.graphql
@@ -0,0 +1,75 @@
+query gqlSubjects {
+  amendableSubjects: subjects {
+    id
+    name
+  }
+}
+
+query gqlPersons {
+  amendableTeachers: persons {
+    id
+    fullName
+  }
+}
+
+query gqlRooms {
+  amendableRooms: rooms {
+    id
+    name
+  }
+}
+
+mutation createAmendLesson($input: CreateLessonEventInput!) {
+  createAmendLesson(input: $input) {
+    lessonEvent {
+      id
+      amends {
+        id
+      }
+      datetimeStart
+      datetimeEnd
+      subject {
+        id
+      }
+      teachers {
+        id
+      }
+      groups {
+        id
+      }
+      rooms {
+        id
+      }
+      cancelled
+      comment
+    }
+  }
+}
+
+mutation patchAmendLesson($input: PatchLessonEventInput!, $id: ID!) {
+  patchAmendLesson(input: $input, id: $id) {
+    lessonEvent {
+      id
+      subject {
+        id
+      }
+      teachers {
+        id
+      }
+      groups {
+        id
+      }
+      rooms {
+        id
+      }
+      cancelled
+      comment
+    }
+  }
+}
+
+mutation deleteAmendLesson($id: ID!) {
+  deleteAmendLesson(id: $id) {
+    ok
+  }
+}
diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue
new file mode 100644
index 0000000000000000000000000000000000000000..0c9311450f0c7d8c3e922743f1403382da1148ce
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue
@@ -0,0 +1,133 @@
+<template>
+  <base-calendar-feed-details
+    v-bind="$props"
+    :color="currentSubject ? currentSubject.colour_bg : null"
+    without-location
+  >
+    <template #title>
+      <div
+        :style="{
+          color: currentSubject ? currentSubject.colour_fg || 'white' : 'white',
+        }"
+      >
+        <lesson-event-subject :event="selectedEvent" />
+      </div>
+    </template>
+    <template #badge>
+      <cancelled-calendar-status-chip
+        v-if="selectedEvent.meta.cancelled"
+        class="ml-4"
+      />
+      <calendar-status-chip
+        color="warning"
+        icon="mdi-clipboard-alert-outline"
+        v-else-if="selectedEvent.meta.amended"
+        class="ml-4"
+      >
+        {{ $t("chronos.event.current_changes") }}
+      </calendar-status-chip>
+    </template>
+    <template #description>
+      <v-divider inset />
+      <v-list-item v-if="selectedEvent.meta.groups.length > 0">
+        <v-list-item-icon>
+          <v-icon color="primary">mdi-account-group-outline</v-icon>
+        </v-list-item-icon>
+        <v-list-item-content>
+          <v-list-item-title>
+            <lesson-related-object-chip
+              v-for="group in selectedEvent.meta.groups"
+              :key="group.id"
+              >{{ group.name }}</lesson-related-object-chip
+            >
+          </v-list-item-title>
+        </v-list-item-content>
+      </v-list-item>
+      <v-list-item>
+        <v-list-item-icon>
+          <v-icon color="primary">mdi-human-male-board </v-icon>
+        </v-list-item-icon>
+        <v-list-item-content>
+          <v-list-item-title>
+            <span v-if="teachers.length === 0" class="body-2 text--secondary">{{
+              $t("chronos.event.no_teacher")
+            }}</span>
+            <lesson-related-object-chip
+              v-for="teacher in teachers"
+              :status="teacher.status"
+              :key="teacher.id"
+              new-icon="mdi-account-plus-outline"
+              >{{ teacher.full_name }}</lesson-related-object-chip
+            >
+          </v-list-item-title>
+        </v-list-item-content>
+      </v-list-item>
+      <v-list-item>
+        <v-list-item-icon>
+          <v-icon color="primary">mdi-door </v-icon>
+        </v-list-item-icon>
+        <v-list-item-content>
+          <v-list-item-title>
+            <span v-if="rooms.length === 0" class="body-2 text--secondary">{{
+              $t("chronos.event.no_room")
+            }}</span>
+            <lesson-related-object-chip
+              v-for="room in rooms"
+              :status="room.status"
+              :key="room.id"
+              new-icon="mdi-door-open"
+              >{{ room.name }}</lesson-related-object-chip
+            >
+          </v-list-item-title>
+        </v-list-item-content>
+      </v-list-item>
+      <v-divider inset />
+      <v-list-item v-if="selectedEvent.meta.comment">
+        <v-list-item-content>
+          <v-list-item-title>
+            <v-alert
+              dense
+              outlined
+              type="warning"
+              icon="mdi-information-outline"
+            >
+              {{ selectedEvent.meta.comment }}
+            </v-alert>
+          </v-list-item-title>
+        </v-list-item-content>
+      </v-list-item>
+      <amend-lesson
+        v-if="selectedEvent"
+        :selected-event="selectedEvent"
+        @refreshCalendar="$emit('refreshCalendar')"
+      />
+    </template>
+  </base-calendar-feed-details>
+</template>
+
+<script>
+import calendarFeedDetailsMixin from "aleksis.core/mixins/calendarFeedDetails.js";
+import BaseCalendarFeedDetails from "aleksis.core/components/calendar/BaseCalendarFeedDetails.vue";
+import CalendarStatusChip from "aleksis.core/components/calendar/CalendarStatusChip.vue";
+import CancelledCalendarStatusChip from "aleksis.core/components/calendar/CancelledCalendarStatusChip.vue";
+
+import LessonRelatedObjectChip from "../../LessonRelatedObjectChip.vue";
+
+import lessonEvent from "../mixins/lessonEvent";
+import LessonEventSubject from "../../LessonEventSubject.vue";
+
+import AmendLesson from "../../AmendLesson.vue";
+
+export default {
+  name: "LessonDetails",
+  components: {
+    LessonEventSubject,
+    LessonRelatedObjectChip,
+    BaseCalendarFeedDetails,
+    CalendarStatusChip,
+    CancelledCalendarStatusChip,
+    AmendLesson,
+  },
+  mixins: [calendarFeedDetailsMixin, lessonEvent],
+};
+</script>
diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7d6455a054fd8d7a46433ea5ad4693e4c40ff43e
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue
@@ -0,0 +1,109 @@
+<template>
+  <base-calendar-feed-details
+    v-bind="$props"
+    :color="currentSubject ? currentSubject.colour_bg : null"
+    without-location
+  >
+    <template #title>
+      <div
+        :style="{
+          color: currentSubject ? currentSubject.colour_fg || 'white' : 'white',
+        }"
+      >
+        <lesson-event-subject :event="selectedEvent" />
+      </div>
+    </template>
+    <template #badge>
+      <cancelled-calendar-status-chip
+        v-if="selectedEvent.meta.cancelled"
+        class="ml-4"
+      />
+      <calendar-status-chip
+        color="warning"
+        icon="mdi-clipboard-alert-outline"
+        v-else-if="selectedEvent.meta.amended"
+        class="ml-4"
+      >
+        {{ $t("chronos.event.current_changes") }}
+      </calendar-status-chip>
+    </template>
+    <template #description>
+      <v-divider inset />
+      <v-list-item>
+        <v-list-item-icon>
+          <v-icon color="primary">mdi-human-male-board </v-icon>
+        </v-list-item-icon>
+        <v-list-item-content>
+          <v-list-item-title>
+            <span v-if="teachers.length === 0" class="body-2 text--secondary">{{
+              $t("chronos.event.no_teacher")
+            }}</span>
+            <lesson-related-object-chip
+              v-for="teacher in teachers"
+              :status="teacher.status"
+              :key="teacher.id"
+              new-icon="mdi-account-plus-outline"
+              >{{ teacher.full_name }}</lesson-related-object-chip
+            >
+          </v-list-item-title>
+        </v-list-item-content>
+      </v-list-item>
+      <v-list-item>
+        <v-list-item-icon>
+          <v-icon color="primary">mdi-door </v-icon>
+        </v-list-item-icon>
+        <v-list-item-content>
+          <v-list-item-title>
+            <span v-if="rooms.length === 0" class="body-2 text--secondary">{{
+              $t("chronos.event.no_room")
+            }}</span>
+            <lesson-related-object-chip
+              v-for="room in rooms"
+              :status="room.status"
+              :key="room.id"
+              new-icon="mdi-door-open"
+              >{{ room.name }}</lesson-related-object-chip
+            >
+          </v-list-item-title>
+        </v-list-item-content>
+      </v-list-item>
+      <v-divider inset />
+      <v-list-item v-if="selectedEvent.meta.comment">
+        <v-list-item-content>
+          <v-list-item-title>
+            <v-alert
+              dense
+              outlined
+              type="warning"
+              icon="mdi-information-outline"
+            >
+              {{ selectedEvent.meta.comment }}
+            </v-alert>
+          </v-list-item-title>
+        </v-list-item-content>
+      </v-list-item>
+    </template>
+  </base-calendar-feed-details>
+</template>
+
+<script>
+import calendarFeedDetailsMixin from "aleksis.core/mixins/calendarFeedDetails.js";
+import BaseCalendarFeedDetails from "aleksis.core/components/calendar/BaseCalendarFeedDetails.vue";
+import CalendarStatusChip from "aleksis.core/components/calendar/CalendarStatusChip.vue";
+import CancelledCalendarStatusChip from "aleksis.core/components/calendar/CancelledCalendarStatusChip.vue";
+
+import LessonRelatedObjectChip from "../../LessonRelatedObjectChip.vue";
+import lessonEvent from "../mixins/lessonEvent";
+import LessonEventSubject from "../../LessonEventSubject.vue";
+export default {
+  name: "LessonDetails",
+  components: {
+    LessonEventSubject,
+    LessonRelatedObjectChip,
+    BaseCalendarFeedDetails,
+    CalendarStatusChip,
+    CancelledCalendarStatusChip,
+  },
+  mixins: [calendarFeedDetailsMixin, lessonEvent],
+};
+</script>
diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/LessonEventBar.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/LessonEventBar.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1f0eb6527d0859ead2edb1cf120ec5ef84b5c6c3
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/event_bar/LessonEventBar.vue
@@ -0,0 +1,103 @@
+<template>
+  <base-calendar-feed-event-bar
+    :with-padding="false"
+    :without-time="true"
+    v-bind="$props"
+  >
+    <template #icon> </template>
+
+    <template #title>
+      <div
+        class="d-flex justify-start"
+        :class="{
+          'px-1': true,
+          'orange-border':
+            selectedEvent.meta.amended && !selectedEvent.meta.cancelled,
+          'red-border': selectedEvent.meta.cancelled,
+        }"
+        :style="{
+          color: currentSubject ? currentSubject.colour_fg || 'white' : 'white',
+          height: '100%',
+          borderRadius: '4px',
+        }"
+      >
+        <span
+          v-if="calendarType === 'month' && eventParsed.start.hasTime"
+          class="mr-1 font-weight-bold ml-1"
+        >
+          {{ eventParsed.start.time }}
+        </span>
+        <div
+          class="d-flex justify-center align-center flex-grow-1 text-truncate"
+        >
+          <div class="d-flex justify-center align-center flex-wrap text">
+            <lesson-event-link-iterator
+              v-if="!selectedEvent.meta.is_member"
+              :items="selectedEvent.meta.groups"
+              attr="short_name"
+              class="mr-1"
+            />
+            <lesson-event-old-new
+              v-if="!selectedEvent.meta.is_teacher || newTeachers.length > 0"
+              :new-items="newTeachers"
+              :old-items="oldTeachers"
+              attr="short_name"
+              class="mr-1"
+            />
+
+            <lesson-event-subject
+              :event="selectedEvent"
+              attr="short_name"
+              class="font-weight-medium mr-1"
+            />
+            <lesson-event-old-new
+              :new-items="newRooms"
+              :old-items="oldRooms"
+              attr="short_name"
+            />
+          </div>
+        </div>
+      </div>
+    </template>
+  </base-calendar-feed-event-bar>
+</template>
+
+<script>
+import calendarFeedEventBarMixin from "aleksis.core/mixins/calendarFeedEventBar.js";
+import BaseCalendarFeedEventBar from "aleksis.core/components/calendar/BaseCalendarFeedEventBar.vue";
+import lessonEvent from "../mixins/lessonEvent";
+import LessonEventSubject from "../../LessonEventSubject.vue";
+import LessonEventLinkIterator from "../../LessonEventLinkIterator.vue";
+import LessonEventOldNew from "../../LessonEventOldNew.vue";
+
+export default {
+  name: "LessonEventBar",
+  components: {
+    LessonEventOldNew,
+    LessonEventLinkIterator,
+    LessonEventSubject,
+    BaseCalendarFeedEventBar,
+  },
+  computed: {
+    selectedEvent() {
+      return this.event;
+    },
+  },
+  mixins: [calendarFeedEventBarMixin, lessonEvent],
+};
+</script>
+
+<style scoped>
+.orange-border {
+  border: 3px orange solid;
+}
+
+.red-border {
+  border: 3px red solid;
+}
+
+.text {
+  line-height: 1.1;
+  font-size: 12px;
+}
+</style>
diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/mixins/lessonEvent.js b/aleksis/apps/chronos/frontend/components/calendar_feeds/mixins/lessonEvent.js
new file mode 100644
index 0000000000000000000000000000000000000000..64d2b60a12ca67c5b586f9ed9b9e029ad00948ec
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/mixins/lessonEvent.js
@@ -0,0 +1,78 @@
+/**
+ * Mixin with common used API for custom lesson event calendar components
+ */
+const lessonEvent = {
+  methods: {
+    addStatuses(attr) {
+      let oldItems = this.getOldItems(attr);
+      let newItems = this.getNewItems(attr);
+      let oldIds = oldItems.map((item) => item.id);
+      let newIds = newItems.map((item) => item.id);
+      let itemsWithStatus = oldItems.concat(newItems).map((item) => {
+        let status = "regular";
+        if (newIds.includes(item.id) && !oldIds.includes(item.id)) {
+          status = "new";
+        } else if (
+          newIds.length > 0 &&
+          !newIds.includes(item.id) &&
+          oldIds.includes(item.id)
+        ) {
+          status = "removed";
+        }
+        return { ...item, status: status };
+      });
+      return itemsWithStatus;
+    },
+    getOldItems(attr) {
+      let oldItems = [];
+      if (this.selectedEvent.meta.amended) {
+        oldItems = this.selectedEvent.meta.amends[attr];
+      } else {
+        oldItems = this.selectedEvent.meta[attr];
+      }
+      return oldItems;
+    },
+    getNewItems(attr) {
+      let newItems = [];
+      if (this.selectedEvent.meta.amended) {
+        newItems = this.selectedEvent.meta[attr];
+      }
+      return newItems;
+    },
+  },
+  computed: {
+    teachers() {
+      return this.addStatuses("teachers");
+    },
+    oldTeachers() {
+      return this.getOldItems("teachers");
+    },
+    newTeachers() {
+      return this.getNewItems("teachers");
+    },
+
+    rooms() {
+      return this.addStatuses("rooms");
+    },
+    oldRooms() {
+      return this.getOldItems("rooms");
+    },
+    newRooms() {
+      return this.getNewItems("rooms");
+    },
+    currentSubject() {
+      if (this.selectedEvent.meta.subject) {
+        return this.selectedEvent.meta.subject;
+      } else if (
+        this.selectedEvent.meta.amended &&
+        this.selectedEvent.meta.amends.subject
+      ) {
+        return this.selectedEvent.meta.amends.subject;
+      } else {
+        return null;
+      }
+    },
+  },
+};
+
+export default lessonEvent;
diff --git a/aleksis/apps/chronos/frontend/components/timetableTypes.js b/aleksis/apps/chronos/frontend/components/timetableTypes.js
new file mode 100644
index 0000000000000000000000000000000000000000..692854f18411cc2f3ed1abf35069d28c9b18e51c
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/timetableTypes.js
@@ -0,0 +1,13 @@
+export default {
+  GROUP: {
+    name: "Groups",
+    id: "GROUP",
+    icon: "mdi-account-group-outline",
+  },
+  TEACHER: {
+    name: "Teachers",
+    id: "TEACHER",
+    icon: "mdi-account-outline",
+  },
+  ROOM: { name: "Rooms", id: "ROOM", icon: "mdi-door" },
+};
diff --git a/aleksis/apps/chronos/frontend/components/timetables.graphql b/aleksis/apps/chronos/frontend/components/timetables.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..5bbe46f734f1981ad1c2648d50a8a6e8d7bd04fc
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/components/timetables.graphql
@@ -0,0 +1,9 @@
+query gqlAvailableTimetables {
+  availableTimetables {
+    id
+    objId
+    type
+    name
+    shortName
+  }
+}
diff --git a/aleksis/apps/chronos/frontend/index.js b/aleksis/apps/chronos/frontend/index.js
index 501071992b10bfad99480b80d2810628d19b48aa..30451f93c2f0dfcb769a4ab92385b50fe1bfe61c 100644
--- a/aleksis/apps/chronos/frontend/index.js
+++ b/aleksis/apps/chronos/frontend/index.js
@@ -1,4 +1,5 @@
 import { hasPersonValidator } from "aleksis.core/routeValidators";
+import Timetable from "./components/Timetable.vue";
 
 export default {
   meta: {
@@ -12,185 +13,22 @@ export default {
   },
   children: [
     {
-      path: "",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.allTimetables",
+      path: "timetable/",
+      component: Timetable,
+      name: "chronos.timetable",
       meta: {
         inMenu: true,
-        titleKey: "chronos.timetable.menu_title_all",
+        titleKey: "chronos.timetable.menu_title",
         icon: "mdi-grid",
         permission: "chronos.view_timetable_overview_rule",
       },
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "timetable/my/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.myTimetable",
-      meta: {
-        inMenu: true,
-        titleKey: "chronos.timetable.menu_title_my",
-        icon: "mdi-account-outline",
-        permission: "chronos.view_my_timetable_rule",
-      },
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "timetable/my/:year/:month/:day/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.myTimetableByDate",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "timetable/:type_/:pk/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.timetable",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "timetable/:type_/:pk/:year/:week/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.timetableByWeek",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "timetable/:type_/:pk/print/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.timetablePrint",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "timetable/:type_/:pk/:regular/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.timetableRegular",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "lessons/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.lessonsDay",
-      meta: {
-        inMenu: true,
-        titleKey: "chronos.lessons.menu_title_daily",
-        icon: "mdi-calendar-outline",
-        permission: "chronos.view_lessons_day_rule",
-      },
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "lessons/:year/:month/:day/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.lessonsDayByDate",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
     },
     {
-      path: "lessons/:id_/:week/substitution/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.editSubstitution",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "lessons/:id_/:week/substitution/delete/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.deleteSubstitution",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "substitutions/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.substitutions",
+      path: "timetable/:type/:id/",
+      component: Timetable,
+      name: "chronos.timetableWithId",
       meta: {
-        inMenu: true,
-        titleKey: "chronos.substitutions.menu_title",
-        icon: "mdi-update",
-        permission: "chronos.view_substitutions_rule",
-      },
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "substitutions/print/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.substitutionsPrint",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "substitutions/:year/:month/:day/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.substitutionsByDate",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "substitutions/:year/:month/:day/print/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.substitutionsPrintByDate",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "supervisions/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.supervisionsDay",
-      meta: {
-        inMenu: true,
-        titleKey: "chronos.supervisions.menu_title_daily",
-        icon: "mdi-calendar-outline",
-        permission: "chronos.view_supervisions_day_rule",
-      },
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "supervisions/:year/:month/:day/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.supervisionsDayByDate",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "supervisions/:id_/:week/substitution/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.editSupervisionSubstitution",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
-    },
-    {
-      path: "supervisions/:id_/:week/substitution/delete/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "chronos.deleteSupervisionSubstitution",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+        permission: "chronos.view_timetable_overview_rule",
       },
     },
   ],
diff --git a/aleksis/apps/chronos/frontend/messages/de.json b/aleksis/apps/chronos/frontend/messages/de.json
index eb3592afd13a36cc3b8f9cf2e30e6ef032b60099..512498ccfae594033f7634e09bfba992233612fe 100644
--- a/aleksis/apps/chronos/frontend/messages/de.json
+++ b/aleksis/apps/chronos/frontend/messages/de.json
@@ -2,8 +2,17 @@
   "chronos": {
     "menu_title": "Stundenpläne",
     "timetable": {
+      "menu_title": "Stundenpläne",
       "menu_title_all": "Alle Stundenpläne",
-      "menu_title_my": "Mein Stundenplan"
+      "menu_title_my": "Mein Stundenplan",
+      "no_timetable_selected": {
+        "title": "Kein Stundenplan ausgewählt",
+        "description": "Wählen Sie auf der linken Seite einen Stundenplan aus, um ihn hier anzuzeigen"
+      },
+      "search": "Stundenpläne suchen",
+      "prev": "Vorheriger Stundenplan",
+      "next": "Nächster Stundenplan",
+      "select": "Stundenplan auswählen"
     },
     "lessons": {
       "menu_title_daily": "Tagesstunden"
@@ -12,7 +21,12 @@
       "menu_title": "Vertretungen"
     },
     "supervisions": {
-      "menu_title_daily": "Tagesvertretungen"
+      "menu_title_daily": "Aufsichten"
+    },
+    "event": {
+      "no_teacher": "Keine Lehrkraft",
+      "no_room": "Kein Raum",
+      "current_changes": "Aktuelle Änderungen"
     }
   }
 }
diff --git a/aleksis/apps/chronos/frontend/messages/en.json b/aleksis/apps/chronos/frontend/messages/en.json
index d824f79087540ca0b92d4efe18e3755d562c370f..b995b749a1677cdfbdbfef9888f4bd427062717c 100644
--- a/aleksis/apps/chronos/frontend/messages/en.json
+++ b/aleksis/apps/chronos/frontend/messages/en.json
@@ -2,8 +2,17 @@
   "chronos": {
     "menu_title": "Timetables",
     "timetable": {
+      "menu_title": "Timetables",
       "menu_title_all": "All timetables",
-      "menu_title_my": "My timetable"
+      "menu_title_my": "My timetable",
+      "no_timetable_selected": {
+        "title": "No Timetable Selected",
+        "description": "Select a timetable on the left side to show it in this place"
+      },
+      "search": "Search Timetables",
+      "prev": "Previous Timetable",
+      "next": "Next Timetable",
+      "select": "Select Timetable"
     },
     "lessons": {
       "menu_title_daily": "Daily lessons"
@@ -13,6 +22,23 @@
     },
     "supervisions": {
       "menu_title_daily": "Daily supervisions"
+    },
+    "event": {
+      "no_teacher": "No teacher",
+      "no_room": "No room",
+      "current_changes": "Current changes",
+      "amend": {
+        "edit_button": "Change",
+        "delete_button": "Reset",
+        "delete_dialog": " Are you sure you want to delete this substitution?",
+        "delete_success": "The substitution was deleted successfully.",
+        "title": "Change lesson",
+        "subject": "Subject",
+        "teachers": "Teachers",
+        "rooms": "Rooms",
+        "cancelled": "Cancelled",
+        "comment": "Comment"
+      }
     }
   }
 }
diff --git a/aleksis/apps/chronos/managers.py b/aleksis/apps/chronos/managers.py
index 486409f95052b51431fcaf74121690dafcb31d7e..79bd043f4b28300ca5d6df961d43e820ae92a40b 100644
--- a/aleksis/apps/chronos/managers.py
+++ b/aleksis/apps/chronos/managers.py
@@ -9,10 +9,11 @@ from django.db.models.fields import DateField
 from django.db.models.functions import Concat
 
 from calendarweek import CalendarWeek
+from polymorphic.managers import PolymorphicQuerySet
 
 from aleksis.apps.chronos.util.date import week_weekday_from_date, week_weekday_to_date
 from aleksis.core.managers import DateRangeQuerySetMixin, SchoolTermRelatedQuerySet
-from aleksis.core.models import Group, Person
+from aleksis.core.models import Group, Person, Room
 from aleksis.core.util.core_helpers import get_site_preferences
 
 
@@ -799,16 +800,20 @@ class ExtraLessonQuerySet(TimetableQuerySet, SchoolTermRelatedQuerySet, GroupByP
 class GroupPropertiesMixin:
     """Mixin for common group properties.
 
-    Needed field: `groups`
+    Necessary method: `get_groups`
     """
 
     @property
     def group_names(self, sep: Optional[str] = ", ") -> str:
-        return sep.join([group.short_name for group in self.groups.all()])
+        return sep.join([group.short_name for group in self.get_groups()])
+
+    @property
+    def group_short_names(self, sep: Optional[str] = ", ") -> str:
+        return sep.join([group.short_name for group in self.get_groups()])
 
     @property
     def groups_to_show(self) -> models.QuerySet:
-        groups = self.groups.all()
+        groups = self.get_groups()
         if (
             groups.count() == 1
             and groups[0].parent_groups.all()
@@ -822,17 +827,68 @@ class GroupPropertiesMixin:
     def groups_to_show_names(self, sep: Optional[str] = ", ") -> str:
         return sep.join([group.short_name for group in self.groups_to_show])
 
+    @property
+    def groups_to_show_short_names(self, sep: Optional[str] = ", ") -> str:
+        return sep.join([group.short_name for group in self.groups_to_show])
+
 
 class TeacherPropertiesMixin:
     """Mixin for common teacher properties.
 
-    Needed field: `teacher`
+    Necessary method: `get_teachers`
     """
 
     @property
     def teacher_names(self, sep: Optional[str] = ", ") -> str:
-        return sep.join([teacher.full_name for teacher in self.get_teachers().all()])
+        return sep.join([teacher.full_name for teacher in self.get_teachers()])
 
     @property
     def teacher_short_names(self, sep: str = ", ") -> str:
-        return sep.join([teacher.short_name for teacher in self.get_teachers().all()])
+        return sep.join([teacher.short_name for teacher in self.get_teachers()])
+
+
+class RoomPropertiesMixin:
+    """Mixin for common room properties.
+
+    Necessary method: `get_rooms`
+    """
+
+    @property
+    def room_names(self, sep: Optional[str] = ", ") -> str:
+        return sep.join([room.name for room in self.get_rooms()])
+
+    @property
+    def room_short_names(self, sep: str = ", ") -> str:
+        return sep.join([room.short_name for room in self.get_rooms()])
+
+
+class LessonEventQuerySet(PolymorphicQuerySet):
+    """Queryset with special query methods for lesson events."""
+
+    def for_teacher(self, teacher: Union[int, Person]):
+        amended = self.filter(Q(amended_by__isnull=False) & (Q(teachers=teacher))).values_list(
+            "amended_by__pk", flat=True
+        )
+        return self.filter(Q(teachers=teacher) | Q(pk__in=amended)).distinct()
+
+    def for_group(self, group: Union[int, Group]):
+        amended = self.filter(
+            Q(amended_by__isnull=False) & (Q(groups=group) | Q(groups__parent_groups=group))
+        ).values_list("amended_by__pk", flat=True)
+        return self.filter(
+            Q(groups=group) | Q(groups__parent_groups=group) | Q(pk__in=amended)
+        ).distinct()
+
+    def for_room(self, room: Union[int, Room]):
+        amended = self.filter(Q(amended_by__isnull=False) & (Q(rooms=room))).values_list(
+            "amended_by__pk", flat=True
+        )
+        return self.filter(Q(rooms=room) | Q(pk__in=amended)).distinct()
+
+    def for_person(self, person: Union[int, Person]):
+        amended = self.filter(
+            Q(amended_by__isnull=False) & (Q(teachers=person) | Q(groups__members=person))
+        ).values_list("amended_by__pk", flat=True)
+        return self.filter(
+            Q(teachers=person) | Q(groups__members=person) | Q(pk__in=amended)
+        ).distinct()
diff --git a/aleksis/apps/chronos/menus.py b/aleksis/apps/chronos/menus.py
deleted file mode 100644
index 810e88c6d75a4cefb95210234fb70d690bb32ea1..0000000000000000000000000000000000000000
--- a/aleksis/apps/chronos/menus.py
+++ /dev/null
@@ -1,73 +0,0 @@
-from django.utils.translation import gettext_lazy as _
-
-MENUS = {
-    "NAV_MENU_CORE": [
-        {
-            "name": _("Timetables"),
-            "url": "#",
-            "svg_icon": "mdi:school-outline",
-            "root": True,
-            "validators": [
-                "menu_generator.validators.is_authenticated",
-                "aleksis.core.util.core_helpers.has_person",
-            ],
-            "submenu": [
-                {
-                    "name": _("My timetable"),
-                    "url": "my_timetable",
-                    "svg_icon": "mdi:account-outline",
-                    "validators": [
-                        (
-                            "aleksis.core.util.predicates.permission_validator",
-                            "chronos.view_my_timetable_rule",
-                        ),
-                    ],
-                },
-                {
-                    "name": _("All timetables"),
-                    "url": "all_timetables",
-                    "svg_icon": "mdi:grid",
-                    "validators": [
-                        (
-                            "aleksis.core.util.predicates.permission_validator",
-                            "chronos.view_timetable_overview_rule",
-                        ),
-                    ],
-                },
-                {
-                    "name": _("Daily lessons"),
-                    "url": "lessons_day",
-                    "svg_icon": "mdi:calendar-outline",
-                    "validators": [
-                        (
-                            "aleksis.core.util.predicates.permission_validator",
-                            "chronos.view_lessons_day_rule",
-                        ),
-                    ],
-                },
-                {
-                    "name": _("Daily supervisions"),
-                    "url": "supervisions_day",
-                    "svg_icon": "mdi:calendar-outline",
-                    "validators": [
-                        (
-                            "aleksis.core.util.predicates.permission_validator",
-                            "chronos.view_supervisions_day_rule",
-                        ),
-                    ],
-                },
-                {
-                    "name": _("Substitutions"),
-                    "url": "substitutions",
-                    "svg_icon": "mdi:update",
-                    "validators": [
-                        (
-                            "aleksis.core.util.predicates.permission_validator",
-                            "chronos.view_substitutions_rule",
-                        ),
-                    ],
-                },
-            ],
-        }
-    ]
-}
diff --git a/aleksis/apps/chronos/migrations/0001_initial.py b/aleksis/apps/chronos/migrations/0001_initial.py
index 883b25bbf97f4bb0660e787d0a787b754b5efec4..023ea06eb613a00e8b24b0f90046d92ae906a126 100644
--- a/aleksis/apps/chronos/migrations/0001_initial.py
+++ b/aleksis/apps/chronos/migrations/0001_initial.py
@@ -1,13 +1,14 @@
 # Generated by Django 3.0.5 on 2020-05-04 14:16
 
 import django.contrib.postgres.fields.jsonb
-import django.contrib.sites.managers
 import django.db.models.deletion
 from django.db import migrations, models
 
 import calendarweek.calendarweek
 import colorfield.fields
 
+import aleksis.core.managers
+
 import aleksis.apps.chronos.managers
 
 
@@ -49,7 +50,7 @@ class Migration(migrations.Migration):
                 ),
                 "managed": False,
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="Break",
@@ -80,7 +81,7 @@ class Migration(migrations.Migration):
                 "verbose_name_plural": "Breaks",
                 "ordering": ["after_period"],
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="Lesson",
@@ -119,7 +120,7 @@ class Migration(migrations.Migration):
                 aleksis.apps.chronos.managers.GroupPropertiesMixin,
                 aleksis.apps.chronos.managers.TeacherPropertiesMixin,
             ),
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="LessonPeriod",
@@ -251,7 +252,7 @@ class Migration(migrations.Migration):
                 "verbose_name_plural": "Time periods",
                 "ordering": ["weekday", "period"],
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="SupervisionSubstitution",
@@ -353,7 +354,7 @@ class Migration(migrations.Migration):
                 "verbose_name_plural": "Supervision areas",
                 "ordering": ["name"],
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.AddField(
             model_name="supervision",
@@ -458,7 +459,7 @@ class Migration(migrations.Migration):
                 "verbose_name_plural": "Subjects",
                 "ordering": ["name", "short_name"],
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="Room",
@@ -500,7 +501,7 @@ class Migration(migrations.Migration):
                 "verbose_name_plural": "Rooms",
                 "ordering": ["name", "short_name"],
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="LessonSubstitution",
@@ -871,7 +872,7 @@ class Migration(migrations.Migration):
                 "verbose_name_plural": "Exams",
                 "ordering": ["date"],
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="Event",
@@ -1032,7 +1033,7 @@ class Migration(migrations.Migration):
                 "verbose_name": "Absence reason",
                 "verbose_name_plural": "Absence reasons",
             },
-            managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
+            managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),],
         ),
         migrations.CreateModel(
             name="Absence",
diff --git a/aleksis/apps/chronos/migrations/0014_lessonevent.py b/aleksis/apps/chronos/migrations/0014_lessonevent.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b0e0cbac8478b850d847d067591b6885d606d2a
--- /dev/null
+++ b/aleksis/apps/chronos/migrations/0014_lessonevent.py
@@ -0,0 +1,114 @@
+# Generated by Django 4.1.8 on 2023-06-29 14:50
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("sites", "0002_alter_domain_unique"),
+        ("core", "0051_calendarevent_and_holiday"),
+        ("cursus", "0001_initial"),
+        ("chronos", "0013_move_room_to_core"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="LessonEvent",
+            fields=[
+                (
+                    "calendarevent_ptr",
+                    models.OneToOneField(
+                        auto_created=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        parent_link=True,
+                        primary_key=True,
+                        serialize=False,
+                        to="core.calendarevent",
+                    ),
+                ),
+                (
+                    "title",
+                    models.CharField(blank=True, max_length=255, verbose_name="Name"),
+                ),
+                (
+                    "cancelled",
+                    models.BooleanField(default=False, verbose_name="Cancelled"),
+                ),
+                ("comment", models.TextField(blank=True, verbose_name="Comment")),
+                (
+                    "course",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="cursus.course",
+                        verbose_name="Course",
+                    ),
+                ),
+                (
+                    "groups",
+                    models.ManyToManyField(
+                        blank=True,
+                        related_name="lesson_events",
+                        to="core.group",
+                        verbose_name="Groups",
+                    ),
+                ),
+                (
+                    "rooms",
+                    models.ManyToManyField(
+                        blank=True,
+                        related_name="lesson_events",
+                        to="core.room",
+                        verbose_name="Rooms",
+                    ),
+                ),
+                (
+                    "subject",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="lesson_events",
+                        to="cursus.subject",
+                        verbose_name="Subject",
+                    ),
+                ),
+                (
+                    "teachers",
+                    models.ManyToManyField(
+                        blank=True,
+                        related_name="lesson_events_as_teacher",
+                        to="core.person",
+                        verbose_name="Teachers",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Lesson Event",
+                "verbose_name_plural": "Lesson Events",
+            },
+            bases=("core.calendarevent",),
+        ),
+        migrations.CreateModel(
+            name="SupervisionEvent",
+            fields=[
+                (
+                    "lessonevent_ptr",
+                    models.OneToOneField(
+                        auto_created=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        parent_link=True,
+                        primary_key=True,
+                        serialize=False,
+                        to="chronos.lessonevent",
+                    ),
+                ),
+            ],
+            options={
+                "abstract": False,
+            },
+            bases=("chronos.lessonevent",),
+        ),
+    ]
diff --git a/aleksis/apps/chronos/migrations/0015_add_managed_by_app_label.py b/aleksis/apps/chronos/migrations/0015_add_managed_by_app_label.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4906fcd7eefb3fffd36136c75fa8d4feee76cc8
--- /dev/null
+++ b/aleksis/apps/chronos/migrations/0015_add_managed_by_app_label.py
@@ -0,0 +1,96 @@
+# Generated by Django 4.2.3 on 2023-07-27 13:35
+
+import aleksis.core.managers
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('sites', '0002_alter_domain_unique'),
+        ('chronos', '0014_lessonevent'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='absence',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='absencereason',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='break',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='event',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='exam',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='extralesson',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='holiday',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='lesson',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='lessonperiod',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='lessonsubstitution',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='subject',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='supervision',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='supervisionarea',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='supervisionsubstitution',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='timeperiod',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+        migrations.AddField(
+            model_name='validityrange',
+            name='managed_by_app_label',
+            field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'),
+        ),
+    ]
diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index ed6905004d257c6e89bcf22ef0b87cd1a1a248e5..11ba2702e648d0eff7e6eb5c29a3748a8f8329c5 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -1,7 +1,7 @@
 # flake8: noqa: DJ01
-
 from __future__ import annotations
 
+import itertools
 import os
 from datetime import date, datetime, time, timedelta
 from itertools import chain
@@ -42,6 +42,7 @@ from aleksis.apps.chronos.managers import (
     ExtraLessonQuerySet,
     GroupPropertiesMixin,
     HolidayQuerySet,
+    LessonEventQuerySet,
     LessonPeriodManager,
     LessonPeriodQuerySet,
     LessonSubstitutionManager,
@@ -60,14 +61,16 @@ from aleksis.apps.chronos.mixins import (
 from aleksis.apps.chronos.util.change_tracker import _get_substitution_models, substitutions_changed
 from aleksis.apps.chronos.util.date import get_current_year
 from aleksis.apps.chronos.util.format import format_m2m
+from aleksis.apps.cursus import models as cursus_models
+from aleksis.apps.cursus.models import Course
 from aleksis.apps.resint.models import LiveDocument
-from aleksis.core.managers import CurrentSiteManagerWithoutMigrations
+from aleksis.core.managers import CurrentSiteManagerWithoutMigrations, PolymorphicCurrentSiteManager
 from aleksis.core.mixins import (
     ExtensibleModel,
     GlobalPermissionModel,
     SchoolTermRelatedExtensibleModel,
 )
-from aleksis.core.models import DashboardWidget, Group, Room, SchoolTerm
+from aleksis.core.models import CalendarEvent, DashboardWidget, Group, Person, Room, SchoolTerm
 from aleksis.core.util.core_helpers import has_person
 from aleksis.core.util.pdf import generate_pdf_from_template
 
@@ -1362,3 +1365,274 @@ class ChronosGlobalPermissions(GlobalPermissionModel):
             ("view_lessons_day", _("Can view all lessons per day")),
             ("view_supervisions_day", _("Can view all supervisions per day")),
         )
+
+
+class LessonEvent(CalendarEvent):
+    name = "lesson"
+    verbose_name = _("Lessons")
+
+    objects = PolymorphicCurrentSiteManager.from_queryset(LessonEventQuerySet)()
+
+    title = models.CharField(verbose_name=_("Name"), max_length=255, blank=True)
+
+    course = models.ForeignKey(
+        Course, on_delete=models.CASCADE, verbose_name=_("Course"), null=True, blank=True
+    )
+
+    groups = models.ManyToManyField(
+        Group,
+        related_name="lesson_events",
+        verbose_name=_("Groups"),
+        blank=True,
+    )
+
+    rooms = models.ManyToManyField(
+        Room,
+        verbose_name=_("Rooms"),
+        related_name="lesson_events",
+        blank=True,
+    )
+    teachers = models.ManyToManyField(
+        Person,
+        verbose_name=_("Teachers"),
+        related_name="lesson_events_as_teacher",
+        blank=True,
+    )
+    subject = models.ForeignKey(
+        cursus_models.Subject,
+        on_delete=models.CASCADE,
+        verbose_name=_("Subject"),
+        related_name="lesson_events",
+        blank=True,
+        null=True,
+    )
+
+    cancelled = models.BooleanField(
+        default=False,
+        verbose_name=_("Cancelled"),
+    )
+
+    comment = models.TextField(
+        verbose_name=_("Comment"),
+        blank=True,
+    )
+
+    @property
+    def actual_groups(self: "LessonEvent"):
+        return self.groups.all() if self.amends else self.real_amends.groups.all()
+
+    @property
+    def all_members(self: "LessonEvent") -> list[Person]:
+        return list(itertools.chain(*[list(g.members.all()) for g in self.actual_groups]))
+
+    @property
+    def all_teachers(self: "LessonEvent") -> list[Person]:
+        all_teachers = list(self.teachers.all())
+        if self.amends:
+            all_teachers += list(self.real_amends.teachers.all())
+        return all_teachers
+
+    @property
+    def group_names(self: "LessonEvent") -> str:
+        return ", ".join([g.name for g in self.actual_groups])
+
+    @property
+    def teacher_names(self: "LessonEvent") -> str:
+        return ", ".join([t.full_name for t in self.teachers.all()])
+
+    @property
+    def room_names(self: "LessonEvent") -> str:
+        return ", ".join([r.name for r in self.rooms.all()])
+
+    @property
+    def room_names_with_amends(self: "LessonEvent") -> str:
+        my_room_names = self.room_names
+        amended_room_names = self.real_amends.room_names if self.amends else ""
+
+        if my_room_names and amended_room_names:
+            return _("{} (instead of {})").format(my_room_names, amended_room_names)
+        elif not my_room_names and amended_room_names:
+            return amended_room_names
+        return my_room_names
+
+    @property
+    def teacher_names_with_amends(self: "LessonEvent") -> str:
+        my_teacher_names = self.teacher_names
+        amended_teacher_names = self.real_amends.teacher_names if self.amends else ""
+
+        if my_teacher_names and amended_teacher_names:
+            return _("{} (instead of {})").format(my_teacher_names, amended_teacher_names)
+        elif not my_teacher_names and amended_teacher_names:
+            return amended_teacher_names
+        return my_teacher_names
+
+    @property
+    def subject_name_with_amends(self: "LessonEvent") -> str:
+        my_subject = self.subject.name if self.subject else ""
+        amended_subject = self.real_amends.subject.name if self.amends else ""
+
+        if my_subject and amended_subject:
+            return _("{} (instead of {})").format(my_subject, amended_subject)
+        elif not my_subject and amended_subject:
+            return amended_subject
+        elif my_subject:
+            return my_subject
+        return _("Lesson")
+
+    @property
+    def real_amends(self: "LessonEvent") -> "LessonEvent":
+        # FIXME THIS IS AWFUL SLOW
+        if self.amends:
+            return LessonEvent.objects.get(pk=self.amends.pk)
+        return self
+
+    @classmethod
+    def value_title(cls, reference_object: "LessonEvent", request) -> str:
+        """Get the title of the event."""
+        if reference_object.title:
+            return reference_object.title
+        elif reference_object.subject or (
+            reference_object.amends and reference_object.real_amends.subject
+        ):
+            title = reference_object.subject_name_with_amends
+            if request.user.person in reference_object.teachers.all():
+                title += " · " + reference_object.group_names
+            else:
+                title += " · " + reference_object.teacher_names_with_amends
+            if reference_object.rooms.all().exists():
+                title += " · " + reference_object.room_names_with_amends
+            return title
+
+        return _("Lesson")
+
+    @classmethod
+    def value_description(cls, reference_object: "LessonEvent", request) -> str:
+        return render_to_string("chronos/lesson_event_description.txt", {"event": reference_object})
+
+    @classmethod
+    def value_color(cls, reference_object: "LessonEvent", request) -> str:
+        """Get the color of the event."""
+        if reference_object.cancelled:
+            return "#eeeeee"
+        if reference_object.subject:
+            return reference_object.subject.colour_bg
+        if reference_object.amends and reference_object.real_amends.subject:
+            return reference_object.real_amends.subject.colour_bg
+        return super().value_color(reference_object, request)
+
+    @classmethod
+    def value_attendee(cls, reference_object: "LessonEvent", request) -> str:
+        """Get the attendees of the event."""
+        attendees = [t.get_vcal_address(role="CHAIR") for t in reference_object.teachers.all()]
+        return [a for a in attendees if a]
+
+    @classmethod
+    def value_location(cls, reference_object: "LessonEvent", request) -> str:
+        """Get the location of the event."""
+        return ", ".join([r.name for r in reference_object.rooms.all()])
+
+    @classmethod
+    def value_status(cls, reference_object: "LessonEvent", request) -> str:
+        """Get the status of the event."""
+        if reference_object.cancelled:
+            return "CANCELLED"
+        return "CONFIRMED"
+
+    @classmethod
+    def value_meta(cls, reference_object: "LessonEvent", request) -> str:
+        """Get the meta of the event."""
+        real_amends = reference_object.real_amends
+
+        return {
+            "id": reference_object.id,
+            "amended": bool(reference_object.amends),
+            "amends": cls.value_meta(real_amends, request) if reference_object.amends else None,
+            "teachers": [
+                {
+                    "id": t.pk,
+                    "first_name": t.first_name,
+                    "last_name": t.last_name,
+                    "full_name": t.full_name,
+                    "short_name": t.short_name,
+                }
+                for t in reference_object.teachers.all()
+            ],
+            "is_teacher": request.user.person in reference_object.all_teachers,
+            "groups": [
+                {"id": g.pk, "name": g.name, "short_name": g.short_name}
+                for g in reference_object.actual_groups
+            ],
+            "is_member": request.user.person in reference_object.all_members,
+            "rooms": [
+                {"id": r.pk, "name": r.name, "short_name": r.short_name}
+                for r in reference_object.rooms.all()
+            ],
+            "subject": {
+                "id": reference_object.subject.pk,
+                "name": reference_object.subject.name,
+                "short_name": reference_object.subject.short_name,
+                "colour_fg": reference_object.subject.colour_fg,
+                "colour_bg": reference_object.subject.colour_bg,
+            }
+            if reference_object.subject
+            else None,
+            "comment": reference_object.comment,
+            "cancelled": reference_object.cancelled,
+        }
+
+    @classmethod
+    def get_objects(cls, request, params=None) -> Iterable:
+        """Return all objects that should be included in the calendar."""
+        objs = super().get_objects(request, params).not_instance_of(SupervisionEvent)
+        if params:
+            obj_id = int(params.get("id", 0))
+            type = params.get("type", None)
+
+            if type and obj_id:
+                if type == "TEACHER":
+                    return objs.for_teacher(obj_id)
+                elif type == "GROUP":
+                    return objs.for_group(obj_id)
+                elif type == "ROOM":
+                    return objs.for_room(obj_id)
+        return objs.for_person(request.user.person)
+
+    class Meta:
+        verbose_name = _("Lesson Event")
+        verbose_name_plural = _("Lesson Events")
+
+
+class SupervisionEvent(LessonEvent):
+    name = "supervision"
+    verbose_name = _("Supervisions")
+
+    objects = PolymorphicCurrentSiteManager.from_queryset(LessonEventQuerySet)()
+
+    @classmethod
+    def value_title(cls, reference_object: "LessonEvent", request) -> str:
+        """Get the title of the event."""
+
+        return _("Supervision: {}").format(reference_object.room_names)
+
+    @classmethod
+    def value_description(cls, reference_object: "LessonEvent", request) -> str:
+        return render_to_string(
+            "chronos/supervision_event_description.txt", {"event": reference_object}
+        )
+
+    @classmethod
+    def get_objects(cls, request, params=None) -> Iterable:
+        """Return all objects that should be included in the calendar."""
+        objs = cls.objects.instance_of(cls)
+        if params:
+            obj_id = int(params.get("id", 0))
+            type = params.get("type", None)
+
+            if type and obj_id:
+                if type == "TEACHER":
+                    return objs.for_teacher(obj_id)
+                elif type == "GROUP":
+                    return objs.for_group(obj_id)
+                elif type == "ROOM":
+                    return objs.for_room(obj_id)
+        return objs.for_person(request.user.person)
diff --git a/aleksis/apps/chronos/schema/__init__.py b/aleksis/apps/chronos/schema/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f7cc25ff46c7fe7018876cc782fb6ad4e92e4eca
--- /dev/null
+++ b/aleksis/apps/chronos/schema/__init__.py
@@ -0,0 +1,193 @@
+from datetime import timezone
+
+import graphene
+from graphene_django import DjangoObjectType
+from graphene_django_cud.mutations import DjangoCreateMutation, DjangoPatchMutation
+
+from aleksis.core.models import CalendarEvent, Group, Person, Room
+from aleksis.core.schema.base import DeleteMutation
+
+from ..models import LessonEvent
+from ..util.chronos_helpers import get_classes, get_rooms, get_teachers
+
+
+class TimetablePersonType(DjangoObjectType):
+    class Meta:
+        model = Person
+        fields = ("id", "first_name", "last_name", "short_name")
+        skip_registry = True
+
+
+class TimetableGroupType(DjangoObjectType):
+    class Meta:
+        model = Group
+        fields = ("id", "name", "short_name")
+        skip_registry = True
+
+
+class TimetableRoomType(DjangoObjectType):
+    class Meta:
+        model = Room
+        fields = ("id", "name", "short_name")
+        skip_registry = True
+
+
+# There is another unrelated CalendarEventType in aleksis/core/schema/calendar
+# This CalendarEventType is needed for the inherited amends field of LessonEvent
+# to work in the graphql query.
+class CalendarEventForLessonEventType(DjangoObjectType):
+    class Meta:
+        model = CalendarEvent
+        fields = ("id", "amends", "datetime_start", "datetime_end")
+
+
+class LessonEventType(DjangoObjectType):
+    class Meta:
+        model = LessonEvent
+        fields = (
+            "id",
+            "amends",
+            "datetime_start",
+            "datetime_end",
+            "subject",
+            "teachers",
+            "groups",
+            "rooms",
+            "cancelled",
+            "comment",
+        )
+
+
+class DatetimeTimezoneMixin:
+    """Handle datetimes for mutations with CalendarEvent objects.
+
+    This is necessary because the client sends timezone information as
+    ISO string which only includes an offset (+00:00 UTC) and an
+    offset is not a valid timezone. Instead we set UTC as timezone
+    here directly.
+    """
+
+    @classmethod
+    def handle_datetime_start(cls, value, name, info) -> int:
+        value = value.replace(tzinfo=timezone.utc)
+        return value
+
+    @classmethod
+    def handle_datetime_end(cls, value, name, info) -> int:
+        value = value.replace(tzinfo=timezone.utc)
+        return value
+
+    @classmethod
+    def before_save(cls, root, info, input, obj, patch_obj=False):
+        # before_save has different signatures for different mutations
+        # This handles create & patch
+        # https://graphene-django-cud.readthedocs.io/en/latest/guide/other-hooks.html?highlight=before_save#before-save
+
+        if patch_obj:
+            obj = patch_obj
+            
+        obj.timezone = obj.amends.timezone
+        return obj
+
+
+class AmendLessonCreateMutation(DatetimeTimezoneMixin, DjangoCreateMutation):
+    class Meta:
+        model = LessonEvent
+        permissions = ("chronos.edit_substitution_rule",)
+        only_fields = (
+            "amends",
+            "datetime_start",
+            "datetime_end",
+            "subject",
+            "teachers",
+            "groups",
+            "rooms",
+            "cancelled",
+            "comment",
+        )
+
+
+class AmendLessonPatchMutation(DatetimeTimezoneMixin, DjangoPatchMutation):
+    class Meta:
+        model = LessonEvent
+        permissions = ("chronos.edit_substitution_rule",)
+        only_fields = ("subject", "teachers", "groups", "rooms", "cancelled", "comment")
+
+
+class AmendLessonDeleteMutation(DeleteMutation):
+    klass = LessonEvent
+    permission_required = "chronos.edit_substitution_rule"
+
+
+class TimetableType(graphene.Enum):
+    TEACHER = "teacher"
+    GROUP = "group"
+    ROOM = "room"
+
+
+class TimetableObjectType(graphene.ObjectType):
+    id = graphene.String()  # noqa
+    obj_id = graphene.String()
+    name = graphene.String()
+    short_name = graphene.String()
+    type = graphene.Field(TimetableType)  # noqa
+
+    def resolve_obj_id(root, info, **kwargs):
+        return root.id
+
+    def resolve_id(root, info, **kwargs):
+        return f"{root.type.value}-{root.id}"
+
+
+class Query(graphene.ObjectType):
+    timetable_teachers = graphene.List(TimetablePersonType)
+    timetable_groups = graphene.List(TimetableGroupType)
+    timetable_rooms = graphene.List(TimetableRoomType)
+    available_timetables = graphene.List(TimetableObjectType)
+
+    def resolve_timetable_teachers(self, info, **kwargs):
+        return get_teachers(info.context.user)
+
+    def resolve_timetable_groups(self, info, **kwargs):
+        return get_classes(info.context.user)
+
+    def resolve_timetable_rooms(self, info, **kwargs):
+        return get_rooms(info.context.user)
+
+    def resolve_available_timetables(self, info, **kwargs):
+        all_timetables = []
+        for group in get_classes(info.context.user):
+            all_timetables.append(
+                TimetableObjectType(
+                    id=group.id,
+                    name=group.name,
+                    short_name=group.short_name,
+                    type=TimetableType.GROUP,
+                )
+            )
+
+        for teacher in get_teachers(info.context.user):
+            print(teacher.full_name)
+            all_timetables.append(
+                TimetableObjectType(
+                    id=teacher.id,
+                    name=teacher.full_name,
+                    short_name=teacher.short_name,
+                    type=TimetableType.TEACHER,
+                )
+            )
+
+        for room in get_rooms(info.context.user):
+            all_timetables.append(
+                TimetableObjectType(
+                    id=room.id, name=room.name, short_name=room.short_name, type=TimetableType.ROOM
+                )
+            )
+
+        return all_timetables
+
+
+class Mutation(graphene.ObjectType):
+    create_amend_lesson = AmendLessonCreateMutation.Field()
+    patch_amend_lesson = AmendLessonPatchMutation.Field()
+    delete_amend_lesson = AmendLessonDeleteMutation.Field()
diff --git a/aleksis/apps/chronos/templates/chronos/lesson_event_description.txt b/aleksis/apps/chronos/templates/chronos/lesson_event_description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..573c92354535f0f4a5ed029e9efab63bc71c3639
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/lesson_event_description.txt
@@ -0,0 +1,6 @@
+{% load i18n %}{% trans "Groups" %}: {{ event.group_names|default:"–" }}{% if event.subject %}
+{% trans "Subject" %}: {{ event.subject_name_with_amends }}{% endif %}
+{% trans "Teachers" %}: {{ event.teacher_names_with_amends|default:"–" }}
+{% trans "Rooms" %}: {{ event.room_names_with_amends|default:"–" }}{% if event.comment %}
+
+{{ event.comment }}{% endif %}
\ No newline at end of file
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
index a80afb98487308b20733e1f23cee8a29821b1eb0..94f2d3574992d23dffe3d6f9b03ffdd3e75cb042 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
@@ -1,6 +1,6 @@
 {% if type == "substitution" %}
   {% if el.cancelled or el.cancelled_for_teachers %}
-    {# Canceled lesson: no room #}
+    {# Cancelled lesson: no room #}
   {% elif el.room and el.lesson_period.room %}
     {# New and old room available #}
     <span class="tooltipped" data-position="bottom"
diff --git a/aleksis/apps/chronos/templates/chronos/supervision_event_description.txt b/aleksis/apps/chronos/templates/chronos/supervision_event_description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d2f311f240e5cf527033bc08de15869085dc0ee5
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/supervision_event_description.txt
@@ -0,0 +1,2 @@
+{% load i18n %}{% trans "Teachers" %}: {{ event.teacher_names }}
+{% trans "Areas" %}: {{ event.room_names }}
\ No newline at end of file
diff --git a/aleksis/apps/chronos/tests/regression/test_regression.py b/aleksis/apps/chronos/tests/regression/test_regression.py
deleted file mode 100644
index 0b4a71838cdfb1fafe2600022868558d70b9bd14..0000000000000000000000000000000000000000
--- a/aleksis/apps/chronos/tests/regression/test_regression.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from datetime import time, timedelta
-
-from django.contrib.auth import get_user_model
-from django.utils import timezone
-
-import pytest
-
-from aleksis.apps.chronos.util.chronos_helpers import get_rooms, get_teachers
-from aleksis.core.models import Group, Person, Room, SchoolTerm
-
-pytestmark = pytest.mark.django_db
-
-
-from aleksis.apps.chronos.models import Lesson, LessonPeriod, Subject, TimePeriod, ValidityRange
-
-
-def test_rooms_teachers_only_from_current_school_term():
-    User = get_user_model()
-
-    user = User.objects.create(username="test", is_staff=True, is_superuser=True)
-    person_user = Person.objects.create(user=user, first_name="Test", last_name="User")
-
-    correct_school_term = SchoolTerm.objects.create(
-        date_start=timezone.now() - timedelta(days=1),
-        date_end=timezone.now() + timedelta(days=1),
-        name="Correct school term",
-    )
-    wrong_school_term = SchoolTerm.objects.create(
-        date_start=timezone.now() - timedelta(days=3),
-        date_end=timezone.now() - timedelta(days=2),
-        name="Wrong school term",
-    )
-
-    correct_validity = ValidityRange.objects.create(
-        school_term=correct_school_term,
-        date_start=correct_school_term.date_start,
-        date_end=correct_school_term.date_end,
-        name="Correct validity",
-    )
-    wrong_validity = ValidityRange.objects.create(
-        school_term=wrong_school_term,
-        date_start=wrong_school_term.date_start,
-        date_end=wrong_school_term.date_end,
-        name="Wrong validity",
-    )
-
-    subject = Subject.objects.create(name="Test subject", short_name="TS")
-    time_period = TimePeriod.objects.create(
-        weekday=0, period=1, time_start=time(8, 0), time_end=time(9, 0)
-    )
-
-    correct_person = Person.objects.create(first_name="Correct", last_name="Person")
-    wrong_person = Person.objects.create(first_name="Wrong", last_name="Person")
-
-    correct_lesson = Lesson.objects.create(validity=correct_validity, subject=subject)
-    correct_lesson.teachers.add(correct_person)
-    wrong_lesson = Lesson.objects.create(validity=wrong_validity, subject=subject)
-    wrong_lesson.teachers.add(wrong_person)
-
-    correct_room = Room.objects.create(name="Correct room", short_name="cr")
-    wrong_room = Room.objects.create(name="Wrong room", short_name="wr")
-
-    correct_lesson_period = LessonPeriod.objects.create(
-        lesson=correct_lesson, period=time_period, room=correct_room
-    )
-    wrong_lesson_period = LessonPeriod.objects.create(
-        lesson=wrong_lesson, period=time_period, room=wrong_room
-    )
-
-    rooms = get_rooms(user)
-    assert correct_room in rooms
-    assert wrong_room not in rooms
-
-    teachers = get_teachers(user)
-    assert correct_person in teachers
-    assert wrong_person not in teachers
diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py
index 17d98c54a88e28c9be5ca81d8561924d4155c83a..5664c0b97e13e68ab18120a2cadfd39396eaa8a4 100644
--- a/aleksis/apps/chronos/util/chronos_helpers.py
+++ b/aleksis/apps/chronos/util/chronos_helpers.py
@@ -9,7 +9,7 @@ from django.utils import timezone
 
 from guardian.core import ObjectPermissionChecker
 
-from aleksis.core.models import Announcement, Group, Person, Room, SchoolTerm
+from aleksis.core.models import Announcement, Group, Person, Room
 from aleksis.core.util.core_helpers import get_site_preferences
 from aleksis.core.util.predicates import check_global_permission
 
@@ -74,10 +74,8 @@ def get_teachers(user: "User"):
     """Get the teachers whose timetables are allowed to be seen by current user."""
     checker = ObjectPermissionChecker(user)
 
-    school_term = SchoolTerm.current
-    school_term_q = Q(lessons_as_teacher__validity__school_term=school_term) if school_term else Q()
     teachers = (
-        Person.objects.annotate(lessons_count=Count("lessons_as_teacher", filter=school_term_q))
+        Person.objects.annotate(lessons_count=Count("lesson_events_as_teacher"))
         .filter(lessons_count__gt=0)
         .order_by("short_name", "last_name")
     )
@@ -93,6 +91,8 @@ def get_teachers(user: "User"):
 
         teachers = teachers.filter(Q(pk=user.person.pk) | Q(pk__in=wanted_teachers))
 
+    teachers = teachers.distinct()
+
     return teachers
 
 
@@ -103,8 +103,8 @@ def get_classes(user: "User"):
     classes = (
         Group.objects.for_current_school_term_or_all()
         .annotate(
-            lessons_count=Count("lessons"),
-            child_lessons_count=Count("child_groups__lessons"),
+            lessons_count=Count("lesson_events"),
+            child_lessons_count=Count("child_groups__lesson_events"),
         )
         .filter(
             Q(lessons_count__gt=0, parent_groups=None)
@@ -128,6 +128,8 @@ def get_classes(user: "User"):
         if user.person.primary_group:
             classes = classes.filter(Q(pk=user.person.primary_group.pk))
 
+    classes = classes.distinct()
+
     return classes
 
 
@@ -135,13 +137,8 @@ def get_rooms(user: "User"):
     """Get the rooms whose timetables are allowed to be seen by current user."""
     checker = ObjectPermissionChecker(user)
 
-    school_term = SchoolTerm.current
-    school_term_q = (
-        Q(lesson_periods__lesson__validity__school_term=school_term) if school_term else Q()
-    )
-
     rooms = (
-        Room.objects.annotate(lessons_count=Count("lesson_periods", filter=school_term_q))
+        Room.objects.annotate(lessons_count=Count("lesson_events"))
         .filter(lessons_count__gt=0)
         .order_by("short_name", "name")
     )
@@ -157,6 +154,8 @@ def get_rooms(user: "User"):
 
         rooms = rooms.filter(Q(pk__in=wanted_rooms))
 
+    rooms = rooms.distinct()
+
     return rooms
 
 
diff --git a/fixtures/highschool-de-20212022_short.json b/fixtures/highschool-de-20212022_short.json
new file mode 100644
index 0000000000000000000000000000000000000000..1faf32e3bbc6607475a42e8d3ca714fdd653e87a
--- /dev/null
+++ b/fixtures/highschool-de-20212022_short.json
@@ -0,0 +1,3088 @@
+[
+  {
+    "model": "auth.user",
+    "pk": 2,
+    "fields": {
+      "password": "!kOVrghAiFaFTsTSTrU96OGEFB4vvhe4KLo9ASH4o",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "?",
+      "first_name": "?",
+      "last_name": "?",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:04.997Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 3,
+    "fields": {
+      "password": "!B4NXmwHzWuvGMfdtRx0agEYe2UEOrJBmPfr6cHDs",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "alb",
+      "first_name": "Carl",
+      "last_name": "Albrecht",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.045Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 4,
+    "fields": {
+      "password": "!GGy8AQjJcmPtqGtZKfeUyE4glGfvZEL6g4CmCObW",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "ald",
+      "first_name": "Tobias",
+      "last_name": "Arnold",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.048Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 5,
+    "fields": {
+      "password": "!QrBCVfLfVPTJHdQIDAcgynKvqk82MZnKHt23ajEY",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bar",
+      "first_name": "Anna",
+      "last_name": "Bauer",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.050Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 6,
+    "fields": {
+      "password": "!TEPmv5Y6SG2pSB2l3v4dTOR8GB6I3k1ph2B21VQ9",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bau",
+      "first_name": "Anna-Lena",
+      "last_name": "Baumann",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.053Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 7,
+    "fields": {
+      "password": "!H2w4tzXb9USp2lccHanAqdgcIAWpk1fDr4OHKutg",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bam",
+      "first_name": "Werner",
+      "last_name": "Baumann",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.056Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 8,
+    "fields": {
+      "password": "!R2SMsUgP2XE5UCVELhCwaAXVkRuBU4CQfbh5YdWq",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bec",
+      "first_name": "Ella",
+      "last_name": "Beck",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.059Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 9,
+    "fields": {
+      "password": "!vkBLfDfinfpcUgoQ20hXQYjBTU93oaTKX9DQVNN3",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bek",
+      "first_name": "Herrmann",
+      "last_name": "Becker",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.062Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 10,
+    "fields": {
+      "password": "!Ku1Shj80attBZOufvRUXP3U908WAh2Wxhe6Lt6UC",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "beg",
+      "first_name": "Georg",
+      "last_name": "Berger",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.065Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 11,
+    "fields": {
+      "password": "!dI923sXrtSeZlW6TSpNmGABySLEi1iFtaNW9ZtaS",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "ber",
+      "first_name": "Stefan",
+      "last_name": "Berger",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.068Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 12,
+    "fields": {
+      "password": "!sTXSXu2O0QITR2t5sfPEqdn8J63bW0mcZvTW4kiP",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "kam",
+      "first_name": "Stefan",
+      "last_name": "Berger",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.071Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 13,
+    "fields": {
+      "password": "!F05GGoRuP83XO4UdrTdwsCv2zabOWolsr95UrIET",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bem",
+      "first_name": "Johanna",
+      "last_name": "Bergmann",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.075Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 14,
+    "fields": {
+      "password": "!MKF0i9NGbRximlktRwwetz76hNo2ck0wNeyieMqU",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "boh",
+      "first_name": "Ilse",
+      "last_name": "Bohr",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.078Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 15,
+    "fields": {
+      "password": "!krWVzD4EcFUEvDvUd2oWK7eJdJoafDm7ktEEt1RD",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bra",
+      "first_name": "Helene",
+      "last_name": "Brandt",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.080Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 16,
+    "fields": {
+      "password": "!IDWlZkdySSkpNJuD9oTc3KWhGjWMYYWxx28il6cC",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bdt",
+      "first_name": "Philipp",
+      "last_name": "Brandt",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.082Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 17,
+    "fields": {
+      "password": "!gWHQ2ClDCw9MIT6g4kCKMDzOMymGVS0qw8NAFias",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bru",
+      "first_name": "Clara",
+      "last_name": "Braun",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.086Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 18,
+    "fields": {
+      "password": "!hBCFGb0NuJyHjJxo0a3QkZDunzYZ0JFBAj6MAdSs",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "bus",
+      "first_name": "Frieda",
+      "last_name": "Busch",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.088Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 19,
+    "fields": {
+      "password": "!q5B1aQZXgA6c7vqBelhF9fxFDKobu5W0Z1AtBcep",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "die",
+      "first_name": "Ursula",
+      "last_name": "Dietrich",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.091Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 20,
+    "fields": {
+      "password": "!TFE3JM6nZgwGab0omHCFuXQZWsllVfOkskrEnk4m",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "eng",
+      "first_name": "Lotte",
+      "last_name": "Engel",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.093Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 21,
+    "fields": {
+      "password": "!Rn25F7BOjekvA0W4DqHxE9hHuTyne8yPkLkPhJ2G",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "ern",
+      "first_name": "Kurt",
+      "last_name": "Ernst",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.096Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 22,
+    "fields": {
+      "password": "!h5gXbGGCuJGlhDjFvfFbXRCOBeyLwaOVaOOGmHH0",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "fis",
+      "first_name": "Alexander",
+      "last_name": "Fischer",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.098Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 23,
+    "fields": {
+      "password": "!iWthkBJYSKH0IgR8qHVDQUQxFlO9MTzbtTx6xkr4",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "fra",
+      "first_name": "Lene",
+      "last_name": "Frank",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.100Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 24,
+    "fields": {
+      "password": "!FEMVVShg0YpqhxWYGbn7kKjWnAXuCkWGTHEwC9DP",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "fre",
+      "first_name": "Sebastian",
+      "last_name": "Franke",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.102Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 25,
+    "fields": {
+      "password": "!VE1EIxTsx24laMGTynWoAoP5oAGryluDPwz42Or0",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "fri",
+      "first_name": "Gabriele",
+      "last_name": "Friedrich",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.105Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 26,
+    "fields": {
+      "password": "!bBO37upTrg6DZ9Hp74Up3iQBeS4QT8YN1KfbLxNZ",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "fuc",
+      "first_name": "Karsten",
+      "last_name": "Fuchs",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.109Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 27,
+    "fields": {
+      "password": "!i69z1Y7rxPhs8l19cDLJoWuFwdkd3hhaXLeQ0ydS",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "gra",
+      "first_name": "Maja",
+      "last_name": "Graf",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.112Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 28,
+    "fields": {
+      "password": "!1Txl1BNrE4tgoIvqtTA3WZp4BGSz7NQJMBEq3uPF",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "gue",
+      "first_name": "Nina",
+      "last_name": "G\u00fcnther",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.114Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 29,
+    "fields": {
+      "password": "!jXSLoz6BEASc9zqMdPF2KcmN3Cwatm5VATZ565rM",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "haa",
+      "first_name": "Greta",
+      "last_name": "Haas",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.116Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 30,
+    "fields": {
+      "password": "!hP5qJsooLG28yfnRymEjJF0qHzLUVnqIsyNB3CMe",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "hah",
+      "first_name": "Michael",
+      "last_name": "Hahn",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.119Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 31,
+    "fields": {
+      "password": "!DWb4yp3P1Xh3S2FLH1AhxqsTuWDtWhtw5cCl2ZY2",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "hei",
+      "first_name": "Constanze",
+      "last_name": "Heinrich",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.121Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 32,
+    "fields": {
+      "password": "!PWkxrNP1FPx2094FcRwUbohVyz9cVv82koiKXvVo",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "her",
+      "first_name": "Karla",
+      "last_name": "Hermann",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.124Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 33,
+    "fields": {
+      "password": "!FFljZ20cxb4AKPyU4sw4hJhSYJJxEcYWNx9XOeui",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "hor",
+      "first_name": "Lukas",
+      "last_name": "Horstmann",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.126Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 34,
+    "fields": {
+      "password": "!g2BwbxLeNA7alWX1lV0Xmx4bZ7UxgayusAiaDt0Q",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "hub",
+      "first_name": "Frank",
+      "last_name": "Hubert",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:30:05.129Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 35,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "jan",
+      "first_name": "Markus",
+      "last_name": "Jansen",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.698Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 36,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "jen",
+      "first_name": "Christiane",
+      "last_name": "Jenner",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.709Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 37,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "jun",
+      "first_name": "Christoph",
+      "last_name": "Jung",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.714Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 38,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "kai",
+      "first_name": "Ida",
+      "last_name": "Kaiser",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.720Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 39,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "kel",
+      "first_name": "Oliver",
+      "last_name": "Keller",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.728Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 40,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "koc",
+      "first_name": "Norman",
+      "last_name": "Koch",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.733Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 41,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "krs",
+      "first_name": "Peter",
+      "last_name": "Kraus",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.738Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 42,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "kra",
+      "first_name": "Justina",
+      "last_name": "Krause",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.743Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 43,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "kr\u00fc",
+      "first_name": "Barbara",
+      "last_name": "Kr\u00fcger",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.750Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 44,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "kuh",
+      "first_name": "Nadine",
+      "last_name": "Kuhn",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.756Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 45,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "lan",
+      "first_name": "Ruth",
+      "last_name": "Lang",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.762Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 46,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "lor",
+      "first_name": "Matthias",
+      "last_name": "Lorenz",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.766Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 47,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "lud",
+      "first_name": "Hans",
+      "last_name": "Ludwig",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.773Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 48,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "lut",
+      "first_name": "Artur",
+      "last_name": "Lutz",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.779Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 49,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "may",
+      "first_name": "Elisa",
+      "last_name": "Mayer",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.784Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 50,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "nag",
+      "first_name": "Sven",
+      "last_name": "Nagel",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.791Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 51,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "ott",
+      "first_name": "Thomas",
+      "last_name": "Ott",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.797Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 52,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "pet",
+      "first_name": "Felicitas",
+      "last_name": "Peters",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.801Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 53,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "pol",
+      "first_name": "Diana",
+      "last_name": "Pohl",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.807Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 54,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "rot",
+      "first_name": "Laura",
+      "last_name": "Roth",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.813Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 55,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "sol",
+      "first_name": "Katja",
+      "last_name": "Scholz",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.819Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 56,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "sub",
+      "first_name": "Miriam",
+      "last_name": "Schubert",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.826Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 57,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "slt",
+      "first_name": "Kathrin",
+      "last_name": "Schulte",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.831Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 58,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "sul",
+      "first_name": "Paul",
+      "last_name": "Schulze",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.838Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 59,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "sus",
+      "first_name": "Lisa",
+      "last_name": "Schuster",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.842Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 60,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "swe",
+      "first_name": "Vera",
+      "last_name": "Schwenck",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.848Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 61,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "sei",
+      "first_name": "Patrick",
+      "last_name": "Seidel",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.854Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 62,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "sim",
+      "first_name": "Michaela",
+      "last_name": "Simon",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.859Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 63,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "som",
+      "first_name": "Dennis",
+      "last_name": "Sommer",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.863Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 64,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "stn",
+      "first_name": "Sarah",
+      "last_name": "Stein",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.868Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 65,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "vot",
+      "first_name": "Melanie",
+      "last_name": "Vogt",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.874Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 66,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "wal",
+      "first_name": "Marie",
+      "last_name": "Walter",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.879Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 67,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "wei",
+      "first_name": "Margarete",
+      "last_name": "Wei\u00df",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.886Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 68,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "win",
+      "first_name": "Horst",
+      "last_name": "Winkler",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.891Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "auth.user",
+    "pk": 69,
+    "fields": {
+      "password": "",
+      "last_login": null,
+      "is_superuser": false,
+      "username": "zie",
+      "first_name": "Julia",
+      "last_name": "Ziegler",
+      "email": "",
+      "is_staff": false,
+      "date_joined": "2021-10-12T16:31:17.895Z",
+      "groups": [],
+      "user_permissions": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 1,
+    "fields": {
+      "site": 1,
+      "extended_data": {},
+      "user": 1,
+      "first_name": "Admin",
+      "last_name": "istrator",
+      "additional_name": "",
+      "short_name": null,
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 69,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 1
+      },
+      "user": null,
+      "first_name": "Anna",
+      "last_name": "Bauer",
+      "additional_name": "",
+      "short_name": "BAR",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 70,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 2
+      },
+      "user": null,
+      "first_name": "Carl",
+      "last_name": "Albrecht",
+      "additional_name": "",
+      "short_name": "ALB",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 71,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 3
+      },
+      "user": null,
+      "first_name": "Werner",
+      "last_name": "Baumann",
+      "additional_name": "",
+      "short_name": "BAM",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 72,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 4
+      },
+      "user": null,
+      "first_name": "Herrmann",
+      "last_name": "Becker",
+      "additional_name": "",
+      "short_name": "BEK",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 73,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 5
+      },
+      "user": null,
+      "first_name": "Georg",
+      "last_name": "Berger",
+      "additional_name": "",
+      "short_name": "BEG",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 74,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 6
+      },
+      "user": null,
+      "first_name": "Johanna",
+      "last_name": "Bergmann",
+      "additional_name": "",
+      "short_name": "BEM",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 75,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 8
+      },
+      "user": null,
+      "first_name": "Ilse",
+      "last_name": "Bohr",
+      "additional_name": "",
+      "short_name": "BOH",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 76,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 10
+      },
+      "user": null,
+      "first_name": "Helene",
+      "last_name": "Brandt",
+      "additional_name": "",
+      "short_name": "BRA",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 77,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 11
+      },
+      "user": null,
+      "first_name": "Clara",
+      "last_name": "Braun",
+      "additional_name": "",
+      "short_name": "BRU",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 78,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 12
+      },
+      "user": null,
+      "first_name": "Frieda",
+      "last_name": "Busch",
+      "additional_name": "",
+      "short_name": "BUS",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 79,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 13
+      },
+      "user": null,
+      "first_name": "Ursula",
+      "last_name": "Dietrich",
+      "additional_name": "",
+      "short_name": "DIE",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 80,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 14
+      },
+      "user": null,
+      "first_name": "Katja",
+      "last_name": "Scholz",
+      "additional_name": "",
+      "short_name": "SOL",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 81,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 15
+      },
+      "user": null,
+      "first_name": "Lotte",
+      "last_name": "Engel",
+      "additional_name": "",
+      "short_name": "ENG",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 82,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 16
+      },
+      "user": null,
+      "first_name": "Kurt",
+      "last_name": "Ernst",
+      "additional_name": "",
+      "short_name": "ERN",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 83,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 17
+      },
+      "user": null,
+      "first_name": "Alexander",
+      "last_name": "Fischer",
+      "additional_name": "",
+      "short_name": "FIS",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 84,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 18
+      },
+      "user": null,
+      "first_name": "Lene",
+      "last_name": "Frank",
+      "additional_name": "",
+      "short_name": "FRA",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 85,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 19
+      },
+      "user": null,
+      "first_name": "Sebastian",
+      "last_name": "Franke",
+      "additional_name": "",
+      "short_name": "FRE",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 86,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 20
+      },
+      "user": null,
+      "first_name": "Gabriele",
+      "last_name": "Friedrich",
+      "additional_name": "",
+      "short_name": "FRI",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 87,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 21
+      },
+      "user": null,
+      "first_name": "Karsten",
+      "last_name": "Fuchs",
+      "additional_name": "",
+      "short_name": "FUC",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 88,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 22
+      },
+      "user": null,
+      "first_name": "Maja",
+      "last_name": "Graf",
+      "additional_name": "",
+      "short_name": "GRA",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 89,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 23
+      },
+      "user": null,
+      "first_name": "Nina",
+      "last_name": "G\u00fcnther",
+      "additional_name": "",
+      "short_name": "GUE",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 90,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 25
+      },
+      "user": null,
+      "first_name": "Greta",
+      "last_name": "Haas",
+      "additional_name": "",
+      "short_name": "HAA",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 91,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 27
+      },
+      "user": null,
+      "first_name": "Michael",
+      "last_name": "Hahn",
+      "additional_name": "",
+      "short_name": "HAH",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 92,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 28
+      },
+      "user": null,
+      "first_name": "Constanze",
+      "last_name": "Heinrich",
+      "additional_name": "",
+      "short_name": "HEI",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 93,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 30
+      },
+      "user": null,
+      "first_name": "Christiane",
+      "last_name": "Jenner",
+      "additional_name": "",
+      "short_name": "JEN",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 94,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 31
+      },
+      "user": null,
+      "first_name": "Barbara",
+      "last_name": "Kr\u00fcger",
+      "additional_name": "",
+      "short_name": "KR\u00dc",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 95,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 32
+      },
+      "user": null,
+      "first_name": "Norman",
+      "last_name": "Koch",
+      "additional_name": "",
+      "short_name": "KOC",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 96,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 33
+      },
+      "user": null,
+      "first_name": "Justina",
+      "last_name": "Krause",
+      "additional_name": "",
+      "short_name": "KRA",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 97,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 34
+      },
+      "user": null,
+      "first_name": "Oliver",
+      "last_name": "Keller",
+      "additional_name": "",
+      "short_name": "KEL",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 98,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 35
+      },
+      "user": null,
+      "first_name": "Christoph",
+      "last_name": "Jung",
+      "additional_name": "",
+      "short_name": "JUN",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 99,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 37
+      },
+      "user": null,
+      "first_name": "Frank",
+      "last_name": "Hubert",
+      "additional_name": "",
+      "short_name": "HUB",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 100,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 38
+      },
+      "user": null,
+      "first_name": "Karla",
+      "last_name": "Hermann",
+      "additional_name": "",
+      "short_name": "HER",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 101,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 39
+      },
+      "user": null,
+      "first_name": "Paul",
+      "last_name": "Schulze",
+      "additional_name": "",
+      "short_name": "SUL",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 102,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 40
+      },
+      "user": null,
+      "first_name": "Ida",
+      "last_name": "Kaiser",
+      "additional_name": "",
+      "short_name": "KAI",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 103,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 41
+      },
+      "user": null,
+      "first_name": "Ruth",
+      "last_name": "Lang",
+      "additional_name": "",
+      "short_name": "LAN",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 104,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 43
+      },
+      "user": null,
+      "first_name": "Felicitas",
+      "last_name": "Peters",
+      "additional_name": "",
+      "short_name": "PET",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 105,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 44
+      },
+      "user": null,
+      "first_name": "Miriam",
+      "last_name": "Schubert",
+      "additional_name": "",
+      "short_name": "SUB",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 106,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 45
+      },
+      "user": null,
+      "first_name": "Laura",
+      "last_name": "Roth",
+      "additional_name": "",
+      "short_name": "ROT",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 107,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 46
+      },
+      "user": null,
+      "first_name": "Stefan",
+      "last_name": "Berger",
+      "additional_name": "",
+      "short_name": "BER",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 108,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 47
+      },
+      "user": null,
+      "first_name": "Horst",
+      "last_name": "Winkler",
+      "additional_name": "",
+      "short_name": "WIN",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 109,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 48
+      },
+      "user": null,
+      "first_name": "Anna-Lena",
+      "last_name": "Baumann",
+      "additional_name": "",
+      "short_name": "BAU",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 110,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 49
+      },
+      "user": null,
+      "first_name": "Peter",
+      "last_name": "Kraus",
+      "additional_name": "",
+      "short_name": "KRS",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 111,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 50
+      },
+      "user": null,
+      "first_name": "Michaela",
+      "last_name": "Simon",
+      "additional_name": "",
+      "short_name": "SIM",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 112,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 51
+      },
+      "user": null,
+      "first_name": "Hans",
+      "last_name": "Ludwig",
+      "additional_name": "",
+      "short_name": "LUD",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 113,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 52
+      },
+      "user": null,
+      "first_name": "Melanie",
+      "last_name": "Vogt",
+      "additional_name": "",
+      "short_name": "VOT",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 114,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 53
+      },
+      "user": null,
+      "first_name": "Sarah",
+      "last_name": "Stein",
+      "additional_name": "",
+      "short_name": "STN",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 115,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 54
+      },
+      "user": null,
+      "first_name": "Dennis",
+      "last_name": "Sommer",
+      "additional_name": "",
+      "short_name": "SOM",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 116,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 55
+      },
+      "user": null,
+      "first_name": "Patrick",
+      "last_name": "Seidel",
+      "additional_name": "",
+      "short_name": "SEI",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 117,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 56
+      },
+      "user": null,
+      "first_name": "Julia",
+      "last_name": "Ziegler",
+      "additional_name": "",
+      "short_name": "ZIE",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 118,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 57
+      },
+      "user": null,
+      "first_name": "Philipp",
+      "last_name": "Brandt",
+      "additional_name": "",
+      "short_name": "BDT",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 119,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 58
+      },
+      "user": null,
+      "first_name": "Nadine",
+      "last_name": "Kuhn",
+      "additional_name": "",
+      "short_name": "KUH",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 120,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 59
+      },
+      "user": null,
+      "first_name": "Kathrin",
+      "last_name": "Schulte",
+      "additional_name": "",
+      "short_name": "SLT",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 121,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 60
+      },
+      "user": null,
+      "first_name": "Diana",
+      "last_name": "Pohl",
+      "additional_name": "",
+      "short_name": "POL",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 122,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 62
+      },
+      "user": null,
+      "first_name": "Tobias",
+      "last_name": "Arnold",
+      "additional_name": "",
+      "short_name": "ALD",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 123,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 63
+      },
+      "user": null,
+      "first_name": "Markus",
+      "last_name": "Jansen",
+      "additional_name": "",
+      "short_name": "JAN",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 124,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 65
+      },
+      "user": null,
+      "first_name": "Thomas",
+      "last_name": "Ott",
+      "additional_name": "",
+      "short_name": "OTT",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 125,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 66
+      },
+      "user": null,
+      "first_name": "Sven",
+      "last_name": "Nagel",
+      "additional_name": "",
+      "short_name": "NAG",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 126,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 67
+      },
+      "user": null,
+      "first_name": "Artur",
+      "last_name": "Lutz",
+      "additional_name": "",
+      "short_name": "LUT",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 127,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 68
+      },
+      "user": null,
+      "first_name": "?",
+      "last_name": "?",
+      "additional_name": "",
+      "short_name": "?",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 128,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 69
+      },
+      "user": null,
+      "first_name": "Marie",
+      "last_name": "Walter",
+      "additional_name": "",
+      "short_name": "WAL",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 129,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 70
+      },
+      "user": null,
+      "first_name": "Lukas",
+      "last_name": "Horstmann",
+      "additional_name": "",
+      "short_name": "HOR",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 130,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 71
+      },
+      "user": null,
+      "first_name": "Ella",
+      "last_name": "Beck",
+      "additional_name": "",
+      "short_name": "BEC",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 131,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 72
+      },
+      "user": null,
+      "first_name": "Matthias",
+      "last_name": "Lorenz",
+      "additional_name": "",
+      "short_name": "LOR",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 132,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 74
+      },
+      "user": null,
+      "first_name": "Elisa",
+      "last_name": "Mayer",
+      "additional_name": "",
+      "short_name": "MAY",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 133,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 75
+      },
+      "user": null,
+      "first_name": "Margarete",
+      "last_name": "Wei\u00df",
+      "additional_name": "",
+      "short_name": "WEI",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 134,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 76
+      },
+      "user": null,
+      "first_name": "Lisa",
+      "last_name": "Schuster",
+      "additional_name": "",
+      "short_name": "SUS",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 135,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 80
+      },
+      "user": null,
+      "first_name": "Stefan",
+      "last_name": "Berger",
+      "additional_name": "",
+      "short_name": "KAM",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  },
+  {
+    "model": "core.person",
+    "pk": 137,
+    "fields": {
+      "site": 1,
+      "extended_data": {
+        "import_ref_untis": 77
+      },
+      "user": null,
+      "first_name": "Vera",
+      "last_name": "Schwenck",
+      "additional_name": "",
+      "short_name": "SWE",
+      "street": "",
+      "housenumber": "",
+      "postal_code": "",
+      "place": "",
+      "phone_number": "",
+      "mobile_number": "",
+      "email": "",
+      "date_of_birth": null,
+      "sex": "",
+      "photo": "",
+      "primary_group": null,
+      "description": "",
+      "guardians": []
+    }
+  }
+]
diff --git a/pyproject.toml b/pyproject.toml
index 631675cb3535b98703b00078556adcf3519b6d0b..2d526d3c9f01fae6b832e332c0dbed0dfb926be4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "AlekSIS-App-Chronos"
-version = "3.0.3.dev0"
+version = "4.0.dev1"
 packages = [
     { include = "aleksis" }
 ]
@@ -50,8 +50,9 @@ priority = "supplemental"
 [tool.poetry.dependencies]
 python = "^3.9"
 calendarweek = "^0.5.0"
-aleksis-core = "^3.0"
-aleksis-app-resint = "^3.0"
+aleksis-core = "^4.0.0.dev0"
+aleksis-app-resint = "^4.0.dev0"
+aleksis-app-cursus = "^0.1.dev0"
 
 [tool.poetry.plugins."aleksis.app"]
 chronos = "aleksis.apps.chronos.apps:ChronosConfig"