Neue Write-Tools create_time_entry, create_grade_entry und update_student_group_assignment schreiben nie direkt: jeder Aufruf zeigt zuerst einen menschenlesbaren Bestätigungsdialog (bestehender ConfirmDialog, über Dispatcher.UIThread aus dem Pipe-Session-Thread angezeigt) und schreibt erst nach Bestätigung, mit 2-Minuten-Timeout gegen eine hängende Session. Zusätzliches Read-Tool get_lesson_plans. create_note bewusst nicht umgesetzt (kollidiert mit dem bestehenden Dokumentations-Ausschluss aus Phase 1), create_lesson_plan/ update_lesson_plan wegen der Modellkomplexität von Lesson zurückgestellt (siehe TODO.md 4.5.26). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
71 lines
3.2 KiB
C#
71 lines
3.2 KiB
C#
using System.ComponentModel;
|
|
using LehrerApp.Core.Interfaces;
|
|
using LehrerApp.Core.Models;
|
|
|
|
namespace LehrerApp.Desktop.Services.Mcp.Tools;
|
|
|
|
/// <summary>MCP-Tools "get_grades" (Phase 1) und "create_grade_entry" (Phase 2), siehe
|
|
/// Planungsdokument.</summary>
|
|
public class GradeTools(IGradeRepository grades, IStudentRepository students, IMcpConfirmationService confirmation)
|
|
{
|
|
[Description("Listet Noten einer Lerngruppe, optional gefiltert auf einen einzelnen Schüler.")]
|
|
public List<GradeDto> GetGrades(
|
|
[Description("Lerngruppen-ID.")] Guid groupId,
|
|
[Description("Optionale Schüler-ID zum Filtern auf einen einzelnen Schüler.")] Guid? studentId = null)
|
|
{
|
|
var list = studentId is { } sid ? grades.GetByStudentAndGroup(sid, groupId) : grades.GetByGroup(groupId);
|
|
return list.Select(g => new GradeDto(
|
|
g.Id, g.StudentId, g.GroupId, g.Category, g.Value, g.Date, g.Weight, g.Note)).ToList();
|
|
}
|
|
|
|
[Description("Schlägt eine neue Note für einen Schüler vor. Muss der Nutzer erst in einem Dialog in LehrerApp bestätigen, bevor sie gespeichert wird.")]
|
|
public async Task<WriteResultDto> CreateGradeEntry(
|
|
[Description("Schüler-ID.")] Guid studentId,
|
|
[Description("Lerngruppen-ID.")] Guid groupId,
|
|
[Description("Kategorie: Oral, Homework, Participation, Project oder Other.")] GradeCategory category,
|
|
[Description("Notenwert als Text, z.B. \"2+\" oder \"gut\".")] string value,
|
|
[Description("Datum, Format YYYY-MM-DD.")] DateOnly date,
|
|
[Description("Gewichtung, Standard 1.0.")] double weight = 1.0,
|
|
[Description("Optionale Notiz.")] string? note = null,
|
|
CancellationToken ct = default)
|
|
{
|
|
var student = students.GetById(studentId);
|
|
if (student is null)
|
|
return new WriteResultDto(false, null, "Unbekannte Schüler-ID.");
|
|
|
|
var message =
|
|
$"Neue Note für {student.FullName}: {GradeCategoryDisplayName(category)} = {value}" +
|
|
(weight != 1.0 ? $" (Gewichtung {weight:0.##})" : "") +
|
|
$", am {date:dd.MM.yyyy}" +
|
|
(string.IsNullOrWhiteSpace(note) ? "" : $"\n„{note}“");
|
|
|
|
if (!await confirmation.ConfirmAsync("Note anlegen?", message, ct))
|
|
return new WriteResultDto(false, null, "Vom Nutzer abgelehnt oder nicht bestätigt.");
|
|
|
|
var grade = new Grade
|
|
{
|
|
StudentId = studentId,
|
|
GroupId = groupId,
|
|
Category = category,
|
|
Value = value,
|
|
Date = date,
|
|
Weight = weight,
|
|
Note = note,
|
|
};
|
|
grades.Save(grade);
|
|
return new WriteResultDto(true, grade.Id, "Note gespeichert.");
|
|
}
|
|
|
|
// Eigene, schlanke Beschriftung statt Wiederverwendung von ViewModels.Groups.GradeCategoryDisplay:
|
|
// Tool-Klassen unter Services/Mcp sollen nicht von ViewModel-Klassen abhängen.
|
|
private static string GradeCategoryDisplayName(GradeCategory c) => c switch
|
|
{
|
|
GradeCategory.Oral => "Mündlich",
|
|
GradeCategory.Homework => "Hausaufgaben",
|
|
GradeCategory.Participation => "Mitarbeit",
|
|
GradeCategory.Project => "Projekt",
|
|
GradeCategory.Other => "Sonstiges",
|
|
_ => c.ToString(),
|
|
};
|
|
}
|