Schreibgeschützter LessonViewerDialog (größere Schrift, ohne Bearbeitungs-/Verlängern-/ Verschieben-Funktion) für den Einsatz während des Unterrichtens, erreichbar über "Anzeigen" im Stunden-Toolbar. Alternative Unterrichtsabläufe (z.B. Kurzversion bei Zeitmangel) laufen jetzt über einen echten Katalog (neues Modell AlternativeLessonPath: Name + Beschreibung) statt Freitext direkt an der Phase: im Verlaufsplan-Editor eine kompakte, farbig unterstützte Checkbox statt einer durchgehend sichtbaren Eingabespalte, Zuordnung/Neuanlage über einen eigenen Dialog. Der Viewer gruppiert Phasen entsprechend und zeigt die hinterlegte Beschreibung. Schema-Migration v3→v4 führt bestehende Freitextwerte verlustfrei in Katalogeinträge über. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
146 lines
7.7 KiB
Markdown
146 lines
7.7 KiB
Markdown
# 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<LessonPhaseStep>` (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.
|
|
|
|
`AlternativePathId` (`Guid?`, `null` = Hauptweg) verweist auf einen Eintrag im eigenständigen
|
|
Katalog `AlternativeLessonPath` (Name + optionale `Description`, verwaltet über
|
|
`IAlternativeLessonPathRepository`) — anders als `Shorthand` bewusst kein Freitext direkt am
|
|
Phasen-Datensatz: die Zuordnung muss zuverlässig gruppierbar sein (`LessonViewerViewModel`
|
|
gruppiert danach für die Anzeige) und soll eine wiederverwendbare Erklärung tragen können ("wann
|
|
nimmt man diesen Weg"), was ein reines Freitextfeld nicht sauber leisten kann. Kein
|
|
Fork-Punkt-Bezug zum Hauptweg: jede Gruppe wird beim Anzeigen unabhängig ab `Lesson.StartTime`
|
|
durchgerechnet, nicht ab einer gemeinsamen Verzweigungsstelle.
|
|
|
|
`LiteDbContext`-Schema-Version 4 führt drei aufeinanderfolgende, unabhängig versionierte
|
|
Migrationsschritte für dieses Feld bzw. seine Vorstufen:
|
|
- **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).
|
|
- **v3→v4** (`MigrateLessonAlternativePaths()`): das ursprünglich als Freitext (`AlternativePath`,
|
|
string) modellierte Kennzeichen für den alternativen Ablauf wird pro distinktem Namen zu einem
|
|
`AlternativeLessonPath`-Katalogeintrag zusammengeführt (derselbe Name über mehrere Stunden hinweg
|
|
referenziert denselben, wiederverwendeten Eintrag) und die Phase auf `AlternativePathId`
|
|
umgestellt.
|
|
|
|
Alle drei 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.
|