WIP (unstable): Klassenlehrer-Tab (fremde Klassenbucheinträge + Fehlzeiten der Klasse)

Neuer Tab in der Gruppendetailansicht (LearningGroup.IsClassTeacher-Flag), zeigt
Klassenbucheinträge anderer Lehrkräfte sowie Fehlzeiten der ganzen Klasse über alle
Fächer, pro Schüler*in und Tag zusammengefasst. Beides rein lesend, kein Übernehmen
in lokale Daten. Fehlzeiten-Bericht (AbsencePerStudent) adressiert die Klasse über
"KL"+getKlassen-ID, automatisch aufgelöst - 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:
2026-08-25 21:19:39 +02:00
co-authored by Claude Sonnet 5
parent f9f5f75aa8
commit 7245196689
17 changed files with 649 additions and 7 deletions
@@ -244,6 +244,53 @@ public sealed class WebUntisClientTests
await client.DisposeAsync();
}
[Fact]
public async Task GetClassAbsencesAsync_BautDenBerichtsAbrufAufUndTypisiertDasErgebnis()
{
// Kopfzeile "Text" kommt zweimal vor (Spalte 3 leer, Spalte 12 die eigentliche Tagesnotiz) -
// deckt ab, dass die spätere Spalte beim Parsen die frühere überschreibt (siehe Parser-Kommentar).
var csv = Encoding.UTF8.GetBytes(
"Schüler*innen\tExterne Id\tText\tKlasse\tDatum\tWochentag\tFehlstd.\tFehlmin.\tLehrkraft\tFach\t" +
"Abwesenheitsgrund\tText\tENr\tErledigt\tAbwesenheit zählt\tEntschuldigungstext\tStundennr.\tStatus\tFehltage\r\n" +
"Muster Max\t12345\t\t6a\t24.08.26\tMo.\t1\t45\tHED\tChe\tAbsent\tKrank gemeldet\t555\t24.08.26\ttrue\t\t3\tnicht entsch.\t1\r\n");
var handler = new QueueHandler(
Json("{\"result\":{\"sessionId\":\"s\",\"personId\":89}}"),
Json("{\"data\":{\"finished\":true,\"error\":false," +
"\"reportParams\":\"get=rpt3.tmp&name=AbsencePerStudent&format=csv\"}}"),
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(csv) },
Json("{\"result\":{}}"));
var client = CreateClient(handler);
var entries = await client.GetClassAbsencesAsync("KL874", 20260824, 20260828, CancellationToken.None);
var entry = Assert.Single(entries);
Assert.Equal("Muster Max", entry.StudentName);
Assert.Equal(12345, entry.ExternKey);
Assert.Equal("6a", entry.ClassName);
Assert.Equal(20260824, entry.Date);
Assert.Equal(1, entry.AbsentPeriods);
Assert.Equal(45, entry.AbsentMinutes);
Assert.Equal("HED", entry.TeacherUsernames);
Assert.Equal("Che", entry.Subject);
Assert.Equal("Absent", entry.AbsenceReason);
Assert.Equal("Krank gemeldet", entry.Note);
Assert.Equal(555, entry.EntryId);
Assert.Equal("24.08.26", entry.HandledOn);
Assert.True(entry.Counts);
Assert.Null(entry.ExcuseNote);
Assert.Equal(3, entry.PeriodNumber);
Assert.Equal("nicht entsch.", entry.Status);
Assert.True(entry.CountsAsFullDay);
Assert.Contains("reports.do?name=AbsencePerStudent", handler.Requests[1].Uri);
Assert.Contains("klasseOrStudentgroupId=KL874", handler.Requests[1].Uri);
Assert.Contains("studentId=-1", handler.Requests[1].Uri);
Assert.Contains("rpt_sd=20260824", handler.Requests[1].Uri);
Assert.Contains("rpt_ed=20260828", handler.Requests[1].Uri);
Assert.EndsWith("reports.do?get=rpt3.tmp&name=AbsencePerStudent&format=csv", handler.Requests[2].Uri);
await client.DisposeAsync();
}
private static WebUntisClient CreateClient(HttpMessageHandler handler) => new(
new HttpClient(handler),
new WebUntisOptions