63 lines
3.8 KiB
C#
63 lines
3.8 KiB
C#
using LehrerApp.Core.Importing;
|
|
using LehrerApp.Core.Models;
|
|
|
|
namespace LehrerApp.Desktop.Services;
|
|
|
|
/// <summary>
|
|
/// Aus <c>WebUntisLessonAbsenceComparisonViewModel</c> herausgelöst (Nachtrag: die MCP-Tools in
|
|
/// <c>UntisComparisonTools</c> brauchen exakt dieselbe Regel für dieselbe Fehlzeile, siehe TODO.md) -
|
|
/// framework-frei (kein ObservableObject/Avalonia-Bezug), damit beide Aufrufer ohne Duplikat
|
|
/// garantiert denselben Statusvorschlag berechnen. Eine Abweichung zwischen Dialog und MCP-Tool für
|
|
/// dieselbe WebUntis-Zeile wäre verwirrender als die eine zusätzliche Indirektion hier.
|
|
/// </summary>
|
|
public static class UntisLessonAbsenceHelper
|
|
{
|
|
private const int FullLessonMinutes = 45;
|
|
// Bewusst zentral gehalten: Die Einstufung ist nur eine überprüfbare Vorauswahl und kann hier
|
|
// leicht an eine geänderte schulische Praxis angepasst werden.
|
|
public const int SignificantLatenessThresholdMinutes = 10;
|
|
|
|
// Der Bericht liefert keinen Entschuldigungstext, nur Minutenwerte, ein Bearbeitet-Datum und die
|
|
// (laut Schule) über Klammerung der ENr codierte Entscheidung des Klassenlehrers - ENr in
|
|
// Klammern bedeutet unentschuldigt, ohne Klammern abgeschlossen/entschuldigt. Reihenfolge ist
|
|
// wichtig: "nach Hause entlassen" zählt immer als vorzeitige Entlassung, unabhängig von der
|
|
// Dauer; darunter zählt jede Fehlzeit unter einer vollen Stunde (45 Min.) immer als Verspätung
|
|
// oder sonstiger Teilverlust, nie als komplette Abwesenheit. Ab zehn Minuten wird für die
|
|
// überprüfbare Vorauswahl die lokale Präzisierung "erheblich verspätet" verwendet. Der Text
|
|
// "Verspätung" allein ist laut Schule nicht zuverlässig genug, deshalb primär über die
|
|
// Minutenschwellen erkannt.
|
|
public static AttendanceStatus MapStatus(UntisLessonAbsenceDto absence)
|
|
{
|
|
if (IsEarlyRelease(absence)) return AttendanceStatus.LeftDuringClass;
|
|
if (absence.AbsentMinutes < SignificantLatenessThresholdMinutes) return AttendanceStatus.Late;
|
|
if (absence.AbsentMinutes < FullLessonMinutes) return AttendanceStatus.SignificantlyLate;
|
|
if (string.IsNullOrWhiteSpace(absence.HandledOn)) return AttendanceStatus.ExcusePending;
|
|
if (absence.ExternKey is null) return AttendanceStatus.ExcusePending;
|
|
return absence.ExternKeyInParentheses ? AttendanceStatus.Unexcused : AttendanceStatus.Excused;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prüft, ob der lokale Status die Aussage des WebUntis-Status bereits erfüllt. WebUntis kennt
|
|
/// nur die allgemeine Kategorie "verspätet"; die lokale Kategorie "erheblich verspätet" ist
|
|
/// eine bewusst genauere Einstufung und darf deshalb nicht auf "verspätet" zurückgestuft
|
|
/// werden. Die Regel ist absichtlich gerichtet: Ergibt die Minutenschwelle umgekehrt eine
|
|
/// erhebliche Verspätung, bleibt die Hochstufung von lokal "verspätet" erkennbar. Andere
|
|
/// Änderungen (z.B. eine nachgereichte Entschuldigung) bleiben echte Abweichungen.
|
|
/// </summary>
|
|
public static bool MatchesUntisStatus(AttendanceStatus? localStatus, AttendanceStatus untisStatus) =>
|
|
localStatus == untisStatus ||
|
|
localStatus == AttendanceStatus.SignificantlyLate && untisStatus == AttendanceStatus.Late;
|
|
|
|
private static bool IsEarlyRelease(UntisLessonAbsenceDto absence) =>
|
|
absence.Reason?.Contains("entlassen", StringComparison.OrdinalIgnoreCase) == true;
|
|
|
|
/// Erste Wahl zum Zuordnen einer WebUntis-Fehlzeile: die externe Schülernummer (ENr), sofern der
|
|
/// Schüler eine hat (z.B. nicht bei manuell statt per WebUntis-Import angelegten Schülern).
|
|
public static int? StudentExternKey(Student student)
|
|
{
|
|
student.ExternalIds ??= [];
|
|
return student.ExternalIds.TryGetValue(StudentImportFormats.MasterDataCsv.Value, out var value)
|
|
&& int.TryParse(value, out var key) ? key : null;
|
|
}
|
|
}
|