- 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>
124 lines
4.5 KiB
C#
124 lines
4.5 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
|
|
{
|
|
// ── 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; }
|
|
}
|