fix: Klassenlehrer-Übersicht rief Schülerreport bislang live statt gecacht ab
Die Übersicht wirkte beim Öffnen spürbar zeitverzögert, obwohl Fehlzeiten und Klassenbuch schon lokal gecacht waren (Nutzer-Feedback: "fühlt sich an wie ein Live-Pull mit CSV-Parsing"). Grund: genau das passierte - der Schülerreport (Namen fürs Roster) lief komplett am Cache vorbei bei jedem Öffnen live gegen WebUntis. Neues UntisStudentRosterCacheEntry/UntisStudentRosterCacheRepository (gleiches "kein db.OnChange"-Prinzip wie die bestehenden Caches) plus UntisReportCacheService.GetStudentRosterAsync - ohne heißes/kaltes Fenster, da eine Klassenliste keine Historie hat, nur dieselbe Stundenschwelle "gilt der letzte Abruf noch als frisch". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Desktop.Services;
|
||||
using LehrerApp.Desktop.ViewModels.ClassTeacher;
|
||||
using Xunit;
|
||||
@@ -125,10 +126,6 @@ public sealed class ClassTeacherViewModelsTests
|
||||
Assert.True(ben.HasRecentClassRegisterEntry);
|
||||
}
|
||||
|
||||
private static UntisStudentDto Student(int? externKey, string displayName) => new(
|
||||
UntisId: externKey ?? 0, ExternKey: externKey, ClassName: "6a", Name: displayName, LongName: null,
|
||||
ForeName: null, DisplayName: displayName, Gender: null, BirthDate: null, BirthDateRaw: null,
|
||||
EntryDate: null, EntryDateRaw: null, ExitDate: null, ExitDateRaw: null, Text: null,
|
||||
MedicalReportDuty: null, Schulpflicht: null, Majority: null,
|
||||
Address: new UntisStudentAddressDto(null, null, null, null, null, null), AttributeIL: null);
|
||||
private static UntisStudentRosterCacheEntry Student(int? externKey, string displayName) =>
|
||||
new() { ClassName = "6a", ExternKey = externKey, DisplayName = displayName };
|
||||
}
|
||||
|
||||
@@ -500,6 +500,18 @@ public class FakeUntisClassRegisterCache : IUntisClassRegisterCacheRepository
|
||||
public void InsertRange(IEnumerable<UntisClassRegisterCacheEntry> entries) => _all.AddRange(entries);
|
||||
}
|
||||
|
||||
public class FakeUntisStudentRosterCache : IUntisStudentRosterCacheRepository
|
||||
{
|
||||
private readonly List<UntisStudentRosterCacheEntry> _all = [];
|
||||
public List<UntisStudentRosterCacheEntry> GetByClass(string className) =>
|
||||
_all.Where(e => e.ClassName == className).ToList();
|
||||
public void ReplaceAll(string className, IEnumerable<UntisStudentRosterCacheEntry> entries)
|
||||
{
|
||||
_all.RemoveAll(e => e.ClassName == className);
|
||||
_all.AddRange(entries);
|
||||
}
|
||||
}
|
||||
|
||||
public class FakeUntisCacheFetchStates : IUntisCacheFetchStateRepository
|
||||
{
|
||||
private readonly List<UntisCacheFetchState> _all = [];
|
||||
|
||||
@@ -144,8 +144,9 @@ public sealed class UntisReportCacheServiceTests
|
||||
|
||||
var absenceCache = new FakeUntisAbsenceCache();
|
||||
var classRegisterCache = new FakeUntisClassRegisterCache();
|
||||
var rosterCache = new FakeUntisStudentRosterCache();
|
||||
var fetchStates = new FakeUntisCacheFetchStates();
|
||||
var cache = new UntisReportCacheService(untis, absenceCache, classRegisterCache, fetchStates);
|
||||
var cache = new UntisReportCacheService(untis, absenceCache, classRegisterCache, rosterCache, fetchStates);
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
|
||||
var first = await cache.GetClassRegisterEventsAsync("10c", today, today);
|
||||
@@ -157,6 +158,35 @@ public sealed class UntisReportCacheServiceTests
|
||||
Assert.Equal(4, handler.Requests.Count); // keine weiteren Anfragen - aus dem Cache bedient
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStudentRosterAsync_ZweiterAufrufBinnenEinerStunde_RuftWebUntisNichtErneutAb()
|
||||
{
|
||||
var csv = Encoding.UTF8.GetBytes(
|
||||
"id\texternKey\tklasse.name\tname\tlongName\tforeName\r\n" +
|
||||
"1\t9001\t10c\tMUST\tMustermann\tMax\r\n");
|
||||
var handler = new QueueHandler(
|
||||
Json("{\"result\":{\"sessionId\":\"s\"}}"),
|
||||
Json("{\"result\":[{\"id\":1,\"name\":\"2026/27\",\"startDate\":20260801,\"endDate\":20270731}]}"),
|
||||
Json("{\"data\":{\"finished\":true,\"error\":false," +
|
||||
"\"reportParams\":\"get=rpt.tmp&name=Student&format=csv\"}}"),
|
||||
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(csv) });
|
||||
var settings = TestSupport.BuildWebUntisSettingsService();
|
||||
await using var untis = new WebUntisIntegrationService(new HttpClient(handler), settings);
|
||||
await untis.ConnectAsync(new WebUntisCredentials("bk-ostvest", "arche.webuntis.com", "lehrkraft", "geheim"));
|
||||
|
||||
var cache = new UntisReportCacheService(untis, new FakeUntisAbsenceCache(), new FakeUntisClassRegisterCache(),
|
||||
new FakeUntisStudentRosterCache(), new FakeUntisCacheFetchStates());
|
||||
|
||||
var first = await cache.GetStudentRosterAsync("10c");
|
||||
Assert.Single(first);
|
||||
Assert.Equal("Max Mustermann", first[0].DisplayName);
|
||||
Assert.Equal(4, handler.Requests.Count);
|
||||
|
||||
var second = await cache.GetStudentRosterAsync("10c");
|
||||
Assert.Single(second);
|
||||
Assert.Equal(4, handler.Requests.Count); // keine weiteren Anfragen - aus dem Cache bedient
|
||||
}
|
||||
|
||||
private static HttpResponseMessage Json(string json) => new(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/json"),
|
||||
|
||||
Reference in New Issue
Block a user