- Dashboard: Fehlzeiten-Warnung lädt Mitarbeitssitzungen einmal vorab statt pro Schüler/Eintrag einzeln nachzuschlagen (N+1 vermieden); neues IParticipationSessionRepository.GetAll() dafür. - CI: .gitea/workflows/ci.yml baut und testet bei jedem Push/PR. Dabei fehlende Release|Any CPU-Konfiguration für 6 Projekte in der .sln behoben (LehrerApp.Data.Tests wurde bei Release-Builds der Solution bislang stillschweigend übersprungen). TreatWarningsAsErrors jetzt aktiv. - SettingsViewModel (1986 Zeilen) als partial class auf 20 Themen- dateien aufgeteilt, Verhalten unverändert. - Backup: optionaler zweiter Sicherungsordner (USB-Stick/Netzlaufwerk, best-effort) und Integritätsprüfung nach jedem Backup (DatabaseEncryptionService.CanOpenAndRead). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
155 lines
6.0 KiB
C#
155 lines
6.0 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using CommunityToolkit.Mvvm.Input;
|
||
using LehrerApp.Core.Interfaces;
|
||
using LehrerApp.Core.Models;
|
||
using LehrerApp.Core.Services;
|
||
using LehrerApp.Data;
|
||
using LehrerApp.Desktop.Services;
|
||
using LehrerApp.Desktop.ViewModels.Planning;
|
||
using LehrerApp.Sync;
|
||
using LehrerApp.Sync.Crypto;
|
||
using System.Collections.ObjectModel;
|
||
using System.Globalization;
|
||
using System.Security.Cryptography;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
|
||
namespace LehrerApp.Desktop.ViewModels.Settings;
|
||
|
||
public partial class SettingsViewModel
|
||
{
|
||
// ── Notenschlüssel-Vorlagen (1.3.2) ──────────────────────────────────────
|
||
|
||
[ObservableProperty] private string _newTemplateName = "";
|
||
[ObservableProperty] private string _newTemplateGradingSystemName = "Noten 1–6";
|
||
[ObservableProperty] private string _newTemplateNameError = "";
|
||
|
||
public List<string> GradingSystemOptions { get; } = ["Noten 1–6", "Punkte 0–15"];
|
||
public ObservableCollection<GradingKeyTemplateEditItem> GradingKeyTemplateList { get; } = [];
|
||
|
||
// ── Notenschlüssel-Vorlagen: Laden / Hinzufügen / Löschen ────────────────
|
||
|
||
private void LoadGradingKeyTemplates()
|
||
{
|
||
GradingKeyTemplateList.Clear();
|
||
foreach (var t in _gradingKeyTemplates.GetAll())
|
||
GradingKeyTemplateList.Add(new GradingKeyTemplateEditItem(t, _gradingKeyTemplates, _grading));
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void AddGradingKeyTemplate()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(NewTemplateName)) { NewTemplateNameError = "Vorlagenname erforderlich."; return; }
|
||
|
||
var system = NewTemplateGradingSystemName == "Punkte 0–15"
|
||
? GradingSystem.Points0To15 : GradingSystem.Grades1To6;
|
||
var defaults = system == GradingSystem.Grades1To6
|
||
? GradingService.DefaultKey1To6() : GradingService.DefaultKey0To15();
|
||
|
||
var template = new GradingKeyTemplate
|
||
{
|
||
Name = NewTemplateName.Trim(),
|
||
GradingSystem = system,
|
||
Entries = defaults.Select(e => new GradingKeyEntry { Grade = e.Grade, MinPercent = e.MinPercent }).ToList(),
|
||
};
|
||
_gradingKeyTemplates.Save(template);
|
||
GradingKeyTemplateList.Add(new GradingKeyTemplateEditItem(template, _gradingKeyTemplates, _grading));
|
||
NewTemplateName = "";
|
||
NewTemplateNameError = "";
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void DeleteGradingKeyTemplate(GradingKeyTemplateEditItem? item)
|
||
{
|
||
if (item is null) return;
|
||
_gradingKeyTemplates.Delete(item.Id);
|
||
GradingKeyTemplateList.Remove(item);
|
||
}
|
||
}
|
||
|
||
public partial class GradingKeyTemplateEditItem : ObservableObject
|
||
{
|
||
private readonly GradingKeyTemplate _template;
|
||
private readonly IGradingKeyTemplateRepository _repo;
|
||
private readonly GradingService _grading;
|
||
|
||
public Guid Id { get; }
|
||
public string Name { get; }
|
||
public string GradingSystemLabel { get; }
|
||
|
||
[ObservableProperty] private string _newGrade = "";
|
||
[ObservableProperty] private double _newMinPercent;
|
||
[ObservableProperty] private string _newGradeError = "";
|
||
[ObservableProperty] private string _newMinPercentError = "";
|
||
[ObservableProperty] private string _completenessWarning = "";
|
||
|
||
public ObservableCollection<GradingKeyEntryVm> Entries { get; } = [];
|
||
|
||
public GradingKeyTemplateEditItem(GradingKeyTemplate template, IGradingKeyTemplateRepository repo,
|
||
GradingService grading)
|
||
{
|
||
_template = template; _repo = repo; _grading = grading;
|
||
Id = template.Id;
|
||
Name = template.Name;
|
||
GradingSystemLabel = template.GradingSystem == GradingSystem.Grades1To6
|
||
? "Noten 1–6" : "Punkte 0–15";
|
||
|
||
foreach (var e in template.Entries.OrderByDescending(e => e.MinPercent))
|
||
Entries.Add(new GradingKeyEntryVm(e, DeleteEntry));
|
||
RecomputeCompleteness();
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void AddEntry()
|
||
{
|
||
NewGradeError = ""; NewMinPercentError = "";
|
||
var valid = true;
|
||
|
||
if (string.IsNullOrWhiteSpace(NewGrade)) { NewGradeError = "Bezeichnung erforderlich."; valid = false; }
|
||
if (NewMinPercent is < 0 or > 100) { NewMinPercentError = "Muss zwischen 0 und 100 liegen."; valid = false; }
|
||
else if (_template.Entries.Any(e => Math.Abs(e.MinPercent - NewMinPercent) < 0.0001))
|
||
{ NewMinPercentError = "Diese Prozentgrenze existiert bereits."; valid = false; }
|
||
|
||
if (!valid) return;
|
||
|
||
var entry = new GradingKeyEntry { Grade = NewGrade.Trim(), MinPercent = NewMinPercent };
|
||
_template.Entries.Add(entry);
|
||
_template.Entries = _template.Entries.OrderByDescending(e => e.MinPercent).ToList();
|
||
_repo.Save(_template);
|
||
|
||
Entries.Clear();
|
||
foreach (var e in _template.Entries) Entries.Add(new GradingKeyEntryVm(e, DeleteEntry));
|
||
NewGrade = ""; NewMinPercent = 0;
|
||
RecomputeCompleteness();
|
||
}
|
||
|
||
private void DeleteEntry(GradingKeyEntryVm vm)
|
||
{
|
||
_template.Entries.RemoveAll(e => e.Grade == vm.Grade && Math.Abs(e.MinPercent - vm.MinPercent) < 0.0001);
|
||
_repo.Save(_template);
|
||
Entries.Remove(vm);
|
||
RecomputeCompleteness();
|
||
}
|
||
|
||
private void RecomputeCompleteness() =>
|
||
CompletenessWarning = _grading.ValidateGradingKey(_template.Entries) ?? "";
|
||
}
|
||
|
||
// ── GradingKeyEntryVm ─────────────────────────────────────────────────────────
|
||
|
||
public class GradingKeyEntryVm
|
||
{
|
||
public string Grade { get; }
|
||
public double MinPercent { get; }
|
||
public string Display { get; }
|
||
public IRelayCommand DeleteCommand { get; }
|
||
|
||
public GradingKeyEntryVm(GradingKeyEntry e, Action<GradingKeyEntryVm> onDelete)
|
||
{
|
||
Grade = e.Grade;
|
||
MinPercent = e.MinPercent;
|
||
Display = $"{Grade} — ab {MinPercent.ToString("0.##")} %";
|
||
DeleteCommand = new RelayCommand(() => onDelete(this));
|
||
}
|
||
}
|