Klausuren-Hauptseite: gruppenübergreifende Liste mit Prioritäts-Score statt Datum
Sidebar-Klausuren war bisher ein Placeholder. Neue Seite zeigt alle Klausuren des Schuljahres sortiert nach abgeleiteter Dringlichkeit (Korrekturfortschritt statt manuellem Status), mit Detailbereich, Parallelkurs-Umschalter und Notenspiegel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Core.Services;
|
||||
using LehrerApp.Desktop.ViewModels.Groups;
|
||||
using Xunit;
|
||||
|
||||
namespace LehrerApp.Desktop.Tests;
|
||||
|
||||
/// Deckt 1.6.3 (Bulk-Aktion "Rest als abwesend markieren") ab — löst das Nutzer-Feedback, dass
|
||||
/// Schüler, die nie nachschreiben, sonst dauerhaft eine leere, unberührte Zeile hinterlassen.
|
||||
public sealed class ExamGradingDialogViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void MarkRemainingAbsent_MarktNurUnberührteZeilenAbwesend()
|
||||
{
|
||||
var group = new LearningGroup { Name = "Q1 Chemie" };
|
||||
var studentWithPoints = new Student { FirstName = "Anna", LastName = "Bauer" };
|
||||
var untouchedStudent1 = new Student { FirstName = "Ben", LastName = "Cordes" };
|
||||
var untouchedStudent2 = new Student { FirstName = "Cara", LastName = "Diehl" };
|
||||
var students = new FakeStudents([studentWithPoints, untouchedStudent1, untouchedStudent2]);
|
||||
var memberships = new FakeMemberships(
|
||||
[
|
||||
new GroupMembership { GroupId = group.Id, StudentId = studentWithPoints.Id },
|
||||
new GroupMembership { GroupId = group.Id, StudentId = untouchedStudent1.Id },
|
||||
new GroupMembership { GroupId = group.Id, StudentId = untouchedStudent2.Id },
|
||||
]);
|
||||
var exam = new Exam
|
||||
{
|
||||
GroupId = group.Id,
|
||||
Title = "Klausur 1",
|
||||
Tasks = [new ExamTask { Nr = 1, MaxPoints = 10 }],
|
||||
GradingKey = GradingService.DefaultKey1To6(),
|
||||
};
|
||||
var results = new FakeResults();
|
||||
|
||||
var vm = new ExamGradingDialogViewModel(results, students, memberships,
|
||||
new GradingService(), exam, group.Id);
|
||||
|
||||
var rowWithPoints = vm.Rows.Single(r => r.StudentId == studentWithPoints.Id);
|
||||
rowWithPoints.Cells[0].TrySetValue(7);
|
||||
|
||||
vm.MarkRemainingAbsentCommand.Execute(null);
|
||||
|
||||
Assert.False(rowWithPoints.Absent);
|
||||
Assert.True(vm.Rows.Single(r => r.StudentId == untouchedStudent1.Id).Absent);
|
||||
Assert.True(vm.Rows.Single(r => r.StudentId == untouchedStudent2.Id).Absent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkRemainingAbsent_LässtBereitsAbwesendMarkierteUnverändert()
|
||||
{
|
||||
var group = new LearningGroup { Name = "Q1 Chemie" };
|
||||
var student = new Student { FirstName = "Ben", LastName = "Cordes" };
|
||||
var students = new FakeStudents([student]);
|
||||
var memberships = new FakeMemberships(
|
||||
[new GroupMembership { GroupId = group.Id, StudentId = student.Id }]);
|
||||
var exam = new Exam { GroupId = group.Id, Title = "Klausur 1", Tasks = [new ExamTask { Nr = 1, MaxPoints = 10 }], GradingKey = GradingService.DefaultKey1To6() };
|
||||
var vm = new ExamGradingDialogViewModel(new FakeResults(), students, memberships,
|
||||
new GradingService(), exam, group.Id);
|
||||
|
||||
vm.Rows[0].Absent = true;
|
||||
vm.MarkRemainingAbsentCommand.Execute(null);
|
||||
|
||||
Assert.True(vm.Rows[0].Absent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Core.Services;
|
||||
using LehrerApp.Desktop.ViewModels.Exams;
|
||||
using Xunit;
|
||||
|
||||
namespace LehrerApp.Desktop.Tests;
|
||||
|
||||
/// Deckt 1.6 (Klausuren-Hauptseite) ab: Priorisierung end-to-end über die ViewModel-Schicht,
|
||||
/// Parallelkurs-Umschalter (1.6.4) und Genehmigung/Ankündigung (1.6.5).
|
||||
public sealed class ExamsOverviewViewModelTests
|
||||
{
|
||||
private static ExamsOverviewViewModel BuildVm(List<LearningGroup> groups, List<Exam> exams,
|
||||
FakeMemberships? memberships = null, FakeResults? results = null) =>
|
||||
new(new FakeExams(exams), results ?? new FakeResults(), new FakeGroups(groups),
|
||||
memberships ?? new FakeMemberships([]), new GradingService(), new SchoolYearService());
|
||||
|
||||
[Fact]
|
||||
public void Load_UnbearbeiteteKorrekturStehtVorGeplanterKlausur()
|
||||
{
|
||||
var group = new LearningGroup { Name = "Q1 Chemie", SchoolYear = new SchoolYearService().CurrentSchoolYear() };
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var overdue = new Exam { GroupId = group.Id, Title = "Alte Klausur", Status = ExamStatus.Conducted, Date = today.AddDays(-10) };
|
||||
var planned = new Exam { GroupId = group.Id, Title = "Neue Klausur", Status = ExamStatus.Planned, Date = today.AddDays(3) };
|
||||
|
||||
var vm = BuildVm([group], [overdue, planned]);
|
||||
vm.LoadCommand.Execute(null);
|
||||
|
||||
Assert.Equal("Alte Klausur", vm.Rows[0].Title);
|
||||
Assert.Equal("Neue Klausur", vm.Rows[1].Title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_ParallelkurseWerdenAlsGeschwisterErkannt()
|
||||
{
|
||||
var groupA = new LearningGroup { Name = "Chemie GK Q1", SchoolYear = new SchoolYearService().CurrentSchoolYear() };
|
||||
var groupB = new LearningGroup { Name = "Chemie GK Q1b", SchoolYear = groupA.SchoolYear };
|
||||
var original = new Exam { GroupId = groupA.Id, Title = "Redox", Status = ExamStatus.Planned, Date = DateOnly.FromDateTime(DateTime.Today).AddDays(5) };
|
||||
var duplicate = new Exam
|
||||
{
|
||||
GroupId = groupB.Id, Title = "Redox", Status = ExamStatus.Planned,
|
||||
Date = DateOnly.FromDateTime(DateTime.Today).AddDays(6), SharedExamGroupId = original.Id,
|
||||
};
|
||||
|
||||
var vm = BuildVm([groupA, groupB], [original, duplicate]);
|
||||
vm.LoadCommand.Execute(null);
|
||||
|
||||
vm.SelectedRow = vm.Rows.Single(r => r.Exam.Id == original.Id);
|
||||
Assert.True(vm.SelectedRow.HasSibling);
|
||||
Assert.Single(vm.Siblings);
|
||||
Assert.Equal(duplicate.Id, vm.Siblings[0].Exam.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleApproval_SetztUndLöschtDatum()
|
||||
{
|
||||
var group = new LearningGroup { Name = "Q1 Chemie", SchoolYear = new SchoolYearService().CurrentSchoolYear() };
|
||||
var exam = new Exam { GroupId = group.Id, Title = "Klausur", Status = ExamStatus.Planned, Date = DateOnly.FromDateTime(DateTime.Today).AddDays(2) };
|
||||
var vm = BuildVm([group], [exam]);
|
||||
vm.LoadCommand.Execute(null);
|
||||
vm.SelectedRow = vm.Rows[0];
|
||||
|
||||
vm.ToggleApprovalCommand.Execute(null);
|
||||
Assert.NotNull(exam.ApprovalGrantedAt);
|
||||
Assert.StartsWith("Genehmigt am", vm.ApprovalLabel);
|
||||
|
||||
vm.ToggleApprovalCommand.Execute(null);
|
||||
Assert.Null(exam.ApprovalGrantedAt);
|
||||
Assert.Equal("Genehmigung noch ausstehend", vm.ApprovalLabel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Selection_ZeigtKorrekturfortschrittOderNotenspiegelJeNachStatus()
|
||||
{
|
||||
var group = new LearningGroup { Name = "Q1 Chemie", SchoolYear = new SchoolYearService().CurrentSchoolYear() };
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var conducted = new Exam { GroupId = group.Id, Title = "Läuft", Status = ExamStatus.Conducted, Date = today.AddDays(-2) };
|
||||
var returned = new Exam { GroupId = group.Id, Title = "Fertig", Status = ExamStatus.Returned, Date = today.AddDays(-20) };
|
||||
var vm = BuildVm([group], [conducted, returned]);
|
||||
vm.LoadCommand.Execute(null);
|
||||
|
||||
vm.SelectedRow = vm.Rows.Single(r => r.Exam.Id == conducted.Id);
|
||||
Assert.True(vm.ShowCorrectionProgress);
|
||||
Assert.False(vm.ShowGradeSummary);
|
||||
|
||||
vm.SelectedRow = vm.Rows.Single(r => r.Exam.Id == returned.Id);
|
||||
Assert.False(vm.ShowCorrectionProgress);
|
||||
Assert.True(vm.ShowGradeSummary);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user