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:
@@ -76,6 +76,9 @@ public class AiLesson
|
|||||||
public List<AiPhaseStep> Phases { get; set; } = [];
|
public List<AiPhaseStep> Phases { get; set; } = [];
|
||||||
public string? Homework { get; set; }
|
public string? Homework { get; set; }
|
||||||
public string? Reflection { get; set; }
|
public string? Reflection { get; set; }
|
||||||
|
/// Rohentwurf/erste Ideen der Lehrkraft vor der Feinplanung (siehe Lesson.PlanningIdeas) — reiner
|
||||||
|
/// Kontext für die KI wie Homework/Reflection, wird symmetrisch übernommen/zurückgegeben.
|
||||||
|
public string? PlanningIdeas { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AiPhaseStep
|
public class AiPhaseStep
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ public class Lesson : IHasAttachments
|
|||||||
/// Optionaler Stundenbeginn — solange kein Stundenplan (Kapitel 4.3) existiert, manuell
|
/// Optionaler Stundenbeginn — solange kein Stundenplan (Kapitel 4.3) existiert, manuell
|
||||||
/// gepflegt. Dient nur der abgeleiteten Uhrzeit-Anzeige je Phase in <see cref="Phases"/>.
|
/// gepflegt. Dient nur der abgeleiteten Uhrzeit-Anzeige je Phase in <see cref="Phases"/>.
|
||||||
public TimeOnly? StartTime { get; set; }
|
public TimeOnly? StartTime { get; set; }
|
||||||
|
/// Grobe Ideen/erster Entwurf, festgehalten lange bevor der Verlaufsplan (<see cref="Phases"/>)
|
||||||
|
/// feingeplant wird (Nutzer-Feedback: die eigentliche Ideenfindung liegt zeitlich oft weit vor
|
||||||
|
/// der Feinplanung) — bewusst ein eigenes Feld statt Zweckentfremdung von <see cref="Reflection"/>
|
||||||
|
/// (die ist nach der Stunde) oder <see cref="Unit.Notes"/> (die ist auf Einheitenebene, nicht je
|
||||||
|
/// Stunde). Wird auch der KI-Planungsunterstützung (4.5.9) als Kontext mitgegeben, damit erste
|
||||||
|
/// eigene Ideen bei der KI-gestützten Weiterplanung nicht erneut abgetippt werden müssen.
|
||||||
|
public string? PlanningIdeas { get; set; }
|
||||||
public List<LessonPhaseStep> Phases { get; set; } = [];
|
public List<LessonPhaseStep> Phases { get; set; } = [];
|
||||||
public TeachingTimelineState? TeachingTimeline { get; set; }
|
public TeachingTimelineState? TeachingTimeline { get; set; }
|
||||||
public string? Homework { get; set; }
|
public string? Homework { get; set; }
|
||||||
@@ -95,6 +102,14 @@ public class LessonPhaseStep
|
|||||||
public string Activity { get; set; } = "";
|
public string Activity { get; set; } = "";
|
||||||
public string Material { get; set; } = "";
|
public string Material { get; set; } = "";
|
||||||
public string Shorthand { get; set; } = "";
|
public string Shorthand { get; set; } = "";
|
||||||
|
/// Der über <see cref="AiPlanning.AiPlanningService.BuildMaterialPrompt"/> erzeugte, vollständige
|
||||||
|
/// Prompt für diese Phase (4.5.20), sofern die KI beim letzten "Übernehmen" einen Medienvorschlag
|
||||||
|
/// gemacht hatte — anders als der reine Vorschlagstext (<c>AiPhaseStep.MaterialSuggestion</c>,
|
||||||
|
/// nur transient während der Review) bewusst hier persistiert (Nutzer-Feedback: der Prompt soll
|
||||||
|
/// auch nach dem Übernehmen noch abrufbar/erneut kopierbar bleiben, statt nur einmalig im
|
||||||
|
/// Review-Dialog verfügbar zu sein). Rein informativ, kein Datenmodell-Bezug zu einem separaten
|
||||||
|
/// "Material"-Konzept, das es weiterhin nicht gibt — siehe TODO.md 4.5.26.
|
||||||
|
public string? MaterialPrompt { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// null = Hauptweg. Sonst Verweis auf einen benannten <see cref="AlternativeLessonPath"/> aus
|
/// null = Hauptweg. Sonst Verweis auf einen benannten <see cref="AlternativeLessonPath"/> aus
|
||||||
/// dem Katalog, über den Phasen alternativer Unterrichtsverläufe zusammengehören —
|
/// dem Katalog, über den Phasen alternativer Unterrichtsverläufe zusammengehören —
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
|||||||
StartTime = l.StartTime,
|
StartTime = l.StartTime,
|
||||||
Homework = l.Homework,
|
Homework = l.Homework,
|
||||||
Reflection = l.Reflection,
|
Reflection = l.Reflection,
|
||||||
|
PlanningIdeas = l.PlanningIdeas,
|
||||||
Phases = ToAiPhases(l.Phases, pathNames),
|
Phases = ToAiPhases(l.Phases, pathNames),
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -270,6 +271,9 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
|||||||
if (!string.Equals(existing.Reflection, proposed.Reflection, StringComparison.Ordinal))
|
if (!string.Equals(existing.Reflection, proposed.Reflection, StringComparison.Ordinal))
|
||||||
diffs.Add("Reflexion geändert");
|
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 pathNames = altPaths.GetAll().ToDictionary(p => p.Id, p => p.Name);
|
||||||
var existingPhases = ToAiPhases(existing.Phases, pathNames);
|
var existingPhases = ToAiPhases(existing.Phases, pathNames);
|
||||||
if (!PhasesEqual(existingPhases, proposed.Phases))
|
if (!PhasesEqual(existingPhases, proposed.Phases))
|
||||||
@@ -586,6 +590,7 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
|||||||
StartTime = ai.StartTime,
|
StartTime = ai.StartTime,
|
||||||
Homework = ai.Homework,
|
Homework = ai.Homework,
|
||||||
Reflection = ai.Reflection,
|
Reflection = ai.Reflection,
|
||||||
|
PlanningIdeas = ai.PlanningIdeas,
|
||||||
// Status bleibt bei einer Änderung erhalten — sonst würde eine bereits
|
// Status bleibt bei einer Änderung erhalten — sonst würde eine bereits
|
||||||
// durchgeführte Stunde durch eine KI-Anpassung stillschweigend auf "Geplant"
|
// durchgeführte Stunde durch eine KI-Anpassung stillschweigend auf "Geplant"
|
||||||
// zurückgesetzt (die KI kennt/liefert diesen Status gar nicht).
|
// zurückgesetzt (die KI kennt/liefert diesen Status gar nicht).
|
||||||
@@ -597,6 +602,10 @@ public class AiPlanningService(HttpClient http, ILessonRepository lessons,
|
|||||||
Activity = p.Activity,
|
Activity = p.Activity,
|
||||||
Material = p.Material,
|
Material = p.Material,
|
||||||
Shorthand = p.Shorthand,
|
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)
|
AlternativePathId = p.AlternativePathName is { } name && pathIdsByName.TryGetValue(name, out var pathId)
|
||||||
? pathId : null,
|
? pathId : null,
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
|
|||||||
@@ -24,14 +24,20 @@ public record TimeEntryDto(
|
|||||||
Guid Id, Guid? TaskId, string Category, Guid? GroupId, DateOnly Date,
|
Guid Id, Guid? TaskId, string Category, Guid? GroupId, DateOnly Date,
|
||||||
TimeOnly? StartTime, TimeOnly? EndTime, int DurationMinutes, string? Description);
|
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 LessonAttachmentDto(string StorageId, string FileName, long SizeBytes);
|
||||||
|
|
||||||
public record LessonDto(
|
public record LessonDto(
|
||||||
Guid Id, Guid UnitId, Guid GroupId, DateOnly Date, int? LessonNumber, string Topic,
|
Guid Id, Guid UnitId, Guid GroupId, DateOnly Date, int? LessonNumber, string Topic,
|
||||||
string? Homework, LessonStatus Status, List<string> Competencies, List<LessonPhaseDto> Phases,
|
string? Homework, string? PlanningIdeas, LessonStatus Status, List<string> Competencies,
|
||||||
List<LessonAttachmentDto> Attachments);
|
List<LessonPhaseDto> Phases, List<LessonAttachmentDto> Attachments);
|
||||||
|
|
||||||
/// <summary>Ergebnis von "download_lesson_attachment": Inhalt Base64-kodiert, weil MCP-Tool-Antworten
|
/// <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) -
|
/// 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.");
|
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(
|
public async Task<WriteResultDto> UpdateLesson(
|
||||||
[Description("ID der Einzelstunde.")] Guid lessonId,
|
[Description("ID der Einzelstunde.")] Guid lessonId,
|
||||||
[Description("Neues Thema. Unverändert lassen: weglassen.")] string? topic = null,
|
[Description("Neues Thema. Unverändert lassen: weglassen.")] string? topic = null,
|
||||||
[Description("Neue Hausaufgabe. Unverändert lassen: weglassen.")] string? homework = 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 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("Neuer Stundenbeginn, Format HH:mm. Unverändert lassen: weglassen.")] TimeOnly? startTime = null,
|
||||||
[Description("Neue Stundennummer. Unverändert lassen: weglassen.")] int? lessonNumber = null,
|
[Description("Neue Stundennummer. Unverändert lassen: weglassen.")] int? lessonNumber = null,
|
||||||
@@ -204,6 +205,7 @@ public class LessonPlanTools(
|
|||||||
var changes = new StringBuilder();
|
var changes = new StringBuilder();
|
||||||
if (topic is not null && topic != lesson.Topic) { changes.AppendLine($"Thema: „{lesson.Topic}“ → „{topic}“"); lesson.Topic = topic; }
|
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 (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 (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 (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; }
|
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("Tätigkeit/Sozialform.")] string activity = "",
|
||||||
[Description("Material.")] string material = "",
|
[Description("Material.")] string material = "",
|
||||||
[Description("Kurzsymbol, z.B. \"AB001->S\".")] string shorthand = "",
|
[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)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var lesson = lessons.GetById(lessonId);
|
var lesson = lessons.GetById(lessonId);
|
||||||
@@ -240,7 +243,7 @@ public class LessonPlanTools(
|
|||||||
var phase = new LessonPhaseStep
|
var phase = new LessonPhaseStep
|
||||||
{
|
{
|
||||||
Name = name, DurationMinutes = durationMinutes, Activity = activity,
|
Name = name, DurationMinutes = durationMinutes, Activity = activity,
|
||||||
Material = material, Shorthand = shorthand,
|
Material = material, Shorthand = shorthand, MaterialPrompt = materialPrompt,
|
||||||
};
|
};
|
||||||
lesson.Phases.Add(phase);
|
lesson.Phases.Add(phase);
|
||||||
lessons.Save(lesson);
|
lessons.Save(lesson);
|
||||||
@@ -256,6 +259,7 @@ public class LessonPlanTools(
|
|||||||
[Description("Neue Tätigkeit. Unverändert lassen: weglassen.")] string? activity = null,
|
[Description("Neue Tätigkeit. Unverändert lassen: weglassen.")] string? activity = null,
|
||||||
[Description("Neues Material. Unverändert lassen: weglassen.")] string? material = null,
|
[Description("Neues Material. Unverändert lassen: weglassen.")] string? material = null,
|
||||||
[Description("Neues Kurzsymbol. Unverändert lassen: weglassen.")] string? shorthand = 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)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var lesson = lessons.GetById(lessonId);
|
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 (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 (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 (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)
|
if (changes.Length == 0)
|
||||||
return new WriteResultDto(true, phase.Id, "Keine Änderung nötig.");
|
return new WriteResultDto(true, phase.Id, "Keine Änderung nötig.");
|
||||||
@@ -409,7 +414,7 @@ public class LessonPlanTools(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static LessonDto ToDto(Lesson l) => new(
|
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.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)).ToList(),
|
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());
|
l.Attachments.Select(a => new LessonAttachmentDto(a.StorageId, a.FileName, a.SizeBytes)).ToList());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -737,6 +737,7 @@ public partial class LessonDialogViewModel : ObservableObject
|
|||||||
[ObservableProperty] private int? _lessonNumber;
|
[ObservableProperty] private int? _lessonNumber;
|
||||||
[ObservableProperty] private string _topic = "";
|
[ObservableProperty] private string _topic = "";
|
||||||
[ObservableProperty] private string _startTimeText = "";
|
[ObservableProperty] private string _startTimeText = "";
|
||||||
|
[ObservableProperty] private string _planningIdeas = "";
|
||||||
[ObservableProperty] private string _homework = "";
|
[ObservableProperty] private string _homework = "";
|
||||||
[ObservableProperty] private bool _homeworkChecked;
|
[ObservableProperty] private bool _homeworkChecked;
|
||||||
[ObservableProperty] private bool _homeworkCheckDismissed;
|
[ObservableProperty] private bool _homeworkCheckDismissed;
|
||||||
@@ -823,6 +824,7 @@ public partial class LessonDialogViewModel : ObservableObject
|
|||||||
LessonNumber = editingLesson.LessonNumber;
|
LessonNumber = editingLesson.LessonNumber;
|
||||||
Topic = editingLesson.Topic;
|
Topic = editingLesson.Topic;
|
||||||
StartTimeText = editingLesson.StartTime?.ToString("HH:mm") ?? "";
|
StartTimeText = editingLesson.StartTime?.ToString("HH:mm") ?? "";
|
||||||
|
PlanningIdeas = editingLesson.PlanningIdeas ?? "";
|
||||||
Homework = editingLesson.Homework ?? "";
|
Homework = editingLesson.Homework ?? "";
|
||||||
HomeworkChecked = editingLesson.HomeworkChecked;
|
HomeworkChecked = editingLesson.HomeworkChecked;
|
||||||
HomeworkCheckDismissed = editingLesson.HomeworkCheckDismissed;
|
HomeworkCheckDismissed = editingLesson.HomeworkCheckDismissed;
|
||||||
@@ -888,6 +890,7 @@ public partial class LessonDialogViewModel : ObservableObject
|
|||||||
Activity = source?.Activity ?? "",
|
Activity = source?.Activity ?? "",
|
||||||
Material = source?.Material ?? "",
|
Material = source?.Material ?? "",
|
||||||
Shorthand = source?.Shorthand ?? "",
|
Shorthand = source?.Shorthand ?? "",
|
||||||
|
MaterialPrompt = source?.MaterialPrompt,
|
||||||
};
|
};
|
||||||
item.OnChanged = RecomputeTimes;
|
item.OnChanged = RecomputeTimes;
|
||||||
item.OnRemove = RemovePhase;
|
item.OnRemove = RemovePhase;
|
||||||
@@ -1075,6 +1078,7 @@ public partial class LessonDialogViewModel : ObservableObject
|
|||||||
Result.LessonNumber = LessonNumber;
|
Result.LessonNumber = LessonNumber;
|
||||||
Result.Topic = Topic.Trim();
|
Result.Topic = Topic.Trim();
|
||||||
Result.StartTime = startTime;
|
Result.StartTime = startTime;
|
||||||
|
Result.PlanningIdeas = string.IsNullOrWhiteSpace(PlanningIdeas) ? null : PlanningIdeas.Trim();
|
||||||
Result.Phases = Phases.Select(p => p.ToModel()).ToList();
|
Result.Phases = Phases.Select(p => p.ToModel()).ToList();
|
||||||
Result.Homework = string.IsNullOrWhiteSpace(Homework) ? null : Homework.Trim();
|
Result.Homework = string.IsNullOrWhiteSpace(Homework) ? null : Homework.Trim();
|
||||||
Result.HomeworkChecked = HomeworkChecked;
|
Result.HomeworkChecked = HomeworkChecked;
|
||||||
@@ -1103,6 +1107,13 @@ public partial class PhaseStepEditItem : ObservableObject
|
|||||||
[ObservableProperty] private string _material = "";
|
[ObservableProperty] private string _material = "";
|
||||||
[ObservableProperty] private string _shorthand = "";
|
[ObservableProperty] private string _shorthand = "";
|
||||||
[ObservableProperty] private string _computedTimeDisplay = "";
|
[ObservableProperty] private string _computedTimeDisplay = "";
|
||||||
|
/// Gespeicherter Materialerstellungs-Prompt (4.5.20/4.5.36) — nur gesetzt, wenn die KI beim
|
||||||
|
/// letzten "Übernehmen" einen Medienvorschlag für diese Phase gemacht hatte. Steuert die
|
||||||
|
/// Sichtbarkeit des Kopieren-Buttons im Verlaufsplan-Editor.
|
||||||
|
[ObservableProperty] private string? _materialPrompt;
|
||||||
|
|
||||||
|
public bool HasMaterialPrompt => !string.IsNullOrWhiteSpace(MaterialPrompt);
|
||||||
|
partial void OnMaterialPromptChanged(string? value) => OnPropertyChanged(nameof(HasMaterialPrompt));
|
||||||
|
|
||||||
/// Checkbox-Zustand im Editor: unchecked→checked öffnet den Zuweisen-Dialog
|
/// Checkbox-Zustand im Editor: unchecked→checked öffnet den Zuweisen-Dialog
|
||||||
/// (<see cref="OnAssignAlternativePath"/>); checked→unchecked entfernt die Zuordnung.
|
/// (<see cref="OnAssignAlternativePath"/>); checked→unchecked entfernt die Zuordnung.
|
||||||
@@ -1162,6 +1173,7 @@ public partial class PhaseStepEditItem : ObservableObject
|
|||||||
Activity = Activity.Trim(),
|
Activity = Activity.Trim(),
|
||||||
Material = Material.Trim(),
|
Material = Material.Trim(),
|
||||||
Shorthand = Shorthand.Trim(),
|
Shorthand = Shorthand.Trim(),
|
||||||
|
MaterialPrompt = MaterialPrompt,
|
||||||
AlternativePathId = AlternativePathId,
|
AlternativePathId = AlternativePathId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,13 @@
|
|||||||
IsVisible="{Binding TopicError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
IsVisible="{Binding TopicError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Spacing="4">
|
||||||
|
<TextBlock Text="Planungsideen (Rohentwurf)" FontSize="12" Opacity="0.7"
|
||||||
|
ToolTip.Tip="Erste, noch grobe Ideen — lange bevor der Verlaufsplan unten feingeplant wird. Fließt auch als Kontext in die KI-Unterstützung (Backend und MCP) ein."/>
|
||||||
|
<TextBox Text="{Binding PlanningIdeas}" AcceptsReturn="True" Height="64" TextWrapping="Wrap"
|
||||||
|
PlaceholderText="Grobe Ideen, mögliche Aufhänger, erste Materialgedanken ..."/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<Separator Margin="0,4"/>
|
<Separator Margin="0,4"/>
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto">
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
@@ -66,7 +73,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Tabellenkopf -->
|
<!-- Tabellenkopf -->
|
||||||
<Grid ColumnDefinitions="130,95,120,*,100,95,26,26,26" Margin="4,0,0,0">
|
<Grid ColumnDefinitions="130,95,120,*,100,95,26,26,26,26" Margin="4,0,0,0">
|
||||||
<TextBlock Grid.Column="0" Text="Phase" FontSize="11" FontWeight="SemiBold" Opacity="0.6"/>
|
<TextBlock Grid.Column="0" Text="Phase" FontSize="11" FontWeight="SemiBold" Opacity="0.6"/>
|
||||||
<TextBlock Grid.Column="1" Text="Pfad" FontSize="11" FontWeight="SemiBold" Opacity="0.6"
|
<TextBlock Grid.Column="1" Text="Pfad" FontSize="11" FontWeight="SemiBold" Opacity="0.6"
|
||||||
ToolTip.Tip="Ankreuzen, wenn diese Phase zu einem alternativen Ablauf gehört (z.B. Kurzversion bei Zeitnot) — öffnet die Zuweisung. Phasen mit demselben Ablauf werden im Verlaufsplan-Viewer gruppiert."/>
|
ToolTip.Tip="Ankreuzen, wenn diese Phase zu einem alternativen Ablauf gehört (z.B. Kurzversion bei Zeitnot) — öffnet die Zuweisung. Phasen mit demselben Ablauf werden im Verlaufsplan-Viewer gruppiert."/>
|
||||||
@@ -83,7 +90,7 @@
|
|||||||
<DataTemplate x:DataType="vm:PhaseStepEditItem">
|
<DataTemplate x:DataType="vm:PhaseStepEditItem">
|
||||||
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
|
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
|
||||||
BorderThickness="0,0,0,1" Padding="0,8">
|
BorderThickness="0,0,0,1" Padding="0,8">
|
||||||
<Grid ColumnDefinitions="130,95,120,*,100,95,26,26,26">
|
<Grid ColumnDefinitions="130,95,120,*,100,95,26,26,26,26">
|
||||||
<TextBox Grid.Column="0" Text="{Binding Name}" PlaceholderText="z.B. Erarbeitung"
|
<TextBox Grid.Column="0" Text="{Binding Name}" PlaceholderText="z.B. Erarbeitung"
|
||||||
VerticalAlignment="Top" Margin="0,0,6,0"/>
|
VerticalAlignment="Top" Margin="0,0,6,0"/>
|
||||||
<CheckBox Grid.Column="1" IsChecked="{Binding HasAlternativePath}"
|
<CheckBox Grid.Column="1" IsChecked="{Binding HasAlternativePath}"
|
||||||
@@ -110,11 +117,14 @@
|
|||||||
FilterMode="Contains" MinimumPrefixLength="0" VerticalAlignment="Top"
|
FilterMode="Contains" MinimumPrefixLength="0" VerticalAlignment="Top"
|
||||||
Margin="0,0,6,0" PlaceholderText="z.B. AB001->S, Plenum, LDE"
|
Margin="0,0,6,0" PlaceholderText="z.B. AB001->S, Plenum, LDE"
|
||||||
ToolTip.Tip="Freitext für den schnellen Überblick — mal ein Materialfluss-Pfeil (AB001->S), mal nur eine Sozialform (Plenum, LDE)."/>
|
ToolTip.Tip="Freitext für den schnellen Überblick — mal ein Materialfluss-Pfeil (AB001->S), mal nur eine Sozialform (Plenum, LDE)."/>
|
||||||
<Button Grid.Column="6" Content="↑" Command="{Binding MoveUpCommand}" Padding="4,2"
|
<Button Grid.Column="6" Content="📋" Padding="4,2" VerticalAlignment="Top" Margin="0,0,2,0"
|
||||||
|
IsVisible="{Binding HasMaterialPrompt}" Click="OnCopyMaterialPrompt"
|
||||||
|
ToolTip.Tip="Gespeicherten Prompt zur Materialerstellung erneut in die Zwischenablage kopieren."/>
|
||||||
|
<Button Grid.Column="7" Content="↑" Command="{Binding MoveUpCommand}" Padding="4,2"
|
||||||
VerticalAlignment="Top" ToolTip.Tip="Nach oben"/>
|
VerticalAlignment="Top" ToolTip.Tip="Nach oben"/>
|
||||||
<Button Grid.Column="7" Content="↓" Command="{Binding MoveDownCommand}" Padding="4,2"
|
<Button Grid.Column="8" Content="↓" Command="{Binding MoveDownCommand}" Padding="4,2"
|
||||||
VerticalAlignment="Top" Margin="2,0,0,0" ToolTip.Tip="Nach unten"/>
|
VerticalAlignment="Top" Margin="2,0,0,0" ToolTip.Tip="Nach unten"/>
|
||||||
<Button Grid.Column="8" Content="✕" Command="{Binding RemoveCommand}" Padding="4,2"
|
<Button Grid.Column="9" Content="✕" Command="{Binding RemoveCommand}" Padding="4,2"
|
||||||
VerticalAlignment="Top" Margin="2,0,0,0" ToolTip.Tip="Entfernen"/>
|
VerticalAlignment="Top" Margin="2,0,0,0" ToolTip.Tip="Entfernen"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input.Platform;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using LehrerApp.Core.Interfaces;
|
using LehrerApp.Core.Interfaces;
|
||||||
@@ -48,6 +49,20 @@ public partial class LessonDialog : Window
|
|||||||
Close(false);
|
Close(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Kopiert den beim letzten KI-"Übernehmen" gespeicherten Materialerstellungs-Prompt (4.5.36,
|
||||||
|
/// Nachtrag zu 4.5.20) erneut in die Zwischenablage — derselbe Mechanismus wie im AiAssistDialog,
|
||||||
|
/// hier aber für einen bereits gespeicherten, nicht mehr nur transienten Vorschlag.
|
||||||
|
private async void OnCopyMaterialPrompt(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not Button { DataContext: PhaseStepEditItem item } || string.IsNullOrWhiteSpace(item.MaterialPrompt))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var clipboard = TopLevel.GetTopLevel(this)?.Clipboard;
|
||||||
|
if (clipboard is null) return;
|
||||||
|
await clipboard.SetTextAsync(item.MaterialPrompt);
|
||||||
|
App.Services.GetRequiredService<NotificationService>().ShowSuccess("Prompt in die Zwischenablage kopiert.");
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnAddAttachment(object? sender, RoutedEventArgs e)
|
private async void OnAddAttachment(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is not LessonDialogViewModel vm) return;
|
if (DataContext is not LessonDialogViewModel vm) return;
|
||||||
|
|||||||
@@ -2916,6 +2916,40 @@ folgenden Punkte gehören direkt in `LehrerApp.Desktop`:
|
|||||||
- Kein zusätzliches Feature wie Windows-Autostart für LehrerApp.Desktop selbst oder ein
|
- Kein zusätzliches Feature wie Windows-Autostart für LehrerApp.Desktop selbst oder ein
|
||||||
Tray-Icon (auf Nachfrage bewusst nicht Teil dieser Änderung) — die Bridge löst das Problem
|
Tray-Icon (auf Nachfrage bewusst nicht Teil dieser Änderung) — die Bridge löst das Problem
|
||||||
rein auf Protokollebene, unabhängig davon, wann der Nutzer LehrerApp tatsächlich startet.
|
rein auf Protokollebene, unabhängig davon, wann der Nutzer LehrerApp tatsächlich startet.
|
||||||
|
- [x] **4.5.35** Freitextfeld "Planungsideen" je Stunde (2026-09-14, Nutzer-Feedback): erste, noch
|
||||||
|
grobe Ideen zu einer Stunde entstehen erfahrungsgemäß lange bevor der Verlaufsplan (4.2.2)
|
||||||
|
feingeplant wird — dafür gab es bisher kein Feld, nur `Reflection` (nach der Stunde) und
|
||||||
|
`Unit.Notes` (auf Einheiten- statt Stundenebene). Neues `Lesson.PlanningIdeas` (`string?`),
|
||||||
|
im [LessonDialog](LehrerApp.Desktop/Views/Groups/LessonDialog.axaml) direkt unter "Thema" als
|
||||||
|
mehrzeiliges Freitextfeld, noch vor dem Verlaufsplan — bewusste Platzierung, damit die Idee
|
||||||
|
zuerst und die Feinplanung erst danach kommt. Keine Migration nötig (LiteDB, neues Feld).
|
||||||
|
Auch als Kontext für die KI-Planungsunterstützung (4.5.9) durchgereicht, wie von der
|
||||||
|
Lehrkraft gewünscht ("auch nützlich für die KI-gestützte Weiterplanung"): `AiLesson`/
|
||||||
|
`AiUnitContext` (Core/AiPlanning) bekommen ein neues, zu `Homework`/`Reflection` symmetrisches
|
||||||
|
Feld `planningIdeas` (gelesen in `AiPlanningService.BuildContext`, übernommen in
|
||||||
|
`ApplyResponse`, im Planungsdiff berücksichtigt in `DescribeChanges`); `ai-backend/plan.php`
|
||||||
|
bekommt das Feld in Ein- und Ausgabeschema samt Hinweis an die KI, es als starken Kontext zu
|
||||||
|
nutzen und unverändert zurückzugeben, außer die Anweisung verlangt ausdrücklich eine
|
||||||
|
Überarbeitung. Für die MCP-gestützte Weiterplanung (4.5.28) zusätzlich in `LessonDto`
|
||||||
|
(Services/Mcp/Tools/Dto.cs) exponiert und über einen neuen optionalen `planningIdeas`-
|
||||||
|
Parameter von `update_lesson` (LessonPlanTools.cs) schreibbar — bewusst kein neues, eigenes
|
||||||
|
MCP-Tool nur für dieses eine Feld, passt in das bestehende kleinteilige Muster.
|
||||||
|
- [x] **4.5.36** Materialerstellungs-Prompt bleibt nach dem Übernehmen erhalten (2026-09-14,
|
||||||
|
Nachtrag zu 4.5.20, Nutzer-Feedback): der in 4.5.20 gebaute Prompt war bisher nur im
|
||||||
|
`AiAssistDialog` (Review vor dem Speichern) kopierbar — einmal übernommen, war er weg, obwohl
|
||||||
|
die Lehrkraft ihn oft erst später in einer externen KI-Sitzung tatsächlich einlöst. Neues
|
||||||
|
`LessonPhaseStep.MaterialPrompt` (`string?`, keine Migration nötig): `AiPlanningService.
|
||||||
|
ApplyResponse` befüllt es automatisch mit dem von `BuildMaterialPrompt` erzeugten Text, sobald
|
||||||
|
die Phase einen `MaterialSuggestion`-Vorschlag hatte — bewusst weiterhin kein separates
|
||||||
|
"Material"-Domänenmodell (siehe 4.5.26), nur ein zusätzliches Feld an der bestehenden Zeile.
|
||||||
|
Im [LessonDialog](LehrerApp.Desktop/Views/Groups/LessonDialog.axaml) erscheint dafür in der
|
||||||
|
Verlaufsplan-Tabelle ein neuer Button "📋" je Phasenzeile, nur sichtbar wenn ein Prompt
|
||||||
|
gespeichert ist (`PhaseStepEditItem.HasMaterialPrompt`) — derselbe Zwischenablage-Mechanismus
|
||||||
|
wie im `AiAssistDialog` (`LessonDialog.axaml.cs:OnCopyMaterialPrompt`). Für die MCP-gestützte
|
||||||
|
Weiterplanung zusätzlich in `LessonPhaseDto.MaterialPrompt` gelesen und über einen neuen
|
||||||
|
optionalen Parameter an `add_lesson_phase`/`update_lesson_phase` (LessonPlanTools.cs)
|
||||||
|
schreibbar, damit auch ein extern (z.B. in Claude Desktop) formulierter Prompt zur
|
||||||
|
Wiederverwendung gespeichert werden kann, nicht nur ein vom eigenen KI-Backend erzeugter.
|
||||||
|
|
||||||
**Wichtige Abweichung von der ursprünglichen Planung (5.2):** Vor der Umsetzung zeigte sich,
|
**Wichtige Abweichung von der ursprünglichen Planung (5.2):** Vor der Umsetzung zeigte sich,
|
||||||
dass 5.2 wie ursprünglich beschrieben eine zweite, parallele Fehlzeiten-Erfassung neben dem
|
dass 5.2 wie ursprünglich beschrieben eine zweite, parallele Fehlzeiten-Erfassung neben dem
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ Unterricht einsetzen kann, auch wenn sie nicht ihre eigene Idee war.
|
|||||||
"phases": [
|
"phases": [
|
||||||
{ "name": "<Phasenname>", "durationMinutes": <Zahl>, "activity": "<Tätigkeit>", "material": "<Material>", "shorthand": "<Kurzsymbol>" }
|
{ "name": "<Phasenname>", "durationMinutes": <Zahl>, "activity": "<Tätigkeit>", "material": "<Material>", "shorthand": "<Kurzsymbol>" }
|
||||||
],
|
],
|
||||||
"homework": "<Hausaufgabe oder null>", "reflection": "<Reflexion oder null>"
|
"homework": "<Hausaufgabe oder null>", "reflection": "<Reflexion oder null>",
|
||||||
|
"planningIdeas": "<grobe Ideen der Lehrkraft vor der Feinplanung oder null, nur Lesekontext>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -65,7 +65,8 @@ Die Eingabe hat exakt diese Struktur:
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"homework": "<Hausaufgabe oder null>",
|
"homework": "<Hausaufgabe oder null>",
|
||||||
"reflection": "<Reflexion oder null>"
|
"reflection": "<Reflexion oder null>",
|
||||||
|
"planningIdeas": "<Grobe Ideen/Rohentwurf der Lehrkraft vor der Feinplanung oder null>"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -111,6 +112,11 @@ Stunde zur Prüfung, alles andere würde ihr gar nicht angezeigt.
|
|||||||
"AB" Arbeitsblatt) — kein Fließtext.
|
"AB" Arbeitsblatt) — kein Fließtext.
|
||||||
- "durationMinutes" je Phase sollte in Summe zur für die Stunde realistischen Zeit passen
|
- "durationMinutes" je Phase sollte in Summe zur für die Stunde realistischen Zeit passen
|
||||||
(i.d.R. rund 45 Minuten je Einzelstunde, abzüglich organisatorischer Zeit).
|
(i.d.R. rund 45 Minuten je Einzelstunde, abzüglich organisatorischer Zeit).
|
||||||
|
- "planningIdeas" einer Stunde enthält oft schon vor der Feinplanung festgehaltene grobe Ideen der
|
||||||
|
Lehrkraft (Stichpunkte, erste Gedanken, mögliche Aufhänger) — nutze das, wo vorhanden, als starken
|
||||||
|
Hinweis für Thema/Verlaufsplan dieser Stunde, ohne jedes Stichwort wörtlich übernehmen zu müssen.
|
||||||
|
Gib das Feld in deiner Antwort unverändert zurück, außer die Anweisung der Lehrkraft verlangt
|
||||||
|
ausdrücklich eine Überarbeitung dieser Ideen selbst.
|
||||||
- "materialSuggestion" je Phase (nur in deiner Antwort, nicht in der Eingabe): ein kurzer Vorschlag
|
- "materialSuggestion" je Phase (nur in deiner Antwort, nicht in der Eingabe): ein kurzer Vorschlag
|
||||||
(1-2 Sätze), WAS ein zu dieser Phase passendes Medium/Material konkret zeigen oder enthalten
|
(1-2 Sätze), WAS ein zu dieser Phase passendes Medium/Material konkret zeigen oder enthalten
|
||||||
sollte (z.B. Aufbau eines Tafelbilds, Inhalt eines Arbeitsblatts) — nicht nur "ein Tafelbild
|
sollte (z.B. Aufbau eines Tafelbilds, Inhalt eines Arbeitsblatts) — nicht nur "ein Tafelbild
|
||||||
@@ -142,7 +148,8 @@ Antworte AUSSCHLIESSLICH mit gültigem JSON (kein Freitext davor/danach) in gena
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"homework": "<Hausaufgabe oder null>",
|
"homework": "<Hausaufgabe oder null>",
|
||||||
"reflection": "<Reflexion oder null>"
|
"reflection": "<Reflexion oder null>",
|
||||||
|
"planningIdeas": "<unverändert aus der Eingabe übernommen, siehe oben, oder null>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"summary": "<kurze menschenlesbare Zusammenfassung, was du getan hast>"
|
"summary": "<kurze menschenlesbare Zusammenfassung, was du getan hast>"
|
||||||
|
|||||||
@@ -129,6 +129,15 @@ statt der typisierten `Lessons`-Collection — nach jeder Modelländerung kennt
|
|||||||
`Lesson`-Klasse die alten Feldnamen nicht mehr, ein Zugriff darüber hätte sie beim Deserialisieren
|
`Lesson`-Klasse die alten Feldnamen nicht mehr, ein Zugriff darüber hätte sie beim Deserialisieren
|
||||||
bereits verworfen, bevor sie gelesen werden können.
|
bereits verworfen, bevor sie gelesen werden können.
|
||||||
|
|
||||||
|
`LessonPhaseStep.MaterialPrompt` (4.5.36, Nachtrag zu 4.5.20) ist kein von Hand gepflegtes Feld:
|
||||||
|
`AiPlanningService.ApplyResponse` befüllt es automatisch mit dem vollständigen, lokal erzeugten
|
||||||
|
Prompt (`BuildMaterialPrompt`), sobald die KI beim letzten "Übernehmen" für diese Phase einen
|
||||||
|
Medienvorschlag gemacht hatte — ursprünglich (4.5.20) bewusst NICHT persistiert, weil der Vorschlag
|
||||||
|
nur für die Review-Anzeige gedacht war; Nutzer-Feedback nach erstem Einsatz war, dass der Prompt
|
||||||
|
auch nach dem Übernehmen noch abrufbar bleiben soll. Kein neues "Material"-Domänenmodell dafür (das
|
||||||
|
gibt es weiterhin nicht, siehe 4.5.26) — der Prompt hängt einfach als zusätzliches Freitextfeld an
|
||||||
|
der bereits bestehenden `LessonPhaseStep`-Zeile.
|
||||||
|
|
||||||
## Eindeutige Schlüssel
|
## Eindeutige Schlüssel
|
||||||
|
|
||||||
Die Datenbank schützt folgende Kombinationen mit eindeutigen Indizes:
|
Die Datenbank schützt folgende Kombinationen mit eindeutigen Indizes:
|
||||||
|
|||||||
Reference in New Issue
Block a user