feat: Anhänge je Stunde, Klausur-Sitzplan mischen, Gefährdungsbeurteilungs-Assistent
Lesson bekommt dieselbe Anhang-Infrastruktur wie Documentation (Material, Arbeitsblätter, Experimentunterlagen), samt Fix einer Sync-Lücke, die Anhang- Dateibytes bisher nur für Documentation statt generisch übertragen hat (IHasAttachments). Sitzplan-Tab bekommt einen "Plätze mischen"-Button für Klausursitzpläne. Neu: mehrschrittiger Gefährdungsbeurteilungs-Assistent mit optionalem KI-Entwurf (ai-backend/gbu.php) und PDF-Export, Format bewusst als JSON-Anhang statt eigener Datenbank-Entität. Details und Architekturentscheidungen in TODO.md (4.2, 7.1.5, 10.1.8). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -542,4 +542,69 @@ public sealed class SeatingPlanViewModelTests
|
||||
Assert.Equal("Sitzplan", session.Comment);
|
||||
Assert.Equal(session.Id, vm.SelectedSession?.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShuffleSeats_ErzeugtNeuenPlanMitVertauschtenPlaetzenUndBehaeltDenUrsprungsplan()
|
||||
{
|
||||
var groupId = Guid.NewGuid();
|
||||
var anna = new Student { FirstName = "Anna", LastName = "A" };
|
||||
var ben = new Student { FirstName = "Ben", LastName = "B" };
|
||||
var cara = new Student { FirstName = "Cara", LastName = "C" };
|
||||
var students = new FakeStudents([anna, ben, cara]);
|
||||
var memberships = new FakeMemberships([
|
||||
new GroupMembership { GroupId = groupId, StudentId = anna.Id },
|
||||
new GroupMembership { GroupId = groupId, StudentId = ben.Id },
|
||||
new GroupMembership { GroupId = groupId, StudentId = cara.Id },
|
||||
]);
|
||||
var plan = new SeatingPlan
|
||||
{
|
||||
GroupId = groupId, Name = "Klausur", Room = "B204", Rows = 1, Columns = 3,
|
||||
Assignments =
|
||||
[
|
||||
new SeatAssignment { Row = 0, Column = 0, StudentId = anna.Id },
|
||||
new SeatAssignment { Row = 0, Column = 1, StudentId = ben.Id },
|
||||
new SeatAssignment { Row = 0, Column = 2, StudentId = cara.Id },
|
||||
],
|
||||
};
|
||||
var plans = new FakeSeatingPlans([plan]);
|
||||
var vm = new SeatingPlanTabViewModel(plans, students, memberships,
|
||||
new FakeSessions([]), new FakeEntries(), new FakeAspects());
|
||||
vm.Initialize(groupId, isReadOnly: false);
|
||||
vm.IsEditMode = true;
|
||||
|
||||
vm.ShuffleSeatsCommand.Execute(null);
|
||||
|
||||
var all = plans.GetByGroup(groupId);
|
||||
Assert.Equal(2, all.Count);
|
||||
var shuffled = Assert.Single(all, p => p.Id != plan.Id);
|
||||
Assert.Equal("B204", shuffled.Room);
|
||||
Assert.Equal(1, shuffled.Rows);
|
||||
Assert.Equal(3, shuffled.Columns);
|
||||
// Dieselben drei Schüler auf denselben drei Koordinaten, nur die Zuordnung untereinander
|
||||
// wurde neu gewürfelt.
|
||||
Assert.Equal(new[] { anna.Id, ben.Id, cara.Id }.OrderBy(id => id),
|
||||
shuffled.Assignments.Select(a => a.StudentId).OrderBy(id => id));
|
||||
Assert.Equal(new[] { (0, 0), (0, 1), (0, 2) },
|
||||
shuffled.Assignments.Select(a => (a.Row, a.Column)).OrderBy(t => t.Item2));
|
||||
// Ursprungsplan bleibt unverändert erhalten.
|
||||
Assert.Equal(3, plans.GetById(plan.Id)!.Assignments.Count);
|
||||
Assert.Equal(vm.SelectedPlan?.Id, shuffled.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShuffleSeatsCommand_OhneBearbeitungsmodus_NichtAusfuehrbar()
|
||||
{
|
||||
var groupId = Guid.NewGuid();
|
||||
var plan = new SeatingPlan { GroupId = groupId, Name = "Standard", Rows = 1, Columns = 1 };
|
||||
var vm = new SeatingPlanTabViewModel(
|
||||
new FakeSeatingPlans([plan]), new FakeStudents([]), new FakeMemberships([]),
|
||||
new FakeSessions([]), new FakeEntries(), new FakeAspects());
|
||||
vm.Initialize(groupId, isReadOnly: false);
|
||||
|
||||
Assert.False(vm.ShuffleSeatsCommand.CanExecute(null));
|
||||
|
||||
vm.IsEditMode = true;
|
||||
|
||||
Assert.True(vm.ShuffleSeatsCommand.CanExecute(null));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user