Files
LehrerApp/LehrerApp.Desktop/ViewModels/Groups/CompetencyOverviewViewModels.cs
T
2026-08-16 02:32:12 +02:00

175 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using System.Collections.ObjectModel;
namespace LehrerApp.Desktop.ViewModels.Groups;
public partial class CompetencyOverviewTabViewModel : ObservableObject
{
private readonly IUnitRepository _units;
private readonly IExamRepository _exams;
private readonly IExamResultRepository _results;
private readonly ICompetencyDomainRepository _catalog;
private readonly IStudentRepository _students;
private readonly CompetencyAnalysisService _analysis;
private LearningGroup? _group;
[ObservableProperty] private CompetencyStudentOption? _selectedStudent;
[ObservableProperty] private double _reviewThreshold = 60;
[ObservableProperty] private string _summary = "";
[ObservableProperty] private string _emptyMessage = "";
public ObservableCollection<CompetencyStudentOption> StudentOptions { get; } = [];
public ObservableCollection<CompetencyOverviewRow> Rows { get; } = [];
public bool HasRows => Rows.Count > 0;
public bool HasStudents => StudentOptions.Count > 0;
public string ThresholdLabel => $"Wiederholungsbedarf unter {ReviewThreshold:0} %";
public CompetencyOverviewTabViewModel(IUnitRepository units, IExamRepository exams,
IExamResultRepository results, ICompetencyDomainRepository catalog,
IStudentRepository students, CompetencyAnalysisService analysis)
{
_units = units;
_exams = exams;
_results = results;
_catalog = catalog;
_students = students;
_analysis = analysis;
}
public void Initialize(LearningGroup group)
{
_group = group;
Refresh();
}
[RelayCommand]
public void Refresh()
{
if (_group is null) return;
var selectedStudentId = SelectedStudent?.Id;
var domains = _group.SubjectId is Guid subjectId
? _catalog.GetBySubjectAndGrade(subjectId, _group.GradeLevel)
: [];
var units = _units.GetByGroup(_group.Id);
var exams = _exams.GetByGroup(_group.Id);
var results = exams.SelectMany(x => _results.GetByExam(x.Id)).ToList();
var analysis = _analysis.Analyze(domains, units, exams, results);
StudentOptions.Clear();
foreach (var student in _students.GetByGroup(_group.Id)
.OrderBy(x => x.LastName).ThenBy(x => x.FirstName))
StudentOptions.Add(new CompetencyStudentOption(student.Id, student.FullName));
SelectedStudent = StudentOptions.FirstOrDefault(x => x.Id == selectedStudentId)
?? StudentOptions.FirstOrDefault();
Rows.Clear();
foreach (var item in analysis)
Rows.Add(new CompetencyOverviewRow(item, SelectedStudent?.Id, ReviewThreshold));
var treated = analysis.Count(x => x.TaughtUnitCount > 0);
var assessed = analysis.Count(x => x.AssessedExamCount > 0);
var evaluated = analysis.Count(x => x.GroupScore is not null);
Summary = $"{treated} von {analysis.Count} behandelt · {assessed} geprüft · {evaluated} mit Ergebnisdaten";
EmptyMessage = analysis.Count == 0
? "Noch keine Kompetenzen im Katalog oder in Unterrichtseinheiten und Klausuren zugeordnet."
: "";
OnPropertyChanged(nameof(HasRows));
OnPropertyChanged(nameof(HasStudents));
}
partial void OnSelectedStudentChanged(CompetencyStudentOption? value)
{
foreach (var row in Rows) row.SetStudent(value?.Id);
}
partial void OnReviewThresholdChanged(double value)
{
OnPropertyChanged(nameof(ThresholdLabel));
foreach (var row in Rows) row.SetThreshold(value);
}
}
public sealed record CompetencyStudentOption(Guid Id, string DisplayName);
public partial class CompetencyOverviewRow : ObservableObject
{
private readonly CompetencyAnalysisItem _item;
private double _threshold;
[ObservableProperty] private string _studentResult = "";
[ObservableProperty] private string _recommendation = "Noch keine Bewertung";
[ObservableProperty] private string _recommendationColor = "#6B7280";
public string Domain => string.IsNullOrWhiteSpace(_item.DomainCode)
? _item.DomainName
: $"{_item.DomainName} ({_item.DomainCode})";
public string Code => string.IsNullOrWhiteSpace(_item.Code) ? "" : _item.Code;
public string Description => _item.Description;
public string InstructionCoverage => CoverageText(
_item.TaughtUnitCount, "behandelt", _item.PlannedUnitCount, "geplant");
public string ExamCoverage => CoverageText(
_item.AssessedExamCount, "geprüft", _item.PlannedExamCount, "geplant");
public string GroupResult => ScoreText(_item.GroupScore, includeParticipants: true);
public CompetencyOverviewRow(CompetencyAnalysisItem item, Guid? studentId, double threshold)
{
_item = item;
_threshold = threshold;
SetStudent(studentId);
UpdateRecommendation();
}
public void SetStudent(Guid? studentId)
{
StudentResult = studentId is Guid id && _item.StudentScores.TryGetValue(id, out var score)
? ScoreText(score, includeParticipants: false)
: "";
}
public void SetThreshold(double threshold)
{
_threshold = threshold;
UpdateRecommendation();
}
private void UpdateRecommendation()
{
if (_item.GroupScore is null)
{
Recommendation = "Noch keine Bewertung";
RecommendationColor = "#6B7280";
}
else if (_item.GroupScore.Percent < _threshold)
{
Recommendation = "Wiederholungsbedarf";
RecommendationColor = "#C62828";
}
else
{
Recommendation = "Stand solide";
RecommendationColor = "#2E7D32";
}
}
private static string CoverageText(int completed, string completedLabel, int planned, string plannedLabel)
{
if (completed == 0 && planned == 0) return "";
if (completed == 0) return $"{planned}× {plannedLabel}";
if (planned == 0) return $"{completed}× {completedLabel}";
return $"{completed}× {completedLabel} · {planned}× {plannedLabel}";
}
private static string ScoreText(CompetencyScore? score, bool includeParticipants)
{
if (score is null) return "";
var evidence = score.EvidenceCount == 1 ? "1 Aufgabenwert" : $"{score.EvidenceCount} Aufgabenwerte";
return includeParticipants
? $"{score.Percent:0.#} % · {score.ParticipantCount} Schüler · {evidence}"
: $"{score.Percent:0.#} % · {evidence}";
}
}