fix: Fehlzeitenkalender in Templates, Datenbezug und Layout
CI / build-and-test (push) Canceled after 0s
CI / build-and-test (push) Canceled after 0s
This commit is contained in:
@@ -18,4 +18,18 @@ public sealed class UntisNameMatchingTests
|
|||||||
[InlineData("Ben Schmidt", "")]
|
[InlineData("Ben Schmidt", "")]
|
||||||
public void NamesMatch_LehntUnterschiedlicheOderFehlendeNamenAb(string? a, string b) =>
|
public void NamesMatch_LehntUnterschiedlicheOderFehlendeNamenAb(string? a, string b) =>
|
||||||
Assert.False(UntisNameMatching.NamesMatch(a, b));
|
Assert.False(UntisNameMatching.NamesMatch(a, b));
|
||||||
|
|
||||||
|
// Regression: Student.FullName liefert "Nachname, Vorname" (mit Komma) für Anzeigezwecke.
|
||||||
|
// Wird dieser String direkt an NamesMatch übergeben, bleibt das Komma am Wort kleben
|
||||||
|
// ("gerste," != "gerste") und der Abgleich gegen WebUntis-Namen (immer ohne Komma) schlägt
|
||||||
|
// fehl - genau das ließ den Anwesenheitskalender/die Fehlzeitenliste im Elternbrief leer
|
||||||
|
// bleiben, obwohl echte Fehlzeiten vorlagen. Aufrufer müssen deshalb "Vorname Nachname" ohne
|
||||||
|
// Komma bilden (siehe StudentAttendanceCalendarDrawingBuilder.StudentAttendanceCalendarService
|
||||||
|
// und ClassTeacherOverviewViewModel.cs:1173), statt Student.FullName direkt zu verwenden.
|
||||||
|
[Fact]
|
||||||
|
public void NamesMatch_KommaGetrennterAnzeigename_PasstNichtOhneUmformung()
|
||||||
|
{
|
||||||
|
Assert.False(UntisNameMatching.NamesMatch("Gerste, Amelia", "Gerste Amelia"));
|
||||||
|
Assert.True(UntisNameMatching.NamesMatch("Amelia Gerste", "Gerste Amelia"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,12 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
_ => .85f,
|
_ => .85f,
|
||||||
};
|
};
|
||||||
var contentWidth = width * scale;
|
var contentWidth = width * scale;
|
||||||
var cellWidth = contentWidth / 7;
|
|
||||||
var cellHeight = 10 * scale;
|
var cellHeight = 10 * scale;
|
||||||
var monthGap = 6 * scale;
|
var monthGapX = 6 * scale;
|
||||||
var first = options.NormalizedStartMonth;
|
var first = options.NormalizedStartMonth;
|
||||||
var monthCount = options.NormalizedMonthCount;
|
var monthCount = options.NormalizedMonthCount;
|
||||||
|
var monthWidth = (contentWidth - monthGapX * (monthCount - 1)) / monthCount;
|
||||||
|
var cellWidth = monthWidth / 7;
|
||||||
var commands = new List<DrawingCommand>();
|
var commands = new List<DrawingCommand>();
|
||||||
var y = 0f;
|
var y = 0f;
|
||||||
commands.Add(new DrawStringEx(0, y, 14 * scale, contentWidth, "Anwesenheit",
|
commands.Add(new DrawStringEx(0, y, 14 * scale, contentWidth, "Anwesenheit",
|
||||||
@@ -52,6 +53,8 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280"));
|
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280"));
|
||||||
y += 10 * scale + 2 * scale;
|
y += 10 * scale + 2 * scale;
|
||||||
var weekdays = new[] { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" };
|
var weekdays = new[] { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" };
|
||||||
|
var gridStartY = y;
|
||||||
|
var maxWeeks = 0;
|
||||||
for (var monthIndex = 0; monthIndex < monthCount; monthIndex++)
|
for (var monthIndex = 0; monthIndex < monthCount; monthIndex++)
|
||||||
{
|
{
|
||||||
var current = first.AddMonths(monthIndex);
|
var current = first.AddMonths(monthIndex);
|
||||||
@@ -59,30 +62,33 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
DateOnly.FromDateTime(DateTime.Today), studentName);
|
DateOnly.FromDateTime(DateTime.Today), studentName);
|
||||||
var offset = ((int)current.DayOfWeek + 6) % 7;
|
var offset = ((int)current.DayOfWeek + 6) % 7;
|
||||||
var weeks = (int)Math.Ceiling((offset + days.Count) / 7d);
|
var weeks = (int)Math.Ceiling((offset + days.Count) / 7d);
|
||||||
commands.Add(new DrawStringEx(0, y, 11 * scale, contentWidth,
|
maxWeeks = Math.Max(maxWeeks, weeks);
|
||||||
|
var xOffset = monthIndex * (monthWidth + monthGapX);
|
||||||
|
var monthY = gridStartY;
|
||||||
|
commands.Add(new DrawStringEx(xOffset, monthY, 11 * scale, monthWidth,
|
||||||
current.ToString("MMMM yyyy", CultureInfo.GetCultureInfo("de-DE")),
|
current.ToString("MMMM yyyy", CultureInfo.GetCultureInfo("de-DE")),
|
||||||
DrawingTextAlignment.AlignLeft, 9 * scale, Color: "#374151", Bold: true));
|
DrawingTextAlignment.AlignLeft, 9 * scale, Color: "#374151", Bold: true));
|
||||||
y += 11 * scale;
|
monthY += 11 * scale;
|
||||||
for (var column = 0; column < 7; column++)
|
for (var column = 0; column < 7; column++)
|
||||||
commands.Add(new DrawStringEx(column * cellWidth, y, 9 * scale, cellWidth, weekdays[column],
|
commands.Add(new DrawStringEx(xOffset + column * cellWidth, monthY, 9 * scale, cellWidth,
|
||||||
DrawingTextAlignment.AlignCenter, 7 * scale, Color: "#6B7280", Bold: true));
|
weekdays[column], DrawingTextAlignment.AlignCenter, 7 * scale, Color: "#6B7280", Bold: true));
|
||||||
y += 9 * scale;
|
monthY += 9 * scale;
|
||||||
|
|
||||||
foreach (var day in days)
|
foreach (var day in days)
|
||||||
{
|
{
|
||||||
var index = offset + day.Date.Day - 1;
|
var index = offset + day.Date.Day - 1;
|
||||||
var column = index % 7;
|
var column = index % 7;
|
||||||
var row = index / 7;
|
var row = index / 7;
|
||||||
var x = column * cellWidth;
|
var x = xOffset + column * cellWidth;
|
||||||
var cellY = y + row * cellHeight;
|
var cellY = monthY + row * cellHeight;
|
||||||
commands.Add(new DrawRectangle(x + scale, cellY, cellWidth - 2 * scale, cellHeight - scale,
|
commands.Add(new DrawRectangle(x + scale, cellY, cellWidth - 2 * scale, cellHeight - scale,
|
||||||
"#D1D5DB", .35f, day.HasSignal ? day.SignalColorHex : "#FFFFFF"));
|
"#D1D5DB", .35f, day.HasSignal ? day.SignalColorHex : "#FFFFFF"));
|
||||||
commands.Add(new DrawStringEx(x, cellY + scale, cellHeight - 2 * scale, cellWidth,
|
commands.Add(new DrawStringEx(x, cellY + scale, cellHeight - 2 * scale, cellWidth,
|
||||||
day.HasSignal ? day.SignalCode : day.DayNumber, DrawingTextAlignment.AlignCenter, 7 * scale,
|
day.HasSignal ? day.SignalCode : day.DayNumber, DrawingTextAlignment.AlignCenter, 7 * scale,
|
||||||
Color: day.HasSignal ? "#FFFFFF" : "#374151", Bold: day.HasSignal));
|
Color: day.HasSignal ? "#FFFFFF" : "#374151", Bold: day.HasSignal));
|
||||||
}
|
}
|
||||||
y += weeks * cellHeight + monthGap;
|
|
||||||
}
|
}
|
||||||
|
y = gridStartY + 11 * scale + 9 * scale + maxWeeks * cellHeight + monthGapX;
|
||||||
|
|
||||||
commands.Add(new DrawStringEx(0, y, 8 * scale, contentWidth,
|
commands.Add(new DrawStringEx(0, y, 8 * scale, contentWidth,
|
||||||
"U unentschuldigt · A abwesend · V verspätet · E entschuldigt · ! Klassenbuch",
|
"U unentschuldigt · A abwesend · V verspätet · E entschuldigt · ! Klassenbuch",
|
||||||
@@ -190,9 +196,10 @@ public sealed class StudentAttendanceCalendarService(
|
|||||||
|
|
||||||
var first = options.NormalizedStartMonth;
|
var first = options.NormalizedStartMonth;
|
||||||
var last = first.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
var last = first.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
||||||
|
var matchName = $"{student.FirstName} {student.LastName}";
|
||||||
var rosterName = rosterCache.GetByClass(className)
|
var rosterName = rosterCache.GetByClass(className)
|
||||||
.FirstOrDefault(r => UntisNameMatching.NamesMatch(r.DisplayName, student.FullName))?.DisplayName
|
.FirstOrDefault(r => UntisNameMatching.NamesMatch(r.DisplayName, matchName))?.DisplayName
|
||||||
?? student.FullName;
|
?? matchName;
|
||||||
var start = first.Year * 10000 + first.Month * 100 + first.Day;
|
var start = first.Year * 10000 + first.Month * 100 + first.Day;
|
||||||
var end = last.Year * 10000 + last.Month * 100 + last.Day;
|
var end = last.Year * 10000 + last.Month * 100 + last.Day;
|
||||||
var absences = absenceCache.GetByClassAndRange(className, start, end)
|
var absences = absenceCache.GetByClassAndRange(className, start, end)
|
||||||
@@ -215,9 +222,10 @@ public sealed class StudentAttendanceCalendarService(
|
|||||||
|
|
||||||
var first = options.NormalizedStartMonth;
|
var first = options.NormalizedStartMonth;
|
||||||
var last = first.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
var last = first.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
||||||
|
var matchName = $"{student.FirstName} {student.LastName}";
|
||||||
var rosterName = rosterCache.GetByClass(className)
|
var rosterName = rosterCache.GetByClass(className)
|
||||||
.FirstOrDefault(r => UntisNameMatching.NamesMatch(r.DisplayName, student.FullName))?.DisplayName
|
.FirstOrDefault(r => UntisNameMatching.NamesMatch(r.DisplayName, matchName))?.DisplayName
|
||||||
?? student.FullName;
|
?? matchName;
|
||||||
var start = first.Year * 10000 + first.Month * 100 + first.Day;
|
var start = first.Year * 10000 + first.Month * 100 + first.Day;
|
||||||
var end = last.Year * 10000 + last.Month * 100 + last.Day;
|
var end = last.Year * 10000 + last.Month * 100 + last.Day;
|
||||||
var absences = absenceCache.GetByClassAndRange(className, start, end)
|
var absences = absenceCache.GetByClassAndRange(className, start, end)
|
||||||
|
|||||||
Reference in New Issue
Block a user