Files
LehrerApp/LehrerApp.Desktop/Services/ExamCoverPlaceholderBuilder.cs
T
2026-10-22 11:24:43 +02:00

86 lines
5.2 KiB
C#

using System.Globalization;
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.ViewModels.Groups;
using LehrerApp.Templating;
namespace LehrerApp.Desktop.Services;
/// <summary>Platzhalterwerte für Klausur-Deckblätter (Vorlagen mit
/// <c>report-type=exam-cover</c>): die Standardwerte der Elternbriefe (Student.*, Group.Name, …)
/// plus Exam.*, Result.* und die Punktetabelle. Alle Punktwerte gelten für die Fassung, die der
/// Schüler geschrieben hat.</summary>
public static class ExamCoverPlaceholderBuilder
{
/// <summary>Stehen statt Punkten bzw. Note auf dem Blatt, solange nichts eingetragen wird —
/// als Linie zum Ausfüllen von Hand.</summary>
public const string BlankPoints = "_________", BlankGrade = "_____";
private static readonly CultureInfo German = CultureInfo.GetCultureInfo("de-DE");
/// <param name="result">Ergebnis des Schülers oder null. Punkte und Note werden nur
/// eingetragen, wenn <paramref name="fillResults"/> gesetzt und das Ergebnis korrigiert ist.</param>
/// <param name="studentNiveau">Niveau des Schülers in dieser Lerngruppe, falls die Arbeit
/// selbst keinem Niveau zugeordnet ist.</param>
/// <param name="title">Titel für das Deckblatt, falls er vom internen Klausurtitel abweichen
/// soll; null oder leer nimmt den Klausurtitel.</param>
public static Dictionary<string, PlaceholderValue> Build(Exam exam, LearningGroup group, string subjectName,
Student student, ExamResult? result, bool fillResults, GradingService grading, LoadedTemplate template,
DateOnly today, Niveau? studentNiveau = null, string teacherAbbreviation = "",
string? title = null, Exam? original = null, ExamResult? originalResult = null,
InterimGrade? interim = null)
{
var tasks = exam.Tasks.OrderBy(t => t.Nr).ToList();
var variant = result?.Variant ?? ExamVariant.A;
var maximum = ExamScoringService.Maximum(tasks, variant);
var filled = fillResults && ExamCoverSheetService.IsCorrected(result);
var values = LetterPlaceholderBuilder.BuildStandardValues(student, contact: null, group, today, "", "");
values["Group.GradeLevel"] = new TextValue(group.GradeLevel > 0 ? group.GradeLevel.ToString() : "");
values["Subject.Name"] = new TextValue(subjectName);
values["Exam.Title"] = new TextValue(string.IsNullOrWhiteSpace(title) ? exam.Title : title.Trim());
values["Exam.Date"] = new DateValue(exam.Date);
values["Exam.Number"] = new TextValue(exam.ExamNumber?.ToString() ?? "");
values["Exam.Kind"] = new TextValue(exam.Kind.Label());
values["Exam.Niveau"] = new TextValue(exam.Niveau.HasValue ? NiveauDisplay.ToName(exam.Niveau) : "");
values["Exam.Course"] = new TextValue(CourseLabel(group, exam.Niveau ?? studentNiveau));
values["Teacher.Abbreviation"] = new TextValue(teacherAbbreviation.Trim());
values["Exam.Variant"] = new TextValue(tasks.Any(t => t.Variant is not null) ? variant.ToString() : "");
values["Exam.MaxPoints"] = new TextValue(Format(maximum));
values["Exam.ApprovalDate"] = new TextValue(Format(exam.ApprovalGrantedAt));
values["Exam.AnnouncedDate"] = new TextValue(Format(exam.AnnouncedAt));
values["Exam.ReturnDate"] = new TextValue(Format(exam.ReturnedAt));
values["Result.Points"] = new TextValue(filled ? Format(result!.TotalPoints) : BlankPoints);
values["Result.Percent"] = new TextValue(filled && maximum > 0
? (result!.TotalPoints / maximum).ToString("0 %", German) : "");
values["Result.Grade"] = new TextValue(filled
? result!.Grade ?? grading.CalculateGrade(result.TotalPoints, maximum, exam.GradingKey) : BlankGrade);
// Ohne Box in der Vorlage gibt es nichts zu zeichnen (die Tabelle ist optional).
values[ExamPointsTableDrawingBuilder.PlaceholderName] =
LetterPlaceholderBuilder.FindDeclaredDrawingBox(template, ExamPointsTableDrawingBuilder.PlaceholderName) is { } box
? ExamPointsTableDrawingBuilder.Build(
ExamCoverSheetService.Columns(tasks, variant, filled ? result!.Points : null),
box.Width, box.MillimeterScale)
: new DrawingValue([], 0);
values[ReportTextDrawing.RepeatInfo] = DrawingBoxLocator.Find(template, ReportTextDrawing.RepeatInfo) is null
? new DrawingValue([], 0) : GradeReportDrawingBuilder.Repeat(exam, original, originalResult, template);
values[ReportTextDrawing.InterimGrades] = interim is null
? new DrawingValue([], 0) : GradeReportDrawingBuilder.Interim(interim, template);
return values;
}
/// <summary>"E-Kurs" / "G-Kurs" für die äußere Differenzierung; leer im Klassenverband und
/// ohne E-/G-Zuordnung.</summary>
public static string CourseLabel(LearningGroup group, Niveau? niveau) =>
group.Type == GroupType.Class ? "" : niveau switch
{
Niveau.E => "E-Kurs",
Niveau.G => "G-Kurs",
_ => "",
};
private static string Format(double value) => value.ToString("0.##", German);
private static string Format(DateOnly? date) => date?.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture) ?? "";
}