feat: Planungsideen-Feld je Stunde + persistierter Materialerstellungs-Prompt
CI / build-and-test (push) Canceled after 0s
CI / build-and-test (push) Canceled after 0s
Lesson bekommt ein neues Freitextfeld PlanningIdeas fuer grobe Ideen vor der Feinplanung, sichtbar im LessonDialog und als Kontext an die KI-Planung (Backend + MCP) durchgereicht. LessonPhaseStep.MaterialPrompt speichert den beim Uebernehmen einer KI-Stunde erzeugten Materialerstellungs-Prompt dauerhaft, statt ihn nur einmalig im AiAssistDialog anzuzeigen - im Verlaufsplan-Editor ueber einen neuen Kopieren-Button je Phase erneut nutzbar, auch ueber MCP lesbar/schreibbar. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -188,6 +188,7 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
||||
StartTime = l.StartTime,
|
||||
Homework = l.Homework,
|
||||
Reflection = l.Reflection,
|
||||
PlanningIdeas = l.PlanningIdeas,
|
||||
Phases = ToAiPhases(l.Phases, pathNames),
|
||||
})
|
||||
.ToList();
|
||||
@@ -270,6 +271,9 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
||||
if (!string.Equals(existing.Reflection, proposed.Reflection, StringComparison.Ordinal))
|
||||
diffs.Add("Reflexion geändert");
|
||||
|
||||
if (!string.Equals(existing.PlanningIdeas, proposed.PlanningIdeas, StringComparison.Ordinal))
|
||||
diffs.Add("Planungsideen geändert");
|
||||
|
||||
var pathNames = altPaths.GetAll().ToDictionary(p => p.Id, p => p.Name);
|
||||
var existingPhases = ToAiPhases(existing.Phases, pathNames);
|
||||
if (!PhasesEqual(existingPhases, proposed.Phases))
|
||||
@@ -586,6 +590,7 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
||||
StartTime = ai.StartTime,
|
||||
Homework = ai.Homework,
|
||||
Reflection = ai.Reflection,
|
||||
PlanningIdeas = ai.PlanningIdeas,
|
||||
// Status bleibt bei einer Änderung erhalten — sonst würde eine bereits
|
||||
// durchgeführte Stunde durch eine KI-Anpassung stillschweigend auf "Geplant"
|
||||
// zurückgesetzt (die KI kennt/liefert diesen Status gar nicht).
|
||||
@@ -597,6 +602,10 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
||||
Activity = p.Activity,
|
||||
Material = p.Material,
|
||||
Shorthand = p.Shorthand,
|
||||
// Persistiert den Prompt (4.5.20/4.5.36), damit er nach dem Übernehmen weiterhin
|
||||
// im Stundeneditor kopierbar bleibt statt nur einmalig im Review-Dialog.
|
||||
MaterialPrompt = string.IsNullOrWhiteSpace(p.MaterialSuggestion)
|
||||
? null : BuildMaterialPrompt(unit, ai, p),
|
||||
AlternativePathId = p.AlternativePathName is { } name && pathIdsByName.TryGetValue(name, out var pathId)
|
||||
? pathId : null,
|
||||
}).ToList(),
|
||||
|
||||
@@ -24,14 +24,20 @@ public record TimeEntryDto(
|
||||
Guid Id, Guid? TaskId, string Category, Guid? GroupId, DateOnly Date,
|
||||
TimeOnly? StartTime, TimeOnly? EndTime, int DurationMinutes, string? Description);
|
||||
|
||||
public record LessonPhaseDto(Guid Id, string Name, int DurationMinutes, string Activity, string Material, string Shorthand);
|
||||
/// <summary><see cref="MaterialPrompt"/> ist der beim letzten KI-"Übernehmen" gespeicherte
|
||||
/// Materialerstellungs-Prompt (4.5.20/4.5.36) — nur gesetzt, wenn die KI für diese Phase einen
|
||||
/// Medienvorschlag gemacht hatte. Ein MCP-Client kann ihn direkt zur Materialerzeugung nutzen, ohne
|
||||
/// erst den Desktop-Dialog öffnen zu müssen.</summary>
|
||||
public record LessonPhaseDto(
|
||||
Guid Id, string Name, int DurationMinutes, string Activity, string Material, string Shorthand,
|
||||
string? MaterialPrompt);
|
||||
|
||||
public record LessonAttachmentDto(string StorageId, string FileName, long SizeBytes);
|
||||
|
||||
public record LessonDto(
|
||||
Guid Id, Guid UnitId, Guid GroupId, DateOnly Date, int? LessonNumber, string Topic,
|
||||
string? Homework, LessonStatus Status, List<string> Competencies, List<LessonPhaseDto> Phases,
|
||||
List<LessonAttachmentDto> Attachments);
|
||||
string? Homework, string? PlanningIdeas, LessonStatus Status, List<string> Competencies,
|
||||
List<LessonPhaseDto> Phases, List<LessonAttachmentDto> Attachments);
|
||||
|
||||
/// <summary>Ergebnis von "download_lesson_attachment": Inhalt Base64-kodiert, weil MCP-Tool-Antworten
|
||||
/// als JSON/Text übertragen werden. Bewusst kein Ressourcen-URI-Mechanismus (siehe Planungsdokument) -
|
||||
|
||||
@@ -188,11 +188,12 @@ public class LessonPlanTools(
|
||||
return new WriteResultDto(true, lesson.Id, "Einzelstunde gespeichert.");
|
||||
}
|
||||
|
||||
[Description("Ändert Metadaten einer bestehenden Einzelstunde (Thema, Hausaufgabe, Status, Beginn, Stundennummer) — der Verlaufsplan (Phasen) bleibt unverändert. Nur angegebene Felder werden geändert.")]
|
||||
[Description("Ändert Metadaten einer bestehenden Einzelstunde (Thema, Hausaufgabe, Planungsideen, Status, Beginn, Stundennummer) — der Verlaufsplan (Phasen) bleibt unverändert. Nur angegebene Felder werden geändert.")]
|
||||
public async Task<WriteResultDto> UpdateLesson(
|
||||
[Description("ID der Einzelstunde.")] Guid lessonId,
|
||||
[Description("Neues Thema. Unverändert lassen: weglassen.")] string? topic = null,
|
||||
[Description("Neue Hausaufgabe. Unverändert lassen: weglassen.")] string? homework = null,
|
||||
[Description("Neue Planungsideen (grober Entwurf vor der Feinplanung). Unverändert lassen: weglassen.")] string? planningIdeas = null,
|
||||
[Description("Neuer Status: Planned, Conducted, Draft, Ready oder Cancelled (ausgefallen, z.B. Exkursion/Feiertag - Alternative zu delete_lesson, wenn die Stunde als Ereignis dokumentiert bleiben soll). Unverändert lassen: weglassen.")] LessonStatus? status = null,
|
||||
[Description("Neuer Stundenbeginn, Format HH:mm. Unverändert lassen: weglassen.")] TimeOnly? startTime = null,
|
||||
[Description("Neue Stundennummer. Unverändert lassen: weglassen.")] int? lessonNumber = null,
|
||||
@@ -204,6 +205,7 @@ public class LessonPlanTools(
|
||||
var changes = new StringBuilder();
|
||||
if (topic is not null && topic != lesson.Topic) { changes.AppendLine($"Thema: „{lesson.Topic}“ → „{topic}“"); lesson.Topic = topic; }
|
||||
if (homework is not null && homework != lesson.Homework) { changes.AppendLine($"Hausaufgabe: „{lesson.Homework}“ → „{homework}“"); lesson.Homework = homework; }
|
||||
if (planningIdeas is not null && planningIdeas != lesson.PlanningIdeas) { changes.AppendLine($"Planungsideen: „{lesson.PlanningIdeas}“ → „{planningIdeas}“"); lesson.PlanningIdeas = planningIdeas; }
|
||||
if (status is not null && status != lesson.Status) { changes.AppendLine($"Status: {lesson.Status} → {status}"); lesson.Status = status.Value; }
|
||||
if (startTime is not null && startTime != lesson.StartTime) { changes.AppendLine($"Beginn: {lesson.StartTime:HH\\:mm} → {startTime:HH\\:mm}"); lesson.StartTime = startTime; }
|
||||
if (lessonNumber is not null && lessonNumber != lesson.LessonNumber) { changes.AppendLine($"Nr.: {lesson.LessonNumber} → {lessonNumber}"); lesson.LessonNumber = lessonNumber; }
|
||||
@@ -228,6 +230,7 @@ public class LessonPlanTools(
|
||||
[Description("Tätigkeit/Sozialform.")] string activity = "",
|
||||
[Description("Material.")] string material = "",
|
||||
[Description("Kurzsymbol, z.B. \"AB001->S\".")] string shorthand = "",
|
||||
[Description("Optionaler, vollständiger Prompt zur Materialerstellung für diese Phase (siehe get_lesson_plans, LessonPhaseDto.MaterialPrompt) - z.B. wenn eine externe KI-Sitzung ihn selbst formuliert hat und er zur Wiederverwendung gespeichert werden soll.")] string? materialPrompt = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var lesson = lessons.GetById(lessonId);
|
||||
@@ -240,7 +243,7 @@ public class LessonPlanTools(
|
||||
var phase = new LessonPhaseStep
|
||||
{
|
||||
Name = name, DurationMinutes = durationMinutes, Activity = activity,
|
||||
Material = material, Shorthand = shorthand,
|
||||
Material = material, Shorthand = shorthand, MaterialPrompt = materialPrompt,
|
||||
};
|
||||
lesson.Phases.Add(phase);
|
||||
lessons.Save(lesson);
|
||||
@@ -256,6 +259,7 @@ public class LessonPlanTools(
|
||||
[Description("Neue Tätigkeit. Unverändert lassen: weglassen.")] string? activity = null,
|
||||
[Description("Neues Material. Unverändert lassen: weglassen.")] string? material = null,
|
||||
[Description("Neues Kurzsymbol. Unverändert lassen: weglassen.")] string? shorthand = null,
|
||||
[Description("Neuer Prompt zur Materialerstellung (siehe get_lesson_plans, LessonPhaseDto.MaterialPrompt). Unverändert lassen: weglassen.")] string? materialPrompt = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var lesson = lessons.GetById(lessonId);
|
||||
@@ -269,6 +273,7 @@ public class LessonPlanTools(
|
||||
if (activity is not null && activity != phase.Activity) { changes.AppendLine($"Tätigkeit: „{phase.Activity}“ → „{activity}“"); phase.Activity = activity; }
|
||||
if (material is not null && material != phase.Material) { changes.AppendLine($"Material: „{phase.Material}“ → „{material}“"); phase.Material = material; }
|
||||
if (shorthand is not null && shorthand != phase.Shorthand) { changes.AppendLine($"Kürzel: „{phase.Shorthand}“ → „{shorthand}“"); phase.Shorthand = shorthand; }
|
||||
if (materialPrompt is not null && materialPrompt != phase.MaterialPrompt) { changes.AppendLine("Materialerstellungs-Prompt geändert."); phase.MaterialPrompt = materialPrompt; }
|
||||
|
||||
if (changes.Length == 0)
|
||||
return new WriteResultDto(true, phase.Id, "Keine Änderung nötig.");
|
||||
@@ -409,7 +414,7 @@ public class LessonPlanTools(
|
||||
}
|
||||
|
||||
private static LessonDto ToDto(Lesson l) => new(
|
||||
l.Id, l.UnitId, l.GroupId, l.Date, l.LessonNumber, l.Topic, l.Homework, l.Status, l.Competencies,
|
||||
l.Phases.Select(p => new LessonPhaseDto(p.Id, p.Name, p.DurationMinutes, p.Activity, p.Material, p.Shorthand)).ToList(),
|
||||
l.Id, l.UnitId, l.GroupId, l.Date, l.LessonNumber, l.Topic, l.Homework, l.PlanningIdeas, l.Status, l.Competencies,
|
||||
l.Phases.Select(p => new LessonPhaseDto(p.Id, p.Name, p.DurationMinutes, p.Activity, p.Material, p.Shorthand, p.MaterialPrompt)).ToList(),
|
||||
l.Attachments.Select(a => new LessonAttachmentDto(a.StorageId, a.FileName, a.SizeBytes)).ToList());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user