fix: WebUntis-Schülerreport verkraftet fehlenden externKey
Klassenlehrer-Übersicht scheiterte komplett mit "Ungültige Zahl in Spalte externKey", sobald ein Schüler der Klasse keinen gepflegten externen Schlüssel hat (in echten Schuldaten vorkommend). externKey ist jetzt optional statt Pflichtfeld (UntisStudent/UntisStudentDto.ExternKey -> int?); betraf denselben Bericht wie der bestehende WebUntis-Klassenimport, der das aber schon immer beim Weiterimport toleriert hatte. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ public sealed record UntisTimetablePeriodDto(int Id, int Date, int StartTime, in
|
||||
IReadOnlyList<UntisEntityDto> Subjects, IReadOnlyList<UntisEntityDto> Rooms);
|
||||
public sealed record UntisStudentAddressDto(string? Email, string? Mobile, string? Phone, string? City,
|
||||
string? PostCode, string? Street);
|
||||
public sealed record UntisStudentDto(int UntisId, int ExternKey, string ClassName, string? Name, string? LongName,
|
||||
public sealed record UntisStudentDto(int UntisId, int? ExternKey, string ClassName, string? Name, string? LongName,
|
||||
string? ForeName, string DisplayName, string? Gender, int? BirthDate, string? BirthDateRaw, int? EntryDate,
|
||||
string? EntryDateRaw, int? ExitDate, string? ExitDateRaw, string? Text, string? MedicalReportDuty,
|
||||
string? Schulpflicht, string? Majority, UntisStudentAddressDto Address, string? AttributeIL);
|
||||
|
||||
@@ -38,7 +38,7 @@ public sealed record ClassTeacherRosterRow(string StudentName, int? ExternKey, b
|
||||
return students
|
||||
.Select(s =>
|
||||
{
|
||||
var absence = absenceByKey.GetValueOrDefault(s.ExternKey)
|
||||
var absence = (s.ExternKey is { } key ? absenceByKey.GetValueOrDefault(key) : null)
|
||||
?? absenceByName.GetValueOrDefault(s.DisplayName);
|
||||
return new ClassTeacherRosterRow(s.DisplayName, s.ExternKey, absence is not null,
|
||||
absence is null ? null : $"{absence.TotalAbsentPeriods} Stunde(n) — {absence.StatusLabel}",
|
||||
|
||||
@@ -51,6 +51,19 @@ public sealed class WebUntisStudentReportParserTests
|
||||
Assert.Equal(20270731, student.ExitDate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_ErlaubtFehlendenExternKey()
|
||||
{
|
||||
// Reale Schuldaten enthalten Schüler*innen ohne gepflegten externen Schlüssel (z.B. frisch
|
||||
// angelegt) - das darf nicht den Abruf der gesamten Klassenliste zum Absturz bringen.
|
||||
const string report = "id\texternKey\tklasse.name\r\n17\t\t10a\r\n";
|
||||
|
||||
var student = Assert.Single(WebUntisStudentReportParser.Parse(report));
|
||||
|
||||
Assert.Equal(17, student.UntisId);
|
||||
Assert.Null(student.ExternKey);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_LehntUngueltigePflichtIdAb()
|
||||
{
|
||||
|
||||
@@ -41,9 +41,12 @@ public sealed record UntisStudentAddress(
|
||||
string? PostCode,
|
||||
string? Street);
|
||||
|
||||
// ExternKey optional: reale Schuldaten enthalten Schüler*innen (z.B. frisch angelegt/übernommen)
|
||||
// ohne gepflegten externen Schlüssel - ein fehlender Wert bei einer/einem soll nicht den Abruf der
|
||||
// gesamten Klassenliste zum Absturz bringen (siehe WebUntisStudentReportParser).
|
||||
public sealed record UntisStudent(
|
||||
int UntisId,
|
||||
int ExternKey,
|
||||
int? ExternKey,
|
||||
string ClassName,
|
||||
string? Name,
|
||||
string? LongName,
|
||||
|
||||
@@ -23,7 +23,7 @@ public static class WebUntisStudentReportParser
|
||||
values[headers[index]] = index < row.Count ? row[index].Trim() : null;
|
||||
|
||||
var untisId = RequiredInt(Get(values, "id"), "id");
|
||||
var externalKey = RequiredInt(Get(values, "externKey"), "externKey");
|
||||
var externalKey = OptionalInt(Get(values, "externKey"));
|
||||
var name = Optional(Get(values, "name"));
|
||||
var lastName = Optional(Get(values, "longName"));
|
||||
var firstName = Optional(Get(values, "foreName"));
|
||||
@@ -73,6 +73,11 @@ public static class WebUntisStudentReportParser
|
||||
? parsed
|
||||
: throw new InvalidDataException($"Ungültige Zahl in Spalte \"{field}\".");
|
||||
|
||||
private static int? OptionalInt(string? value) =>
|
||||
int.TryParse(value?.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsed)
|
||||
? parsed
|
||||
: null;
|
||||
|
||||
private static int? GermanDate(string? value)
|
||||
{
|
||||
if (!DateOnly.TryParseExact(value?.Trim(), ["dd.MM.yyyy", "yyyyMMdd"], CultureInfo.InvariantCulture,
|
||||
|
||||
@@ -1328,6 +1328,15 @@ eigenen Unterricht abfragt und deshalb mit den regulären Lehrkraft-Rechten funk
|
||||
Schülerdetail; ein Detail-Drill-down pro Schüler*in (Heatmap gefehlter Einzelstunden,
|
||||
Verspätung/vorzeitiges Verlassen je Woche); eine KI-gestützte Zusammenfassung/Bewertung der
|
||||
Einträge (pro Schüler*in oder gesamt) mit Namens-Anonymisierung/Synonymen vor dem Versand.
|
||||
**Echter Fehler gefunden und behoben:** Die Übersicht rief für das Roster
|
||||
`WebUntisIntegrationService.GetStudentsAsync` auf (den bestehenden, schon länger genutzten
|
||||
Schülerlisten-Bericht) — `WebUntisStudentReportParser` behandelte die Spalte `externKey` bisher als
|
||||
Pflichtfeld (`RequiredInt`) und ließ den kompletten Abruf mit "Ungültige Zahl in Spalte
|
||||
\"externKey\"" scheitern, sobald auch nur eine/einer der Schüler*innen der Klasse keinen
|
||||
gepflegten externen Schlüssel hat (in der Praxis vorkommend, z. B. frisch angelegt). `externKey`
|
||||
ist jetzt optional (`UntisStudent.ExternKey`/`UntisStudentDto.ExternKey` → `int?`) — betraf auch
|
||||
den bestehenden WebUntis-Klassenimport in der Kursansicht, der das aber schon immer über
|
||||
`FirstNotEmpty` beim Weiterimport toleriert hatte und deshalb nicht auffiel.
|
||||
|
||||
### 4.4 Wochen-/Tagesansicht
|
||||
- [x] **4.4.1** Kalenderansicht über alle Gruppen: Woche und Tag — siehe Nachtrag zu 4.3
|
||||
|
||||
Reference in New Issue
Block a user