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:
@@ -0,0 +1,123 @@
|
||||
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
|
||||
{
|
||||
// ── JSON Import / Export ──────────────────────────────────────────────────
|
||||
|
||||
public CompetencyCatalogImportPreview? PrepareCatalogImport(string json)
|
||||
{
|
||||
if (CatalogSubject is null)
|
||||
{
|
||||
CatalogValidation = "Bitte zuerst ein Fach auswählen.";
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
CatalogValidation = "";
|
||||
return _catalogImport.Analyze(
|
||||
json, CatalogSubject.Id, CatalogSubject.Name, CatalogGradeLevel);
|
||||
}
|
||||
catch (InvalidDataException ex)
|
||||
{
|
||||
CatalogValidation = ex.Message;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyCatalogImport(CompetencyCatalogImportPreview preview,
|
||||
CompetencyCatalogImportMode mode, IReadOnlySet<string> useImportedConflicts)
|
||||
{
|
||||
_catalogImport.Apply(preview, mode, useImportedConflicts);
|
||||
LoadCatalog();
|
||||
CatalogValidation = mode == CompetencyCatalogImportMode.Merge
|
||||
? "Kompetenzkatalog wurde sicher zusammengeführt."
|
||||
: "Kompetenzkatalog wurde vollständig ersetzt.";
|
||||
}
|
||||
|
||||
public string ExportCatalog()
|
||||
=> SerializeCatalog(CatalogGradeLevel);
|
||||
|
||||
public CompetencyCatalogImportPreview? PrepareCatalogCopy(int targetGradeLevel)
|
||||
{
|
||||
if (CatalogSubject is null)
|
||||
{
|
||||
CatalogValidation = "Bitte zuerst ein Fach auswählen.";
|
||||
return null;
|
||||
}
|
||||
if (Domains.Count == 0)
|
||||
{
|
||||
CatalogValidation = "Der ausgewählte Katalog enthält keine Bereiche.";
|
||||
return null;
|
||||
}
|
||||
if (targetGradeLevel is < 1 or > 13 || targetGradeLevel == CatalogGradeLevel)
|
||||
{
|
||||
CatalogValidation = "Bitte eine andere Zielklassenstufe zwischen 1 und 13 auswählen.";
|
||||
return null;
|
||||
}
|
||||
|
||||
CatalogValidation = "";
|
||||
return _catalogImport.Analyze(SerializeCatalog(targetGradeLevel), CatalogSubject.Id,
|
||||
CatalogSubject.Name, targetGradeLevel);
|
||||
}
|
||||
|
||||
public void SetCatalogCopyStatus(int targetGradeLevel) =>
|
||||
CatalogValidation = $"Kompetenzkatalog wurde in Klassenstufe {targetGradeLevel} kopiert.";
|
||||
|
||||
private string SerializeCatalog(int gradeLevel)
|
||||
{
|
||||
var dto = new CatalogDto
|
||||
{
|
||||
Subject = CatalogSubject?.Name ?? "",
|
||||
GradeLevel = gradeLevel,
|
||||
Domains = Domains.Select(d => new DomainDto
|
||||
{
|
||||
Name = d.Name,
|
||||
Code = d.Code,
|
||||
Competencies = d.Items.Select(i => new CompetencyDto
|
||||
{
|
||||
Code = i.Code,
|
||||
Description = i.Description,
|
||||
}).ToList(),
|
||||
}).ToList(),
|
||||
};
|
||||
return JsonSerializer.Serialize(dto, new JsonSerializerOptions { WriteIndented = true });
|
||||
}
|
||||
}
|
||||
|
||||
// ── JSON DTOs ─────────────────────────────────────────────────────────────────
|
||||
|
||||
internal class CatalogDto
|
||||
{
|
||||
[JsonPropertyName("subject")] public string? Subject { get; set; }
|
||||
[JsonPropertyName("gradeLevel")] public int GradeLevel { get; set; }
|
||||
[JsonPropertyName("domains")] public List<DomainDto>? Domains { get; set; }
|
||||
}
|
||||
|
||||
internal class DomainDto
|
||||
{
|
||||
[JsonPropertyName("name")] public string? Name { get; set; }
|
||||
[JsonPropertyName("code")] public string? Code { get; set; }
|
||||
[JsonPropertyName("competencies")] public List<CompetencyDto>? Competencies { get; set; }
|
||||
}
|
||||
|
||||
internal class CompetencyDto
|
||||
{
|
||||
[JsonPropertyName("code")] public string? Code { get; set; }
|
||||
[JsonPropertyName("description")] public string? Description { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user