WIP (unstable): lokaler Cache für Klassenlehrer-Fehlzeiten & Klassenbucheinträge
Nutzer-Feedback: die WebUntis-Berichtszeilen sind starr genug für ein eigenes Datenmodell, Warnungen sollen sofort da sein statt bei jedem Öffnen neu abgerufen zu werden - vor allem darf derselbe Bericht nicht mehrfach pro Stunde abgerufen werden, nur weil die Ansicht mehrfach geöffnet wird (Sorge, bei WebUntis aufzufallen). Neue Modelle UntisAbsenceCacheEntry/UntisClassRegisterCacheEntry (1:1 zu den bestehenden DTOs) + UntisCacheFetchState, bewusst nicht synchronisiert (gleiches "kein db.OnChange"-Muster wie UntisSnapshotEntry/AnnualPlanEvent) - jedes Gerät ruft WebUntis selbst ab, die Zeilenzahl wächst übers Schuljahr gewollt an. UntisReportCacheService: festes heißes Fenster der letzten 14 Tage, höchstens stündlich automatisch aufgefrischt; alles Ältere gilt als endgültig und wird dauerhaft aus dem Cache bedient. Die Entscheidungslogik (Plan) ist als reine, ohne Repositories/HTTP testbare Funktion ausgelagert. Klassenlehrer-Ansichten nutzen den Cache-Service statt WebUntisIntegrationService direkt; ein zusätzlicher Button umgeht die Stundensperre bewusst für manuelle Abrufe. Noch nicht mit echtem WebUntis-Zugang gegengeprüft (siehe TODO.md). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -58,17 +58,17 @@ public sealed record ClassAbsenceDaySummaryRow(DateOnly Date, string StudentName
|
||||
/// <summary>
|
||||
/// Details-Ansicht des Klassenlehrer-Bereichs: Klassenbucheinträge, die andere Lehrkräfte zu
|
||||
/// Schülern der Klasse angelegt haben (WebUntis "-alle-"-Bericht, gefiltert auf fremde statt der
|
||||
/// eigenen Benutzerkennung wie beim bestehenden Dokumentations-Abgleich, siehe
|
||||
/// <see cref="WebUntisIntegrationService.GetForeignClassRegisterEventsAsync"/>), sowie die
|
||||
/// Fehlzeiten der Klasse über alle Fächer (<see cref="WebUntisIntegrationService.GetClassAbsencesAsync"/>).
|
||||
/// Beides rein zur Ansicht, kein Übernehmen in lokale Daten. Bewusst unabhängig von jeder
|
||||
/// <see cref="Core.Models.LearningGroup"/> — Klassenlehrer ist man für die ganze Klasse, nicht für
|
||||
/// einen einzelnen Unterricht; die Klasse kommt aus <see cref="ClassTeacherOverviewViewModel"/>
|
||||
/// (letztlich aus den WebUntis-Einstellungen), nicht aus einer Gruppen-ID.
|
||||
/// eigenen Benutzerkennung wie beim bestehenden Dokumentations-Abgleich), sowie die Fehlzeiten der
|
||||
/// Klasse über alle Fächer — beide über <see cref="UntisReportCacheService"/> (lokal gecacht statt
|
||||
/// bei jedem Öffnen neu abgerufen, siehe TODO.md). Beides rein zur Ansicht, kein Übernehmen in
|
||||
/// lokale Daten. Bewusst unabhängig von jeder <see cref="Core.Models.LearningGroup"/> —
|
||||
/// Klassenlehrer ist man für die ganze Klasse, nicht für einen einzelnen Unterricht; die Klasse
|
||||
/// kommt aus <see cref="ClassTeacherOverviewViewModel"/> (letztlich aus den WebUntis-Einstellungen),
|
||||
/// nicht aus einer Gruppen-ID.
|
||||
/// </summary>
|
||||
public partial class ClassTeacherDetailsViewModel : ObservableObject
|
||||
{
|
||||
private readonly WebUntisIntegrationService _untis;
|
||||
private readonly UntisReportCacheService _cache;
|
||||
private string _className = "";
|
||||
|
||||
public ObservableCollection<ClassTeacherClassRegisterRow> Entries { get; } = [];
|
||||
@@ -81,9 +81,9 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
|
||||
/// Von der Übersicht gesetzt (Klick auf eine Roster-Zeile) - leer zeigt alle Schüler*innen.
|
||||
[ObservableProperty] private string _studentFilter = "";
|
||||
|
||||
public ClassTeacherDetailsViewModel(WebUntisIntegrationService untis)
|
||||
public ClassTeacherDetailsViewModel(UntisReportCacheService cache)
|
||||
{
|
||||
_untis = untis;
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public void Initialize(string className)
|
||||
@@ -95,7 +95,14 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Load()
|
||||
private Task Load() => LoadInternal(forceRefresh: false);
|
||||
|
||||
/// Umgeht bewusst die Stunden-Sperre von <see cref="UntisReportCacheService"/> — für den Fall,
|
||||
/// dass man sicher weiß, dass sich seit dem letzten automatischen Abruf etwas geändert hat.
|
||||
[RelayCommand]
|
||||
private Task Refresh() => LoadInternal(forceRefresh: true);
|
||||
|
||||
private async Task LoadInternal(bool forceRefresh)
|
||||
{
|
||||
var start = DateOnly.FromDateTime(StartDate.LocalDateTime);
|
||||
var end = DateOnly.FromDateTime(EndDate.LocalDateTime);
|
||||
@@ -105,8 +112,8 @@ public partial class ClassTeacherDetailsViewModel : ObservableObject
|
||||
Busy = true; Entries.Clear(); AbsenceEntries.Clear();
|
||||
try
|
||||
{
|
||||
var classRegisterTask = _untis.GetForeignClassRegisterEventsAsync(_className, start, end);
|
||||
var absencesTask = _untis.GetClassAbsencesAsync(_className, start, end);
|
||||
var classRegisterTask = _cache.GetClassRegisterEventsAsync(_className, start, end, forceRefresh);
|
||||
var absencesTask = _cache.GetAbsencesAsync(_className, start, end, forceRefresh);
|
||||
await Task.WhenAll(classRegisterTask, absencesTask);
|
||||
|
||||
var ordered = classRegisterTask.Result
|
||||
|
||||
@@ -62,6 +62,7 @@ public partial class ClassTeacherOverviewViewModel : ObservableObject
|
||||
{
|
||||
private readonly WebUntisSettingsService _settings;
|
||||
private readonly WebUntisIntegrationService _untis;
|
||||
private readonly UntisReportCacheService _cache;
|
||||
|
||||
public ClassTeacherDetailsViewModel DetailsTab { get; }
|
||||
public ObservableCollection<ClassTeacherRosterRow> Roster { get; } = [];
|
||||
@@ -78,9 +79,9 @@ public partial class ClassTeacherOverviewViewModel : ObservableObject
|
||||
public Func<Task>? OnNavigateToSettings { get; set; }
|
||||
|
||||
public ClassTeacherOverviewViewModel(WebUntisSettingsService settings, WebUntisIntegrationService untis,
|
||||
ClassTeacherDetailsViewModel detailsTab)
|
||||
UntisReportCacheService cache, ClassTeacherDetailsViewModel detailsTab)
|
||||
{
|
||||
_settings = settings; _untis = untis; DetailsTab = detailsTab;
|
||||
_settings = settings; _untis = untis; _cache = cache; DetailsTab = detailsTab;
|
||||
}
|
||||
|
||||
partial void OnHomeroomClassNameChanged(string? value) => OnPropertyChanged(nameof(HomeroomClassConfigured));
|
||||
@@ -104,8 +105,8 @@ public partial class ClassTeacherOverviewViewModel : ObservableObject
|
||||
{
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var studentsTask = _untis.GetStudentsAsync(className);
|
||||
var absencesTask = _untis.GetClassAbsencesAsync(className, today, today);
|
||||
var classRegisterTask = _untis.GetForeignClassRegisterEventsAsync(className, today.AddDays(-7), today);
|
||||
var absencesTask = _cache.GetAbsencesAsync(className, today, today);
|
||||
var classRegisterTask = _cache.GetClassRegisterEventsAsync(className, today.AddDays(-7), today);
|
||||
await Task.WhenAll(studentsTask, absencesTask, classRegisterTask);
|
||||
|
||||
var todayAbsences = ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absencesTask.Result);
|
||||
|
||||
Reference in New Issue
Block a user