Untis-API: Fehlzeiten abgleich

This commit is contained in:
2026-08-25 00:21:53 +02:00
parent 17475a781f
commit 87a7badb44
21 changed files with 593 additions and 298 deletions
+37 -16
View File
@@ -143,16 +143,10 @@ public sealed class WebUntisClientTests
}
[Fact]
public async Task FehlzeitenUndKlassenbuch_WerdenTypisiertAbgerufen()
public async Task Klassenbuch_WirdTypisiertAbgerufen()
{
var handler = new QueueHandler(
Json("{\"result\":{\"sessionId\":\"s\"}}"),
Json("{\"result\":{\"periodsWithAbsences\":[" +
"{\"studentId\":9001,\"date\":20260901,\"startTime\":800,\"endTime\":845," +
"\"absentTime\":45,\"checked\":true,\"absenceReason\":\"Krank\"," +
"\"excuseStatus\":\"entschuldigt\",\"subjectId\":12,\"teacherIds\":[\"7\"]}," +
"{\"studentId\":9999,\"date\":20260901,\"startTime\":800,\"endTime\":845," +
"\"absentTime\":45,\"checked\":false}]}}"),
Json("{\"result\":[{\"studentid\":9001,\"surname\":\"Müller\",\"forname\":\"Ada\"," +
"\"date\":20260902,\"subject\":\"MA\",\"categoryId\":3,\"reason\":\"Material\"," +
"\"text\":\"Buch vergessen\"}]}"),
@@ -162,25 +156,18 @@ public sealed class WebUntisClientTests
Json("{\"result\":{}}"));
var client = CreateClient(handler);
var absences = await client.GetAbsencesAsync(20260801, 20270731, CancellationToken.None);
var entries = await client.GetClassRegisterEntriesAsync(17, 20260801, 20270731,
CancellationToken.None);
var categories = await client.GetClassRegisterCategoriesAsync(CancellationToken.None);
var groups = await client.GetClassRegisterCategoryGroupsAsync(CancellationToken.None);
Assert.Equal(2, absences.Count);
var absence = Assert.Single(absences, x => x.StudentKey == 9001);
Assert.Equal(45, absence.AbsentMinutes);
Assert.Equal("entschuldigt", absence.ExcuseStatus);
Assert.Equal(7, Assert.Single(absence.TeacherIds));
var entry = Assert.Single(entries);
Assert.Equal("Ada Müller", entry.DisplayName);
Assert.Equal("Buch vergessen", entry.Text);
Assert.Equal(3, Assert.Single(categories).Id);
Assert.Equal("Organisation", Assert.Single(groups).Name);
Assert.Contains("\"method\":\"getTimetableWithAbsences\"", handler.Requests[1].Body);
Assert.Contains("\"method\":\"getClassregEvents\"", handler.Requests[2].Body);
Assert.Contains("\"id\":17,\"type\":5", handler.Requests[2].Body);
Assert.Contains("\"method\":\"getClassregEvents\"", handler.Requests[1].Body);
Assert.Contains("\"id\":17,\"type\":5", handler.Requests[1].Body);
Assert.Single(handler.Requests,
request => request.Body.Contains("\"method\":\"authenticate\""));
@@ -188,6 +175,40 @@ public sealed class WebUntisClientTests
Assert.Contains("\"method\":\"logout\"", handler.Requests[^1].Body);
}
[Fact]
public async Task GetLessonAbsencesAsync_LoestTeacherIdAusDerEigenenSessionAufUndTypisiertDasErgebnis()
{
var csv = Encoding.UTF8.GetBytes(
"Schüler*innen\tDatum\tFehlstd.\tUnentsch. Fehlstd.\tFehlmin.\tUnentsch. Fehlmin.\tZeit\t" +
"Abwesenheitsgrund\tENr\tErledigt\tAbwesenheit zählt\r\n" +
"Erika Mustermann\t09.12.25\t1\t1\t45\t45\t09:40-10:25\tKrank\t(185120)\t09.12.25\ttrue\r\n");
var handler = new QueueHandler(
Json("{\"result\":{\"sessionId\":\"s\",\"personId\":89}}"),
Json("{\"data\":{\"finished\":true,\"error\":false," +
"\"reportParams\":\"get=rpt1.tmp&name=AbsencePerLesson&format=csv\"}}"),
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(csv) },
Json("{\"result\":{}}"));
var client = CreateClient(handler);
var absences = await client.GetLessonAbsencesAsync(38262, 20250811, 20260710, CancellationToken.None);
var absence = Assert.Single(absences);
Assert.Equal("Erika Mustermann", absence.StudentName);
Assert.Equal(20251209, absence.Date);
Assert.Equal(185120, absence.ExternKey);
Assert.True(absence.ExternKeyInParentheses);
Assert.Equal(940, absence.StartTime);
Assert.True(absence.Counts);
Assert.Contains("reports.do?name=AbsencePerLesson", handler.Requests[1].Uri);
Assert.Contains("lsid=38262", handler.Requests[1].Uri);
Assert.Contains("teacherId=89", handler.Requests[1].Uri);
Assert.Contains("rpt_sd=20250811", handler.Requests[1].Uri);
Assert.Contains("rpt_ed=20260710", handler.Requests[1].Uri);
Assert.EndsWith("reports.do?get=rpt1.tmp&name=AbsencePerLesson&format=csv", handler.Requests[2].Uri);
await client.DisposeAsync();
}
private static WebUntisClient CreateClient(HttpMessageHandler handler) => new(
new HttpClient(handler),
new WebUntisOptions
@@ -0,0 +1,54 @@
using Xunit;
using LehrerApp.WebUntis;
namespace LehrerApp.WebUntis.Tests;
public sealed class WebUntisLessonAbsenceParserTests
{
[Fact]
public void Parse_UebernimmtAlleFelderUndZweistelligesJahr()
{
const string report = "\uFEFFSchüler*innen\tDatum\tFehlstd.\tUnentsch. Fehlstd.\tFehlmin.\tUnentsch. Fehlmin.\tZeit\tAbwesenheitsgrund\tENr\tErledigt\tAbwesenheit zählt\r\n" +
"Erika Mustermann\t09.12.25\t1\t1\t45\t45\t09:40-10:25\tKrank\t(185120)\t09.12.25\ttrue\r\n";
var absence = Assert.Single(WebUntisLessonAbsenceParser.Parse(report));
Assert.Equal("Erika Mustermann", absence.StudentName);
Assert.Equal(20251209, absence.Date);
Assert.Equal(1, absence.AbsentPeriods);
Assert.Equal(1, absence.UnexcusedAbsentPeriods);
Assert.Equal(45, absence.AbsentMinutes);
Assert.Equal(45, absence.UnexcusedAbsentMinutes);
Assert.Equal(940, absence.StartTime);
Assert.Equal(1025, absence.EndTime);
Assert.Equal("Krank", absence.Reason);
Assert.Equal(185120, absence.ExternKey);
Assert.True(absence.ExternKeyInParentheses);
Assert.Equal("09.12.25", absence.HandledOn);
Assert.True(absence.Counts);
}
[Fact]
public void Parse_AkzeptiertExterneSchuelerkennungOhneKlammern()
{
const string report = "Schüler*innen\tDatum\tFehlstd.\tUnentsch. Fehlstd.\tFehlmin.\tUnentsch. Fehlmin.\tZeit\tAbwesenheitsgrund\tENr\tErledigt\tAbwesenheit zählt\r\n" +
"Max Muster\t27.01.26\t1\t0\t45\t0\t10:25-11:10\t\t178156\t\tfalse\r\n";
var absence = Assert.Single(WebUntisLessonAbsenceParser.Parse(report));
Assert.Equal(178156, absence.ExternKey);
Assert.False(absence.ExternKeyInParentheses);
Assert.Null(absence.Reason);
Assert.Null(absence.HandledOn);
Assert.False(absence.Counts);
}
[Fact]
public void Parse_LehntUngueltigesDatumAb()
{
const string report = "Schüler*innen\tDatum\tFehlstd.\tUnentsch. Fehlstd.\tFehlmin.\tUnentsch. Fehlmin.\tZeit\tAbwesenheitsgrund\tENr\tErledigt\tAbwesenheit zählt\r\n" +
"Max Muster\tkein-datum\t1\t0\t45\t0\t10:25-11:10\t\t178156\t\tfalse\r\n";
Assert.Throws<InvalidDataException>(() => WebUntisLessonAbsenceParser.Parse(report));
}
}