Erkennt Vertretungen, Ausfälle und Zusatzaufsichten aus dem persönlichen WebUntis-iCal-Feed und schreibt sie automatisch als SubstitutionEntry. Bekannter offener Bug: es tauchen weiterhin falsche Vertretungen für Stunden auf, die real unverändert sind — wird in einem Folge-Commit untersucht, deshalb vorerst auf diesem Branch statt main. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
239 lines
12 KiB
C#
239 lines
12 KiB
C#
namespace LehrerApp.Core.Models;
|
||
|
||
public class Grade
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public Guid StudentId { get; set; }
|
||
public Guid GroupId { get; set; }
|
||
public GradeCategory Category { get; set; }
|
||
public string Value { get; set; } = "";
|
||
public DateOnly Date { get; set; }
|
||
public double Weight { get; set; } = 1.0;
|
||
public string? Note { get; set; }
|
||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||
}
|
||
public enum GradeCategory { Oral, Homework, Participation, Project, Other }
|
||
|
||
/// <summary>
|
||
/// Prozentuale Gewichtung von Klausuren/Mitarbeit/sonstigen Leistungen für die Zeugnisnote.
|
||
/// Entweder gruppenspezifisch (<see cref="GroupId"/> gesetzt) oder als Voreinstellung je
|
||
/// Gruppentyp (<see cref="GroupType"/> gesetzt, <see cref="GroupId"/> null).
|
||
/// </summary>
|
||
public class GradingScheme
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public Guid? GroupId { get; set; }
|
||
public GroupType? GroupType { get; set; }
|
||
public double ExamsPercent { get; set; }
|
||
public double ParticipationPercent { get; set; }
|
||
public double OtherPercent { get; set; }
|
||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||
}
|
||
|
||
public class Unit
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public Guid GroupId { get; set; }
|
||
public string Title { get; set; } = "";
|
||
public DateOnly? StartDate { get; set; }
|
||
public DateOnly? EndDate { get; set; }
|
||
public List<string> Competencies { get; set; } = [];
|
||
public UnitStatus Status { get; set; } = UnitStatus.Planned;
|
||
public string? Notes { get; set; }
|
||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||
}
|
||
public class Lesson : IHasAttachments
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public Guid UnitId { get; set; }
|
||
public Guid GroupId { get; set; }
|
||
public DateOnly Date { get; set; }
|
||
public int? LessonNumber { get; set; }
|
||
public string Topic { get; set; } = "";
|
||
/// Optionaler Stundenbeginn — solange kein Stundenplan (Kapitel 4.3) existiert, manuell
|
||
/// gepflegt. Dient nur der abgeleiteten Uhrzeit-Anzeige je Phase in <see cref="Phases"/>.
|
||
public TimeOnly? StartTime { get; set; }
|
||
public List<LessonPhaseStep> Phases { get; set; } = [];
|
||
public string? Homework { get; set; }
|
||
/// Markiert, dass die hier eingetragene Hausaufgabe in einer Folgestunde besprochen/kontrolliert
|
||
/// wurde — treibt das Stundenplan-Badge "Hausaufgabe kontrollieren" (4.5.4).
|
||
public bool HomeworkChecked { get; set; }
|
||
/// Bewusstes Abschalten der Erinnerung, ohne eine tatsächliche Kontrolle vorzutäuschen (z.B.
|
||
/// wenn die Hausaufgabe absichtlich nicht mehr kontrolliert wird).
|
||
public bool HomeworkCheckDismissed { get; set; }
|
||
public string? Reflection { get; set; }
|
||
public LessonStatus Status { get; set; } = LessonStatus.Planned;
|
||
/// Material/Arbeitsblätter sowie fachspezifische Anhänge (z.B. Experiment- und
|
||
/// Gefährdungsbeurteilungs-Dokumente im Chemieunterricht) — dieselbe Anhang-Infrastruktur wie
|
||
/// bei <see cref="Documentation"/>.
|
||
public List<DocumentAttachment> Attachments { get; set; } = [];
|
||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Eine Zeile im tabellarischen Stundenverlaufsplan (Phase/Dauer/Tätigkeit/Material/Kurzsymbol).
|
||
/// <see cref="DurationMinutes"/> ist die primäre Eingabe (man plant in Zeitblöcken); die Uhrzeit
|
||
/// je Phase wird im Editor daraus abgeleitet, wenn <see cref="Lesson.StartTime"/> gesetzt ist.
|
||
/// <see cref="Shorthand"/> ist bewusst ein einzelnes Freitextfeld statt einer erzwungenen
|
||
/// Von/Nach-Struktur: manchmal ist es ein Materialfluss-Pfeil ("AB001->S"), manchmal nur eine
|
||
/// Sozialform ohne Pfeil ("Plenum", "LDE"). Vorschläge kommen sowohl aus dem in den Einstellungen
|
||
/// gepflegten Kürzel-Katalog (<see cref="ShorthandCode"/>) als auch aus bereits in anderen Stunden
|
||
/// verwendeten Werten.
|
||
/// </summary>
|
||
public class LessonPhaseStep
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public string Name { get; set; } = "";
|
||
public int DurationMinutes { get; set; }
|
||
public string Activity { get; set; } = "";
|
||
public string Material { get; set; } = "";
|
||
public string Shorthand { get; set; } = "";
|
||
/// <summary>
|
||
/// null = Hauptweg. Sonst Verweis auf einen benannten <see cref="AlternativeLessonPath"/> aus
|
||
/// dem Katalog, über den Phasen alternativer Unterrichtsverläufe zusammengehören —
|
||
/// Verlaufsplan-Editor und -Ansicht gruppieren danach. Bewusst kein Fork-Punkt-Bezug zum
|
||
/// Hauptweg: eine Alternative beginnt in der Anzeige unabhängig ab dem Stundenbeginn, nicht ab
|
||
/// einer gemeinsamen Verzweigungsstelle — für den schnellen Überblick ausreichend, siehe TODO.md.
|
||
/// </summary>
|
||
public Guid? AlternativePathId { get; set; }
|
||
}
|
||
|
||
public enum UnitStatus { Planned, Active, Completed }
|
||
public enum LessonStatus { Planned, Conducted }
|
||
|
||
/// <summary>
|
||
/// Katalogeintrag für einen wiederverwendbaren "alternativen Ablauf" (z.B. "Kurzversion" bei
|
||
/// Zeitmangel), dem einzelne <see cref="LessonPhaseStep"/>-Zeilen über
|
||
/// <see cref="LessonPhaseStep.AlternativePathId"/> zugeordnet werden. Wird direkt beim Zuweisen im
|
||
/// Verlaufsplan-Editor angelegt (kein separates Verwaltungs-UI in den Einstellungen bisher).
|
||
/// </summary>
|
||
public class AlternativeLessonPath
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public string Name { get; set; } = "";
|
||
/// Freitext, wann/wofür dieser Ablauf gedacht ist, z.B. "Bei Zeitmangel, wenn Aufgabe 3 nicht
|
||
/// mehr passt." Absichtlich kein strukturiertes Bedingungsfeld (z.B. "wenn an Stelle X weniger
|
||
/// als Y Minuten übrig sind") — das setzt eine Live-Zeiterfassung während des Unterrichtens
|
||
/// voraus, die es noch nicht gibt (siehe Live-Unterrichtsmodus-Ideensammlung in TODO.md).
|
||
public string? Description { get; set; }
|
||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Ein fester Termin im wöchentlichen Stundenplan (4.3): eine Lerngruppe trifft sich an einem
|
||
/// Wochentag zu einer bestimmten Stunde. Wiederkehrendes Muster, kein konkretes Datum — für
|
||
/// tatsächlich gehaltene Einzelstunden siehe <see cref="Lesson"/>. Pro Wochentag/Stunde ist
|
||
/// höchstens eine Gruppe eingetragen (ein Lehrer kann nicht gleichzeitig an zwei Orten sein).
|
||
/// </summary>
|
||
public class TimetableSlot
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public Guid GroupId { get; set; }
|
||
public DayOfWeek Weekday { get; set; }
|
||
public int PeriodNumber { get; set; }
|
||
public string? Room { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Unterrichtsfreier Zeitraum (4.3.5): Schulferien werden manuell gepflegt (nicht algorithmisch
|
||
/// herleitbar, jährlich neu von den Bundesländern festgelegt). Gesetzliche Feiertage werden
|
||
/// dagegen berechnet (PublicHolidayService) und nicht in der Datenbank gespeichert.
|
||
/// </summary>
|
||
public class SchoolHoliday
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public string Name { get; set; } = "";
|
||
public DateOnly StartDate { get; set; }
|
||
public DateOnly EndDate { get; set; }
|
||
}
|
||
|
||
public enum GermanState
|
||
{
|
||
BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH
|
||
}
|
||
|
||
/// <summary>
|
||
/// Wiederkehrende Pausenaufsicht (kein Bezug zu einer Lerngruppe, anders als
|
||
/// <see cref="TimetableSlot"/>). <see cref="AfterPeriod"/> = 0 bedeutet "vor der 1. Stunde"
|
||
/// (Frühaufsicht), sonst "in der Pause nach dieser Stunde". Pro Wochentag/Pause höchstens eine
|
||
/// Aufsicht (ein Lehrer kann nicht an zwei Orten gleichzeitig Aufsicht führen).
|
||
/// </summary>
|
||
public class SupervisionDuty
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public DayOfWeek Weekday { get; set; }
|
||
public int AfterPeriod { get; set; }
|
||
public string Location { get; set; } = "";
|
||
}
|
||
|
||
public enum SubstitutionKind { Supervision, Lesson, SpecialAssignment, Cancelled }
|
||
|
||
/// <summary>
|
||
/// Einmalige Ausnahme vom regulären Plan an einem konkreten Datum: eine Vertretungsaufsicht
|
||
/// (zusätzlich zur oder anstelle einer regulären <see cref="SupervisionDuty"/>), eine
|
||
/// Vertretungsstunde in einer eigenen oder fremden Lerngruppe, ein Sondereinsatz (Ausflug,
|
||
/// Berufsmesse, Exkursion, ...), der einen Teil des Tages oder den ganzen Tag blockiert, oder ein
|
||
/// schlichter Stundenausfall ohne Ersatz (z.B. weil die betroffene Gruppe selbst auf Klassenfahrt
|
||
/// ist). Bewusst getrennt von <see cref="Lesson"/>: die meisten dieser Einträge sind nicht Teil der
|
||
/// durchgeplanten Einheiten-Reihenfolge (siehe TODO.md, Nachtrag zu 4.3) — nur bei
|
||
/// <see cref="SubstitutionKind.Lesson"/> kann beim Anlegen ausdrücklich "als Stunde in der Einheit
|
||
/// übernehmen" gewählt werden, was zusätzlich eine echte <see cref="Lesson"/> erzeugt; dieser
|
||
/// Eintrag hier bleibt trotzdem bestehen, da er (unabhängig von der Einheiten-Fortschrittsanzeige)
|
||
/// das, was tatsächlich im Plan an diesem Tag stattfand, festhält.
|
||
/// </summary>
|
||
public class SubstitutionEntry
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public DateOnly Date { get; set; }
|
||
public SubstitutionKind Kind { get; set; }
|
||
/// Bei <see cref="SubstitutionKind.Lesson"/> und <see cref="SubstitutionKind.Cancelled"/>
|
||
/// gesetzt — bei Cancelled wird Gruppe/Fach zur Anzeige aus dem an dieser Stelle regulär
|
||
/// eingetragenen <see cref="TimetableSlot"/> abgeleitet, nicht hier gespeichert.
|
||
public int? PeriodNumber { get; set; }
|
||
/// Nur bei <see cref="SubstitutionKind.Supervision"/> gesetzt — wie bei
|
||
/// <see cref="SupervisionDuty.AfterPeriod"/>, 0 = vor der 1. Stunde.
|
||
public int? AfterPeriod { get; set; }
|
||
/// Nur bei <see cref="SubstitutionKind.SpecialAssignment"/> und nicht <see cref="IsAllDay"/>:
|
||
/// der belegte Stundenbereich (inklusive).
|
||
public int? FromPeriod { get; set; }
|
||
public int? ToPeriod { get; set; }
|
||
/// Nur bei <see cref="SubstitutionKind.SpecialAssignment"/>: blockiert den ganzen Tag statt
|
||
/// nur <see cref="FromPeriod"/>–<see cref="ToPeriod"/>.
|
||
public bool IsAllDay { get; set; }
|
||
/// Gesetzt, wenn es sich um eine eigene Lerngruppe handelt — bei fremden/unbekannten Gruppen
|
||
/// oder Sondereinsätzen ohne Gruppenbezug (z.B. Berufsmesse) bleibt es null und nur
|
||
/// <see cref="GroupLabel"/> beschreibt ggf., um welche Gruppe es ging.
|
||
public Guid? GroupId { get; set; }
|
||
public string GroupLabel { get; set; } = "";
|
||
/// Thema (bei Lesson), Grund/Ort (bei Supervision, z.B. "Vertretung für Hr. Müller") bzw.
|
||
/// Bezeichnung (bei SpecialAssignment, z.B. "Ausflug ins Museum", "Berufsmesse").
|
||
public string Description { get; set; } = "";
|
||
public string? Notes { get; set; }
|
||
/// Gesetzt, wenn dieser Eintrag automatisch aus dem WebUntis-iCal-Abgleich entstanden ist —
|
||
/// trägt die stabile iCal-UID des auslösenden Termins. Macht wiederholte Abgleich-Läufe
|
||
/// idempotent (Update statt Duplikat, siehe ISubstitutionEntryRepository.GetByExternalId) und
|
||
/// unterscheidet automatisch erzeugte von von Hand eingetragenen Ausnahmen. Bei Handeinträgen
|
||
/// (SubstitutionEntryDialogViewModel) bleibt es null.
|
||
public string? ExternalId { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Zeugnisnote eines Schülers in einer Lerngruppe für einen Zeitraum (Halbjahr/Gesamtjahr).
|
||
/// <see cref="CalculatedValue"/> ist das zuletzt berechnete Ergebnis; <see cref="OverrideValue"/>
|
||
/// überschreibt es bei pädagogischem Ermessen (erfordert <see cref="OverrideReason"/>).
|
||
/// Nach dem Festschreiben (<see cref="IsLocked"/>) wird der Datensatz nicht mehr neu berechnet.
|
||
/// </summary>
|
||
public class ReportGrade
|
||
{
|
||
public Guid Id { get; set; } = Guid.NewGuid();
|
||
public Guid StudentId { get; set; }
|
||
public Guid GroupId { get; set; }
|
||
public string Period { get; set; } = "";
|
||
public string CalculatedValue { get; set; } = "";
|
||
public string? OverrideValue { get; set; }
|
||
public string? OverrideReason { get; set; }
|
||
public bool IsLocked { get; set; }
|
||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||
}
|