# Datenmodell und Begriffe Dieses Dokument beschreibt die fachliche Bedeutung der zentralen Datensätze. Es soll verhindern, dass technisch ähnliche Felder als unterschiedliche Sachverhalte interpretiert oder dieselben Informationen mehrfach gespeichert werden. ## Lerngruppe (`LearningGroup`) Eine Lerngruppe ist die konkrete Unterrichtsgruppe eines Fachs in genau einem Schuljahr. Sie kann als Klasse oder Kurs organisiert sein. - `Name`: frei gewählte Bezeichnung, zum Beispiel `5b NAT (BEN)` oder `Mathe G` - `SubjectId`: Verweis auf das unterrichtete Fach - `SchoolYear`: Schuljahr dieser konkreten Lerngruppe - `GradeLevel`: Klassen- beziehungsweise Jahrgangsstufe Der Fachname wird ausschließlich im `Subject`-Stammdatensatz gepflegt. Die Lerngruppe speichert keine zweite Kopie des Fachnamens. ## Gruppenzuordnung (`GroupMembership`) Eine Gruppenzuordnung verbindet einen Schüler mit einer Lerngruppe. Sie ist keine Aufnahme oder Einschreibung an der Schule. - `StudentId`: Schüler - `GroupId`: Lerngruppe - `AddedOn`: Tag, an dem die Zuordnung in der App angelegt wurde - `Period`: ganzes Schuljahr, erstes Halbjahr, zweites Halbjahr oder eigener Zeitraum - `JoinedAt` / `LeftAt`: Grenzen eines eigenen Teilnahmezeitraums - `Niveau`: optionale Niveaudifferenzierung Das Schuljahr wird über die Lerngruppe ermittelt und deshalb nicht zusätzlich in der Gruppenzuordnung gespeichert. Für eine spätere echte Schulaufnahme wäre ein eigenes Feld wie `Student.SchoolEntryDate` zu verwenden. Pro Kombination aus Schüler und Lerngruppe darf es höchstens eine Zuordnung geben. ## Gewichtungsschema (`GradingScheme`) Legt die prozentuale Gewichtung von Klausuren, Mitarbeit und sonstigen Leistungen für die Zeugnisnote fest (Kapitel 2.3/2.4). Ein Datensatz ist entweder: - gruppenspezifisch (`GroupId` gesetzt, `GroupType` leer), oder - eine Voreinstellung je Gruppentyp (`GroupType` gesetzt, `GroupId` leer, in den Einstellungen gepflegt). Bei der Zeugnisnotenberechnung wird zuerst nach einem gruppenspezifischen Schema gesucht, sonst nach der Voreinstellung des Gruppentyps, sonst greift ein fest codierter Fallback (50/40/10). ## Zeugnisnote (`ReportGrade`) Eine Zeugnisnote gehört zu genau einem Schüler, einer Lerngruppe und einem Zeitraum (`Period`: "Gesamtes Schuljahr" / "1. Halbjahr" / "2. Halbjahr" — freier Text, keine Verknüpfung zu `MembershipPeriod`). `CalculatedValue` ist das zuletzt berechnete Ergebnis, `OverrideValue` überschreibt es bei pädagogischem Ermessen und erfordert `OverrideReason`. Nach dem Festschreiben (`IsLocked`) wird der Datensatz bei einer Neuberechnung nicht mehr verändert. ## Bewusst gespeicherte Momentaufnahmen Einige berechnete Werte bleiben absichtlich gespeichert: - `ExamResult.TotalPoints` und `ExamResult.Grade` halten das zuletzt berechnete Klausurergebnis fest. - `Exam.GradingKey` hält den für die konkrete Klausur verwendeten Notenschlüssel fest und ist unabhängig von später geänderten Vorlagen. - `Exam.Tasks` hält die Aufgabenstruktur der konkreten Klausur fest. Änderungen an Aufgaben oder Notenschlüssel müssen die betroffenen Ergebnisse kontrolliert neu berechnen. Diese Werte sind daher fachliche Momentaufnahmen und nicht bloß unkontrollierte Kopien. ## Bewusste Denormalisierung `Lesson.GroupId` bleibt zusätzlich zu `Lesson.UnitId` gespeichert. Dadurch kann der häufige Kalenderzugriff auf alle Stunden einer Lerngruppe direkt indiziert werden. Beim späteren Ausbau der Unterrichtsplanung muss sichergestellt werden, dass `Lesson.GroupId` mit der Lerngruppe der zugehörigen Einheit übereinstimmt. Umgesetzt in `PlanningTabViewModel` (Kapitel 4.1/4.2): Beim Kopieren einer Einheit als Vorlage in eine andere Gruppe (4.1.4) wird `Lesson.GroupId` auf jeder neu erzeugten Stunde explizit auf die Zielgruppe gesetzt, nicht von der Quell-Lesson übernommen. Beim Verschieben einer Stunde inkl. Nachrücken der Folgestunden (4.2.4) ändert sich ausschließlich `Lesson.Date` — `UnitId`/`GroupId` bleiben unangetastet. ## Stundenverlaufsplan (`Lesson.Phases`) Eine Stunde hat statt eines einzelnen `Phase`-Textfelds plus Methoden-/Materialien-Listen eine geordnete Liste `Lesson.Phases: List` (Name, Dauer in Minuten, Tätigkeit, Material, Kurzsymbol `Shorthand`). `DurationMinutes` ist die primäre, vom Nutzer gepflegte Größe; die im Editor angezeigte Uhrzeit je Phase ist rein abgeleitet (kumulierte Dauer ab `Lesson.StartTime`, sofern gesetzt) und wird nirgends persistiert — es gibt also keine Konsistenzpflicht zwischen gespeicherter Dauer und einer gespeicherten Uhrzeit, weil Letztere gar nicht gespeichert wird. `Shorthand` ist bewusst ein einzelnes Freitextfeld statt einer erzwungenen Von/Nach-Struktur: in der Praxis ist es mal ein Materialfluss-Pfeil ("AB001->S"), mal nur eine Sozialform ohne Pfeil ("Plenum", "LDE"). Der Kürzel-Katalog (`ShorthandCode`, Einstellungen) und bereits in anderen Stunden verwendete Werte dienen nur als Autovervollständigungs-Vorschläge, erzwingen aber keine Struktur. `LiteDbContext`-Schema-Version 3 führt zwei aufeinanderfolgende, unabhängig versionierte Migrationsschritte für dieses Feld: - **v1→v2** (`MigrateLessonPhases()`): führt bereits gespeicherte alte Stunden (einzelnes `Phase`-Feld, `Methods`/`Materials`-Listen) verlustfrei in eine einzige synthetisierte `LessonPhaseStep`-Zeile zusammen (`Name` = altes `Phase`, `Activity` = alte `Methods` verbunden, `Material` = alte `Materials` verbunden, `DurationMinutes = 0` da unbekannt). - **v2→v3** (`MigrateLessonShorthand()`): das ursprünglich als Von/Nach-Paar (`ShorthandFrom`/ `ShorthandTo`) modellierte Kurzsymbol wird auf das einzelne Freitextfeld zusammengeführt (beide gesetzt → `"Von->Nach"`, nur eines gesetzt → dieser Einzelwert). Beide Migrationen lesen dafür die rohe `BsonDocument`-Repräsentation der `lessons`-Collection statt der typisierten `Lessons`-Collection — nach jeder Modelländerung kennt die typisierte `Lesson`- Klasse die alten Feldnamen nicht mehr, ein Zugriff darüber hätte sie beim Deserialisieren bereits verworfen, bevor sie gelesen werden können. ## Eindeutige Schlüssel Die Datenbank schützt folgende Kombinationen mit eindeutigen Indizes: - Gruppenzuordnung: `StudentId + GroupId` - Klausurergebnis: `ExamId + StudentId` - Mitarbeitseintrag: `SessionId + StudentId` - Zeugnisnote: `StudentId + GroupId + Period` - Fach: normalisierter Fachname Altdaten werden beim Öffnen der Datenbank automatisch migriert. Die Migration verknüpft bisherige Fachtexte mit den Fachstammdaten, benennt die bisherige `enrollments`-Collection in `group_memberships` um und entfernt daraus das doppelt gespeicherte Schuljahr.