using CommunityToolkit.Mvvm.ComponentModel; using LehrerApp.Core.Interfaces; using LehrerApp.Core.Models; using LehrerApp.Desktop.Services; using LehrerApp.Desktop.ViewModels.Students; using LehrerApp.Templating; using System.Collections.ObjectModel; namespace LehrerApp.Desktop.ViewModels.Groups; /// Erzeugt aus einer in TemplateDesigner gebauten Arbeitsblatt-.lavorlage-Vorlage ein /// personalisiertes PDF je aktivem Mitglied der aktuell geöffneten Lerngruppe. Nutzt bewusst /// dieselbe Platzhalter-/Rendering-Infrastruktur wie der Elternbrief-Dialog /// (, ), aber eine getrennte /// -Vorlagenbibliothek. public partial class PersonalizeWorksheetDialogViewModel : ObservableObject { private readonly LearningGroup _group; private readonly WorksheetTemplateStore _templates; private readonly ITemplateRenderer _renderer; [ObservableProperty] private LetterTemplateChoice? _selectedTemplate; [ObservableProperty] private string _statusMessage = ""; public string GroupName => $"{_group.Name} · {_group.SchoolYear}"; public ObservableCollection Templates { get; } = []; public ObservableCollection Students { get; } = []; public ObservableCollection Results { get; } = []; public bool HasNoTemplates => Templates.Count == 0; public bool HasResults => Results.Count > 0; public PersonalizeWorksheetDialogViewModel(LearningGroup group, IReadOnlyList studentIds, IStudentRepository students, WorksheetTemplateStore templates, ITemplateRenderer renderer) { _group = group; _templates = templates; _renderer = renderer; foreach (var template in templates.Store.GetTemplates()) Templates.Add(new(template)); SelectedTemplate = Templates.FirstOrDefault(); foreach (var id in studentIds) if (students.GetById(id) is { } student) Students.Add(new(student)); } public void Generate(string outputFolder) { Results.Clear(); if (SelectedTemplate is null) return; LoadedTemplate loaded; try { loaded = _templates.Store.Load(SelectedTemplate.Model); } catch (Exception ex) when (ex is IOException or InvalidDataException or TemplateValidationException) { StatusMessage = $"Vorlage ist ungültig: {ex.Message}"; return; } Directory.CreateDirectory(outputFolder); foreach (var choice in Students.Where(x => x.IsIncluded)) { var student = choice.Model; try { var values = LetterPlaceholderBuilder.BuildStandardValues(student, contact: null, _group, DateOnly.FromDateTime(DateTime.Now), "", ""); var pdf = _renderer.RenderToPdf(loaded, new LetterDataProvider(values)); var fileName = SanitizeFileName($"{SelectedTemplate.Name}_{student.LastName}_{student.FirstName}.pdf"); File.WriteAllBytes(Path.Combine(outputFolder, fileName), pdf); Results.Add(new(student.FullName, true, "")); } catch (Exception ex) when (ex is IOException or InvalidDataException or UnauthorizedAccessException or TemplateValidationException) { Results.Add(new(student.FullName, false, ex.Message)); } } OnPropertyChanged(nameof(HasResults)); StatusMessage = $"{Results.Count(x => x.Success)} von {Results.Count} Arbeitsblättern erzeugt."; } private static string SanitizeFileName(string value) { foreach (var character in Path.GetInvalidFileNameChars()) value = value.Replace(character, '_'); return value; } } public partial class WorksheetStudentChoice(Student model) : ObservableObject { public Student Model { get; } = model; public string FullName => Model.FullName; [ObservableProperty] private bool _isIncluded = true; } public sealed record WorksheetGenerationResult(string StudentName, bool Success, string ErrorMessage) { public string Icon => Success ? "✓" : "⚠"; public string Color => Success ? "SeaGreen" : "#D97706"; }