CI / build-and-test (push) Canceled after 0s
- Mitarbeit: Tagesflagge (👑 Spitzentag / 😴 Schlaftag / ⚡ Schlechter Tag) je Schüler und Sitzung. Primär im Sitzplatz-Dialog (⇧1/2/3, ⇧X) mit Badge auf der Sitzplatz-Kachel; Fallback in der Schnelleingabe für Lerngruppen ohne Sitzplan. - Noten: neuer Dialog "Überblick" in der Notenübersicht zeigt Klausuren, Mitarbeit- und sonstige Noten eines Schülers samt berechneter Zeugnisnote. Zielnoten-Rechner (ReportGradeTargetCalculator) beantwortet "was brauche ich noch für Note X", ein Was-wäre-wenn-Rechner simuliert eine zusätzliche Klausurnote. Bewerter-/Schülermodus per Umschalter im selben Fenster — Bewertermodus zeigt zusätzlich Kursdurchschnitt je Klausur und erlaubt das Bearbeiten von Mitarbeit-/Sonstige-Noten. Klausurverlauf als Balken-Sparkline wie die bestehende Notenentwicklung. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
147 lines
6.6 KiB
C#
147 lines
6.6 KiB
C#
using LehrerApp.Core.Models;
|
|
using LehrerApp.Core.Services;
|
|
using LehrerApp.Desktop.ViewModels.Groups;
|
|
using Xunit;
|
|
|
|
namespace LehrerApp.Desktop.Tests;
|
|
|
|
/// Tests für den Schüler-Leistungsüberblick (Nutzer-Feedback): Klausuren/Mitarbeit/Sonstige
|
|
/// im Überblick, berechnete Zeugnisnote, Zielnoten- und Was-wäre-wenn-Rechner sowie der
|
|
/// Bewerter-/Schülermodus-Unterschied (Kursdurchschnitt, Bearbeitbarkeit).
|
|
public sealed class StudentPerformanceOverviewViewModelTests
|
|
{
|
|
private static readonly Guid GroupId = Guid.NewGuid();
|
|
private static readonly Guid StudentId = Guid.NewGuid();
|
|
private static readonly Guid OtherStudentId = Guid.NewGuid();
|
|
|
|
private static StudentPerformanceOverviewViewModel BuildViewModel(
|
|
FakeExams? exams = null, FakeResults? results = null, FakeGrades? grades = null,
|
|
FakeSchemes? schemes = null, GradingSystem system = GradingSystem.Grades1To6)
|
|
{
|
|
var scheme = new GradingScheme { ExamsPercent = 50, ParticipationPercent = 40, OtherPercent = 10 };
|
|
var schemesRepo = schemes ?? new FakeSchemes();
|
|
if (schemes is null) schemesRepo.SetForGroup(GroupId, scheme);
|
|
|
|
return new StudentPerformanceOverviewViewModel(
|
|
grades ?? new FakeGrades(), exams ?? new FakeExams([]), results ?? new FakeResults(),
|
|
new FakeMemberships([]), schemesRepo, new GradingService(),
|
|
GroupId, StudentId, GroupType.Class, system, "Beispiel, Anna", "Testkurs", "2025/26");
|
|
}
|
|
|
|
[Fact]
|
|
public void ExamRows_ZeigtEigeneNoteUndKursdurchschnitt()
|
|
{
|
|
var exam = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) };
|
|
var exams = new FakeExams([exam]);
|
|
var results = new FakeResults();
|
|
results.Add(new ExamResult { ExamId = exam.Id, StudentId = StudentId, Grade = "2" });
|
|
results.Add(new ExamResult { ExamId = exam.Id, StudentId = OtherStudentId, Grade = "4" });
|
|
|
|
var vm = BuildViewModel(exams, results);
|
|
|
|
var row = Assert.Single(vm.ExamRows);
|
|
Assert.Equal("2", row.OwnGradeDisplay);
|
|
Assert.Equal("3.0", row.ClassAverageDisplay);
|
|
}
|
|
|
|
[Fact]
|
|
public void ExamHistory_ZeigtEigenenVerlaufUndMarkiertNotenabfall()
|
|
{
|
|
var exam1 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) };
|
|
var exam2 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K2", Date = new DateOnly(2025, 9, 24) };
|
|
var exams = new FakeExams([exam1, exam2]);
|
|
var results = new FakeResults();
|
|
results.Add(new ExamResult { ExamId = exam1.Id, StudentId = StudentId, Grade = "2" });
|
|
results.Add(new ExamResult { ExamId = exam2.Id, StudentId = StudentId, Grade = "5" }); // Abfall + mangelhaft
|
|
|
|
var vm = BuildViewModel(exams, results);
|
|
|
|
Assert.Equal(2, vm.ExamHistory.Points.Count);
|
|
Assert.False(vm.ExamHistory.Points[0].IsWarning);
|
|
Assert.True(vm.ExamHistory.Points[1].IsWarning);
|
|
Assert.Contains("Abfall", vm.ExamHistory.Points[1].WarningText);
|
|
Assert.Contains("Versetzungsgefährdung", vm.ExamHistory.Points[1].WarningText);
|
|
Assert.True(vm.ExamHistory.Points[1].BarHeight < vm.ExamHistory.Points[0].BarHeight);
|
|
}
|
|
|
|
[Fact]
|
|
public void AktuelleZeugnisnote_KombiniertAlleDreiBereiche()
|
|
{
|
|
var exam = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) };
|
|
var exams = new FakeExams([exam]);
|
|
var results = new FakeResults();
|
|
results.Add(new ExamResult { ExamId = exam.Id, StudentId = StudentId, Grade = "2" });
|
|
var grades = new FakeGrades();
|
|
grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation,
|
|
Date = new DateOnly(2025, 9, 5), Value = "3" });
|
|
grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Other,
|
|
Date = new DateOnly(2025, 9, 5), Value = "1" });
|
|
|
|
var vm = BuildViewModel(exams, results, grades);
|
|
|
|
// (2*50 + 3*40 + 1*10) / 100 = 2.3 -> kaufmännisch 2
|
|
Assert.Equal("Note 2", vm.CurrentReportGradeDisplay);
|
|
}
|
|
|
|
[Fact]
|
|
public void Zielnote_BerechnetNoetigenKlausurschnitt()
|
|
{
|
|
var grades = new FakeGrades();
|
|
grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation,
|
|
Date = new DateOnly(2025, 9, 5), Value = "3" });
|
|
grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Other,
|
|
Date = new DateOnly(2025, 9, 5), Value = "2" });
|
|
var vm = BuildViewModel(grades: grades);
|
|
|
|
vm.TargetGradeText = "2";
|
|
vm.TargetBucketName = "Klausuren";
|
|
|
|
// Ziel 2,0 = (x*50 + 3*40 + 2*10)/100 => 200 = 50x+120+20 => x = 1,2
|
|
Assert.Contains("1.2", vm.TargetResultDisplay);
|
|
}
|
|
|
|
[Fact]
|
|
public void WasWaereWenn_ZeigtZeugnisnoteMitZusaetzlicherKlausurAn()
|
|
{
|
|
var exam = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) };
|
|
var exams = new FakeExams([exam]);
|
|
var results = new FakeResults();
|
|
results.Add(new ExamResult { ExamId = exam.Id, StudentId = StudentId, Grade = "2" });
|
|
var grades = new FakeGrades();
|
|
grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation,
|
|
Date = new DateOnly(2025, 9, 5), Value = "2" });
|
|
|
|
var vm = BuildViewModel(exams, results, grades);
|
|
|
|
vm.WhatIfExamGradeText = "6";
|
|
|
|
// Klausuren neu: (2+6)/2=4 (50%), Mitarbeit 2 (40%, Sonstige fehlt) -> (4*50+2*40)/90 = 3,11 -> kaufm. 3
|
|
Assert.Contains("3", vm.WhatIfResultDisplay);
|
|
}
|
|
|
|
[Fact]
|
|
public void SaveCommand_AufMitarbeitszeile_SpeichertUndAktualisiertDurchschnitt()
|
|
{
|
|
var grades = new FakeGrades();
|
|
var grade = new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation,
|
|
Date = new DateOnly(2025, 9, 5), Value = "3" };
|
|
grades.Add(grade);
|
|
var vm = BuildViewModel(grades: grades);
|
|
|
|
var row = Assert.Single(vm.ParticipationRows);
|
|
row.Value = "1";
|
|
row.SaveCommand.Execute(null);
|
|
|
|
Assert.Equal("1.0", vm.ParticipationAverageDisplay);
|
|
Assert.Equal("1", grades.GetByStudentAndGroup(StudentId, GroupId).Single().Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void OhneWerteImZeitraum_ZeigtHinweisStattZeugnisnote()
|
|
{
|
|
var vm = BuildViewModel();
|
|
|
|
Assert.Contains("Noch nicht berechenbar", vm.CurrentReportGradeDisplay);
|
|
}
|
|
}
|