feat: Fehlzeiten Tabelle für Templates
This commit is contained in:
@@ -20,7 +20,9 @@ public static class LetterDialogs
|
||||
App.Services.GetRequiredService<ITemplateRenderer>(),
|
||||
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
||||
App.Services.GetRequiredService<IGroupRepository>(),
|
||||
options => App.Services.GetRequiredService<StudentAttendanceCalendarService>().Build(student, options));
|
||||
options => App.Services.GetRequiredService<StudentAttendanceCalendarService>().Build(student, options),
|
||||
options => App.Services.GetRequiredService<StudentAttendanceCalendarService>()
|
||||
.BuildAbsenceDayList(student, options));
|
||||
var dialog = new CreateLetterDialog { DataContext = vm };
|
||||
var path = await dialog.ShowDialog<string?>(owner);
|
||||
if (!string.IsNullOrEmpty(path) && File.Exists(path))
|
||||
|
||||
@@ -15,7 +15,8 @@ public static class LetterPlaceholderBuilder
|
||||
{
|
||||
public static Dictionary<string, PlaceholderValue> BuildStandardValues(
|
||||
Student student, Contact? contact, LearningGroup? group, DateOnly date,
|
||||
string letterText, string teacherName, DrawingValue? attendanceCalendar = null)
|
||||
string letterText, string teacherName, DrawingValue? attendanceCalendar = null,
|
||||
DrawingValue? absenceDays = null)
|
||||
{
|
||||
var cityLine = string.Join(" ", new[] { contact?.PostalCode, contact?.City }.Where(x => !string.IsNullOrWhiteSpace(x)));
|
||||
var address = string.Join(Environment.NewLine, new[] { contact?.Name, contact?.Street, cityLine }.Where(x => !string.IsNullOrWhiteSpace(x)));
|
||||
@@ -30,6 +31,7 @@ public static class LetterPlaceholderBuilder
|
||||
["Contact.City"] = new TextValue(contact?.City ?? ""), ["Letter.Salutation"] = new TextValue(contact?.LetterSalutation ?? ""),
|
||||
["Group.Name"] = new TextValue(group?.Name ?? ""), ["SchoolYear"] = new TextValue(group?.SchoolYear ?? ""),
|
||||
[StudentAttendanceCalendarDrawingBuilder.PlaceholderName] = attendanceCalendar ?? new DrawingValue([], 0),
|
||||
[StudentAbsenceDayListDrawingBuilder.PlaceholderName] = absenceDays ?? new DrawingValue([], 0),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ public class LetterTemplateTools(
|
||||
var date = letterDate ?? DateOnly.FromDateTime(DateTime.Today);
|
||||
|
||||
var values = LetterPlaceholderBuilder.BuildStandardValues(student, contact, group, date, letterText,
|
||||
teacherName, attendanceCalendars?.Build(student, date));
|
||||
teacherName, attendanceCalendars?.Build(student, date),
|
||||
attendanceCalendars?.BuildAbsenceDayList(student, new AttendanceCalendarOptions(date, 1)));
|
||||
foreach (var (name, raw) in extraValues ?? [])
|
||||
{
|
||||
var definition = loaded.Manifest.Placeholders.FirstOrDefault(p => p.Name == name);
|
||||
|
||||
@@ -91,6 +91,87 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Chronologische, portable Fehlzeitenliste für Elternbriefvorlagen.</summary>
|
||||
public static class StudentAbsenceDayListDrawingBuilder
|
||||
{
|
||||
public const string PlaceholderName = "Student.AbsenceDays";
|
||||
|
||||
public static DrawingValue Build(string studentName, AttendanceCalendarOptions options,
|
||||
IReadOnlyList<ClassAbsenceDaySummaryRow> absences)
|
||||
{
|
||||
const float width = 170;
|
||||
var scale = options.Size switch
|
||||
{
|
||||
AttendanceCalendarSize.Small => .75f,
|
||||
AttendanceCalendarSize.Large => 1f,
|
||||
_ => .88f,
|
||||
};
|
||||
var contentWidth = width * scale;
|
||||
var rowHeight = 9 * scale;
|
||||
var start = options.NormalizedStartMonth;
|
||||
var end = start.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
||||
var rows = absences
|
||||
.Where(a => a.Date >= start && a.Date <= end &&
|
||||
UntisNameMatching.NamesMatch(a.StudentName, studentName))
|
||||
.OrderBy(a => a.Date)
|
||||
.ToList();
|
||||
var commands = new List<DrawingCommand>
|
||||
{
|
||||
new DrawStringEx(0, 0, 7 * scale, contentWidth, "Fehltage",
|
||||
DrawingTextAlignment.AlignLeft, 11 * scale, Color: "#1F2937", Bold: true),
|
||||
new DrawStringEx(0, 7 * scale, 6 * scale, contentWidth, studentName,
|
||||
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280"),
|
||||
};
|
||||
var y = 16 * scale;
|
||||
commands.Add(new DrawRectangle(0, y, contentWidth, rowHeight, "#CBD5E1", .4f, "#F3F4F6"));
|
||||
commands.Add(new DrawStringEx(2 * scale, y + scale, rowHeight - 2 * scale, 31 * scale, "Datum",
|
||||
DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
||||
commands.Add(new DrawStringEx(36 * scale, y + scale, rowHeight - 2 * scale, 75 * scale, "Umfang",
|
||||
DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
||||
commands.Add(new DrawStringEx(113 * scale, y + scale, rowHeight - 2 * scale, 55 * scale, "Status",
|
||||
DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
||||
y += rowHeight;
|
||||
|
||||
if (rows.Count == 0)
|
||||
{
|
||||
commands.Add(new DrawStringEx(2 * scale, y + 2 * scale, rowHeight, contentWidth - 4 * scale,
|
||||
"Keine Fehltage im gewählten Zeitraum", DrawingTextAlignment.AlignLeft, 8 * scale,
|
||||
Color: "#6B7280", Italic: true));
|
||||
y += rowHeight + 3 * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var index = 0; index < rows.Count; index++)
|
||||
{
|
||||
var row = rows[index];
|
||||
var fill = index % 2 == 0 ? "#FFFFFF" : "#F9FAFB";
|
||||
var extent = row.CountsAsFullDay
|
||||
? "Ganzer Fehltag"
|
||||
: row.TotalAbsentPeriods > 0
|
||||
? $"Fehlzeit · {row.TotalAbsentPeriods} Std."
|
||||
: $"Fehlzeit · {row.TotalAbsentMinutes} Min.";
|
||||
var status = row.IsUnexcused
|
||||
? "Unentschuldigt"
|
||||
: row.FriendlyStatusLabel.Contains("Entschuldigt", StringComparison.OrdinalIgnoreCase)
|
||||
? "Entschuldigt"
|
||||
: row.FriendlyStatusLabel;
|
||||
var statusColor = row.IsUnexcused ? "#C62828" : "#2E7D32";
|
||||
commands.Add(new DrawRectangle(0, y, contentWidth, rowHeight, "#E5E7EB", .3f, fill));
|
||||
commands.Add(new DrawStringEx(2 * scale, y + scale, rowHeight - 2 * scale, 31 * scale,
|
||||
row.Date.ToString("dd.MM.yyyy"), DrawingTextAlignment.AlignLeft, 7.5f * scale,
|
||||
Color: "#374151"));
|
||||
commands.Add(new DrawStringEx(36 * scale, y + scale, rowHeight - 2 * scale, 75 * scale,
|
||||
extent, DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151"));
|
||||
commands.Add(new DrawStringEx(113 * scale, y + scale, rowHeight - 2 * scale, 55 * scale,
|
||||
status, DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: statusColor,
|
||||
Bold: row.IsUnexcused));
|
||||
y += rowHeight;
|
||||
}
|
||||
}
|
||||
return new DrawingValue(commands, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Liest ausschließlich den lokalen WebUntis-Cache. Das Öffnen oder Rendern eines
|
||||
/// Elternbriefs löst dadurch keinen unerwarteten Netzwerkabruf aus.</summary>
|
||||
public sealed class StudentAttendanceCalendarService(
|
||||
@@ -126,4 +207,24 @@ public sealed class StudentAttendanceCalendarService(
|
||||
return StudentAttendanceCalendarDrawingBuilder.Build(rosterName, options,
|
||||
ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absences), register);
|
||||
}
|
||||
|
||||
public DrawingValue BuildAbsenceDayList(Student student, AttendanceCalendarOptions options)
|
||||
{
|
||||
var className = settings.HomeroomClassName;
|
||||
if (string.IsNullOrWhiteSpace(className)) return new DrawingValue([], 0);
|
||||
|
||||
var first = options.NormalizedStartMonth;
|
||||
var last = first.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
||||
var rosterName = rosterCache.GetByClass(className)
|
||||
.FirstOrDefault(r => UntisNameMatching.NamesMatch(r.DisplayName, student.FullName))?.DisplayName
|
||||
?? student.FullName;
|
||||
var start = first.Year * 10000 + first.Month * 100 + first.Day;
|
||||
var end = last.Year * 10000 + last.Month * 100 + last.Day;
|
||||
var absences = absenceCache.GetByClassAndRange(className, start, end)
|
||||
.Select(e => new UntisClassAbsenceEntryDto(e.StudentName, e.ExternKey, e.ClassName, e.Date,
|
||||
e.AbsentPeriods, e.AbsentMinutes, e.TeacherUsernames, e.Subject, e.AbsenceReason, e.Note,
|
||||
e.EntryId, e.HandledOn, e.Counts, e.ExcuseNote, e.PeriodNumber, e.Status, e.CountsAsFullDay));
|
||||
return StudentAbsenceDayListDrawingBuilder.Build(rosterName, options,
|
||||
ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absences));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user