KI-Feature 4.5.14: Feld-Diff für geänderte Stunden im AiAssistDialog

Statt Vorschläge nur pauschal als "Neu"/"Geändert" zu markieren, zeigt der
Dialog jetzt je geänderter Stunde, was sich konkret unterscheidet (Thema,
Datum, Beginn, Stundennummer, Hausaufgabe/Reflexion, Verlaufsplan) —
AiPlanningService.DescribeChanges vergleicht bestehende Lesson und
KI-Vorschlag feldweise und listet nur echte Unterschiede auf.

Granulare Übernahme einzelner Phasen und Schutz vor Überschreiben eigener
zwischenzeitlicher Änderungen als 4.5.19 zurückgestellt (größerer Umbau).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 16:50:35 +02:00
co-authored by Claude Sonnet 5
parent 2005b73a16
commit 8f2be89133
5 changed files with 167 additions and 21 deletions
@@ -143,6 +143,68 @@ public sealed class AiPlanningServiceTests
Assert.Contains(context.Lessons, l => l.Topic == "Neu aus Runde 1" && l.Id == null);
}
[Fact]
public void DescribeChanges_ErkenntNurTatsaechlichGeaenderteFelder()
{
var existing = new Lesson
{
Topic = "Alt", Date = new DateOnly(2026, 3, 10), StartTime = new TimeOnly(8, 0),
Homework = "Alte HA", Reflection = "Alte Reflexion",
};
var proposed = new AiLesson
{
Id = existing.Id, Topic = "Neu", Date = new DateOnly(2026, 3, 17), StartTime = new TimeOnly(8, 0),
Homework = "Alte HA", Reflection = "Neue Reflexion", Phases = [],
};
var service = Build(new FakeLessons(), new FakeGroups([]), new FakeSubjects([]),
new FakeCompetencyDomains(), new FakeAlternativeLessonPaths([]));
var diffs = service.DescribeChanges(existing, proposed);
Assert.Contains(diffs, d => d.Contains("Thema"));
Assert.Contains(diffs, d => d.Contains("Datum"));
Assert.Contains(diffs, d => d.Contains("Reflexion"));
Assert.DoesNotContain(diffs, d => d.Contains("Beginn"));
Assert.DoesNotContain(diffs, d => d.Contains("Hausaufgabe"));
}
[Fact]
public void DescribeChanges_IdentischeLesson_LiefertKeineUnterschiede()
{
var existing = new Lesson { Topic = "Gleich", Date = new DateOnly(2026, 3, 10) };
var proposed = new AiLesson { Id = existing.Id, Topic = "Gleich", Date = new DateOnly(2026, 3, 10), Phases = [] };
var service = Build(new FakeLessons(), new FakeGroups([]), new FakeSubjects([]),
new FakeCompetencyDomains(), new FakeAlternativeLessonPaths([]));
Assert.Empty(service.DescribeChanges(existing, proposed));
}
[Fact]
public void DescribeChanges_UnterschiedlicheAnzahlPhasen_WirdAlsVerlaufsplanAenderungErkannt()
{
var existing = new Lesson
{
Topic = "T",
Phases = [new LessonPhaseStep { Name = "Einstieg", DurationMinutes = 10 }],
};
var proposed = new AiLesson
{
Id = existing.Id, Topic = "T",
Phases =
[
new AiPhaseStep { Name = "Einstieg", DurationMinutes = 10 },
new AiPhaseStep { Name = "Erarbeitung", DurationMinutes = 20 },
],
};
var service = Build(new FakeLessons(), new FakeGroups([]), new FakeSubjects([]),
new FakeCompetencyDomains(), new FakeAlternativeLessonPaths([]));
Assert.Contains(service.DescribeChanges(existing, proposed), d => d.Contains("Verlaufsplan"));
}
[Fact]
public void ApplyResponse_BekannteId_WirdAlsUpdateBehandelt()
{