Files
LehrerApp/LehrerApp.Core/Models/Participation.cs
T

163 lines
6.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace LehrerApp.Core.Models;
public class ParticipationSession
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid GroupId { get; set; }
public DateOnly Date { get; set; } = DateOnly.FromDateTime(DateTime.Today);
public Guid? LessonId { get; set; }
public string? Comment { get; set; }
public List<string> CompetencyCodes { get; set; } = [];
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}
public class ParticipationEntry
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid SessionId { get; set; }
public Guid StudentId { get; set; }
public List<AspectRating> Ratings { get; set; } = [];
public List<CompetencyRating> CompetencyRatings { get; set; } = [];
public string? Note { get; set; }
public HomeworkStatus? Homework { get; set; }
// Bleibt für bereits gespeicherte Daten erhalten. Neue Schreibvorgänge setzen beide Felder.
public bool HomeworkMissing { get; set; }
public AttendanceStatus? Attendance { get; set; }
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}
/// <summary>
/// Hausaufgabenstatus einer Sitzung; null bedeutet, dass für diesen Termin keine
/// Hausaufgabe erfasst wurde. MissingOpen kann später in MissingOverdue oder SubmittedLate
/// überführt werden.
/// </summary>
public enum HomeworkStatus
{
Completed,
MissingOpen,
MissingOverdue,
SubmittedLate,
// Hinten angefügt, damit bestehende LiteDB-Werte numerisch stabil bleiben.
PartiallyCompleted,
PartialSubmittedLate,
PartialMissingOverdue,
}
/// <summary>
/// Anwesenheitsstatus einer Sitzung; null bedeutet, dass die Anwesenheit für diesen Termin
/// noch nicht kontrolliert wurde. <see cref="Present"/> bestätigt die Anwesenheit ausdrücklich.
/// <see cref="ExcusePending"/> ist ein bewusster Zwischenzustand, da die Entschuldigung meist
/// erst später eintrifft. <see cref="Truant"/> bezeichnet gesichertes Schwänzen;
/// <see cref="OtherSchoolEvent"/> eine schulisch veranlasste Abwesenheit.
/// </summary>
public enum AttendanceStatus
{
ExcusePending,
Excused,
Unexcused,
Truant,
OtherSchoolEvent,
// Hinten angefügt, damit die numerischen Werte bereits gespeicherter LiteDB-Einträge stabil bleiben.
Present,
Late,
SignificantlyLate,
LeftDuringClass,
LearningIsland,
Suspended,
}
/// <summary>
/// Ein Bewertungsabschnitt einer Lerngruppe (z.B. alle 47 Wochen), an dessen Ende eine
/// Abschnittsnote Mitarbeit vergeben wird. Nur abgeschlossene Abschnitte werden gespeichert;
/// der aktuell laufende Zeitraum ergibt sich aus dem Ende des letzten Abschnitts bis heute.
/// </summary>
public class ParticipationSection
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid GroupId { get; set; }
public string Label { get; set; } = "";
public DateOnly StartDate { get; set; }
public DateOnly EndDate { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class CompetencyRating
{
public string Code { get; set; } = "";
public int Value { get; set; }
}
public class AspectRating
{
public string Key { get; set; } = "";
public int Value { get; set; }
}
public class ParticipationAspect
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid? GroupId { get; set; }
public string Key { get; set; } = "";
public string Label { get; set; } = "";
public AspectValueType ValueType { get; set; } = AspectValueType.Scale5;
// Nur bei ValueType.Points relevant: die Rohbewertung ist dann ein echter Punktwert
// 0..MaxPoints (nicht auf der -2..+2-Achse), siehe ParticipationRatingScale.
public int MaxPoints { get; set; } = 5;
public double Weight { get; set; } = 1.0;
public bool IsActive { get; set; } = true;
public int SortOrder { get; set; }
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}
public enum AspectValueType { Scale5, Scale3, Binary, Points }
public static class DefaultParticipationAspects
{
public static readonly IReadOnlyList<ParticipationAspect> All =
[
new() { Key = "quality", Label = "Qualität", ValueType = AspectValueType.Scale5, SortOrder = 0 },
new() { Key = "quantity", Label = "Quantität", ValueType = AspectValueType.Scale5, SortOrder = 1 },
new() { Key = "workphase", Label = "Arbeitsphase", ValueType = AspectValueType.Scale5, SortOrder = 2 },
];
}
/// <summary>
/// Definiert je <see cref="AspectValueType"/>, welche Rohwerte eine Bewertung annehmen kann und
/// wie sie sich auf die gemeinsame Qualitätsachse (-2..+2, wie <see cref="AspectValueType.Scale5"/>)
/// abbilden, auf der die Gewichtung/Mittelwertbildung zur Mitarbeitsnote (3.2) rechnet.
/// Scale3/Binary liegen bewusst direkt AUF dieser Achse (nur mit weniger Zwischenschritten)
/// statt einer eigenen Wertemenge zu bekommen — dadurch bleibt die bestehende Aggregation
/// unverändert kompatibel, ohne pro Bewertung erst umrechnen zu müssen. Nur "Points" hat eine
/// eigene, von der Lehrkraft festgelegte Obergrenze (<see cref="ParticipationAspect.MaxPoints"/>)
/// und wird für die Aggregation linear auf dieselbe Achse normiert (0 → -2, MaxPoints → +2).
/// </summary>
public static class ParticipationRatingScale
{
/// Feste Rohwert/Label-Paare für Typen mit einer kleinen, festen Stufenzahl — Grundlage für
/// Raster-Buttons und Schnelleingabe-Zifferntasten. Leer für Points (freie Zahleneingabe).
public static IReadOnlyList<(int Value, string Label)> Steps(AspectValueType type) => type switch
{
AspectValueType.Scale5 => [(-2, "−−"), (-1, ""), (0, ""), (1, "+"), (2, "++")],
AspectValueType.Scale3 => [(-2, ""), (0, ""), (2, "+")],
AspectValueType.Binary => [(-2, "Nein"), (2, "Ja")],
_ => [],
};
public static string DisplayLabel(AspectValueType type, int? value)
{
if (value is null) return "·";
if (type == AspectValueType.Points) return value.Value.ToString();
var match = Steps(type).FirstOrDefault(s => s.Value == value.Value);
return match.Label ?? "·";
}
/// Bildet einen Rohwert auf die gemeinsame -2..+2-Achse ab, für die Mittelwertbildung in 3.2.
public static double Normalize(AspectValueType type, int value, int maxPoints) => type switch
{
AspectValueType.Points when maxPoints > 0 => Math.Clamp(value, 0, maxPoints) / (double)maxPoints * 4.0 - 2.0,
AspectValueType.Points => 0, // keine sinnvolle Obergrenze konfiguriert -> neutral werten
_ => value,
};
}