Dashboard-Performance, CI-Workflow, Settings-Aufteilung, Backup-Härtung

- 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>
This commit is contained in:
2026-08-30 00:52:44 +02:00
co-authored by Claude Sonnet 5
parent dbd8777e64
commit 2b299fc940
37 changed files with 2547 additions and 1823 deletions
@@ -0,0 +1,36 @@
using System.Text.Json;
namespace LehrerApp.Desktop.Services;
internal sealed class BackupSettingsConfig
{
public string? SecondaryDirectory { get; set; }
}
/// <summary>Merkt sich einen optionalen zweiten Sicherungsordner (13.3.1 Nachtrag) — z.B. ein
/// USB-Stick oder Netzlaufwerk, damit ein Backup nicht ausschließlich neben der Live-Datenbank
/// liegt und bei Plattenausfall oder gelöschtem Profilverzeichnis mit verloren geht. Gleiches
/// Muster wie <see cref="AppearanceSettingsService"/>. Das eigentliche Kopieren übernimmt
/// <see cref="LehrerApp.Core.Services.BackupService"/> bewusst best-effort.</summary>
public sealed class BackupSettingsService
{
private readonly string _configPath;
public BackupSettingsService(string appDataPath) =>
_configPath = Path.Combine(appDataPath, "backupsettings.json");
public string? LoadSecondaryDirectory()
{
try
{
if (File.Exists(_configPath))
return JsonSerializer.Deserialize<BackupSettingsConfig>(File.ReadAllText(_configPath))
?.SecondaryDirectory;
}
catch { /* beschädigte Konfiguration -> kein zweites Ziel */ }
return null;
}
public void SaveSecondaryDirectory(string? path) =>
File.WriteAllText(_configPath, JsonSerializer.Serialize(new BackupSettingsConfig { SecondaryDirectory = path }));
}