Files
adminandClaude Sonnet 5 056e864edd 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>
2026-08-25 22:47:04 +02:00

220 lines
6.0 KiB
C#

using System.Text.Json;
namespace LehrerApp.WebUntis;
public sealed class WebUntisOptions
{
public string School { get; set; } = "";
public string Host { get; set; } = "";
public string Username { get; set; } = "";
public string Password { get; set; } = "";
public string Client { get; set; } = "LehrerApp";
public int SessionIdleTimeoutMinutes { get; set; } = 10;
}
public sealed record UntisSchoolYear(int UntisId, string Name, int StartDate, int EndDate);
public sealed record UntisClass(
int UntisId,
string Name,
string? LongName,
string? ForeColor,
string? BackColor,
int? DepartmentUntisId,
int? Teacher1UntisId,
int? Teacher2UntisId);
public sealed record UntisTeacher(
int UntisId,
string Name,
string? ForeName,
string? LongName,
string? Title,
bool Active,
IReadOnlyList<int> DepartmentUntisIds);
public sealed record UntisStudentAddress(
string? Email,
string? Mobile,
string? Phone,
string? City,
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,
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,
UntisStudentAddress Address,
string? AttributeIL);
public sealed record UntisStudentReport(
int Count,
string? ClassNameFilter,
IReadOnlyList<UntisStudent> Students);
public sealed record UntisTimeUnit(string Name, int StartTime, int EndTime);
public sealed record UntisTimeGridDay(
int Day,
IReadOnlyList<UntisTimeUnit> TimeUnits,
JsonElement Raw);
public sealed record UntisHoliday(
int UntisId,
string Name,
string? LongName,
int StartDate,
int EndDate);
public sealed record UntisEntity(
int Id,
string Name,
int? OriginalId,
string? OriginalName,
string? ExternalKey);
public sealed record UntisReschedule(int Date, int StartTime, int EndTime);
public sealed record UntisSubstitution(
string Type,
int? LessonId,
string? LessonType,
int Date,
int StartTime,
int EndTime,
string? Text,
IReadOnlyList<UntisEntity> Classes,
IReadOnlyList<UntisEntity> Teachers,
IReadOnlyList<UntisEntity> Subjects,
IReadOnlyList<UntisEntity> Rooms,
UntisReschedule? Reschedule,
JsonElement Raw);
public enum UntisTimetableElementType
{
Class = 1,
Teacher = 2,
Subject = 3,
Room = 4,
Student = 5,
}
public sealed record UntisTimetablePeriod(
int Id,
int Date,
int StartTime,
int EndTime,
string? Code,
string? ActivityType,
string? Info,
string? LessonText,
string? SubstitutionText,
string? StudentGroup,
IReadOnlyList<UntisEntity> Classes,
IReadOnlyList<UntisEntity> Teachers,
IReadOnlyList<UntisEntity> Subjects,
IReadOnlyList<UntisEntity> Rooms,
JsonElement Raw);
public sealed record UntisLessonAbsence(
string StudentName,
int Date,
int AbsentPeriods,
int UnexcusedAbsentPeriods,
int AbsentMinutes,
int UnexcusedAbsentMinutes,
int? StartTime,
int? EndTime,
string? Reason,
int? ExternKey,
bool ExternKeyInParentheses,
string? HandledOn,
bool Counts);
// Undokumentierter Bericht "AbsencePerStudent" (Fehlzeiten der ganzen Klasse über alle Fächer,
// nicht nur den eigenen Unterricht wie AbsencePerLesson) - eigenes Spaltenschema, eine Zeile pro
// Fehlstunde. Anders als bei AbsencePerLesson liefert "Status" hier einen Klartext-Wert direkt
// (im Sample nur "nicht entsch." beobachtet, andere Werte nicht bestätigt - Rohwert durchreichen).
// "Fehltage" ist der auf eine ganze Zahl abgerundete Fehlzeit-Tagesanteil (Nutzer-Angabe, nicht
// durch WebUntis-Dokumentation bestätigt): bleibt 0, bis auch die letzte Unterrichtsstunde des
// Tages als versäumt gilt - erst dann springt er auf 1. Pro Schüler*in/Tag trägt deshalb genau die
// Zeile der letzten Stunde des Tages den Wert 1, alle vorherigen Stunden dieses Tages bleiben 0.
public sealed record UntisClassAbsenceEntry(
string StudentName,
int? ExternKey,
string ClassName,
int Date,
int AbsentPeriods,
int AbsentMinutes,
string? TeacherUsernames,
string? Subject,
string? AbsenceReason,
string? Note,
int? EntryId,
string? HandledOn,
bool Counts,
string? ExcuseNote,
int? PeriodNumber,
string? Status,
bool CountsAsFullDay);
// "-alle-"-Bericht ohne externe Schülerkennung - anders als bei AbsencePerLesson bleibt für den
// Abgleich mit lokalen Schülern nur der Name (siehe Namens-Fallback im Desktop-Abgleich).
public sealed record UntisClassRegisterEventReportEntry(
string ClassName,
int Date,
string? Subject,
string StudentName,
string? TeacherUsername,
string? CategoryName,
string? CategoryGroup,
string? Text);
public sealed record UntisClassRegisterEntry(
int? StudentKey,
string? Surname,
string? ForeName,
string DisplayName,
int Date,
string? Subject,
int? CategoryId,
string? Reason,
string? Text,
JsonElement Raw);
public sealed record UntisClassRegisterCategory(
int Id,
string Name,
string? LongName,
int? GroupId,
JsonElement Raw);
public sealed record UntisClassRegisterCategoryGroup(
int Id,
string Name,
JsonElement Raw);
public sealed class WebUntisException(string message, Exception? innerException = null)
: Exception(message, innerException);
public sealed class WebUntisConfigurationException(string message) : Exception(message);