CI / build-and-test (push) Canceled after 0s
- Mitarbeit: Tagesflagge (👑 Spitzentag / 😴 Schlaftag / ⚡ Schlechter Tag) je Schüler und Sitzung. Primär im Sitzplatz-Dialog (⇧1/2/3, ⇧X) mit Badge auf der Sitzplatz-Kachel; Fallback in der Schnelleingabe für Lerngruppen ohne Sitzplan. - Noten: neuer Dialog "Überblick" in der Notenübersicht zeigt Klausuren, Mitarbeit- und sonstige Noten eines Schülers samt berechneter Zeugnisnote. Zielnoten-Rechner (ReportGradeTargetCalculator) beantwortet "was brauche ich noch für Note X", ein Was-wäre-wenn-Rechner simuliert eine zusätzliche Klausurnote. Bewerter-/Schülermodus per Umschalter im selben Fenster — Bewertermodus zeigt zusätzlich Kursdurchschnitt je Klausur und erlaubt das Bearbeiten von Mitarbeit-/Sonstige-Noten. Klausurverlauf als Balken-Sparkline wie die bestehende Notenentwicklung. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
49 lines
2.5 KiB
C#
49 lines
2.5 KiB
C#
using LehrerApp.Core.Models;
|
|
|
|
namespace LehrerApp.Core.Services;
|
|
|
|
/// Welcher der drei Leistungsbereiche (siehe GradingScheme) bei der Zielnoten-Frage der
|
|
/// gesuchte/unbekannte ist.
|
|
public enum GradeBucketKind { Exams, Participation, Other }
|
|
|
|
public readonly record struct TargetGradeResult(double RequiredAverage, bool IsAchievable);
|
|
|
|
/// <summary>
|
|
/// Beantwortet die Zielnoten-Frage "Was brauche ich noch, um Zeugnisnote X zu erreichen?"
|
|
/// (Nutzer-Feedback) — kehrt <see cref="GradingService.CalculateReportGrade"/> um: löst nach
|
|
/// dem Durchschnitt eines gewählten Bereichs auf, der zusammen mit den bekannten Durchschnitten
|
|
/// der übrigen Bereiche (gewichtet nach <see cref="GradingScheme"/>) genau die Zielnote ergibt.
|
|
/// Bereiche ohne bekannten Durchschnitt (z.B. noch keine Mitarbeitsnote im Halbjahr) fließen
|
|
/// nicht in die bekannte Seite der Gleichung ein — exakt dieselbe Normierung wie
|
|
/// <see cref="GradingService.CalculateReportGrade"/> bei fehlenden Bereichen.
|
|
/// </summary>
|
|
public static class ReportGradeTargetCalculator
|
|
{
|
|
public static TargetGradeResult? SolveRequiredAverage(double target, GradeBucketKind solveFor,
|
|
double? examsAverage, double? participationAverage, double? otherAverage,
|
|
GradingScheme scheme, GradingSystem system)
|
|
{
|
|
var solvePercent = solveFor switch
|
|
{
|
|
GradeBucketKind.Exams => scheme.ExamsPercent,
|
|
GradeBucketKind.Participation => scheme.ParticipationPercent,
|
|
_ => scheme.OtherPercent,
|
|
};
|
|
// Dieser Bereich fließt laut Gewichtungsschema gar nicht in die Zeugnisnote ein —
|
|
// keine Zielnoten-Frage für ihn beantwortbar.
|
|
if (solvePercent <= 0) return null;
|
|
|
|
var known = new List<(double Avg, double Percent)>();
|
|
if (solveFor != GradeBucketKind.Exams && examsAverage is { } e) known.Add((e, scheme.ExamsPercent));
|
|
if (solveFor != GradeBucketKind.Participation && participationAverage is { } p) known.Add((p, scheme.ParticipationPercent));
|
|
if (solveFor != GradeBucketKind.Other && otherAverage is { } o) known.Add((o, scheme.OtherPercent));
|
|
|
|
var totalPercent = known.Sum(k => k.Percent) + solvePercent;
|
|
var knownContribution = known.Sum(k => k.Avg * k.Percent);
|
|
var required = (target * totalPercent - knownContribution) / solvePercent;
|
|
|
|
var (min, max) = system == GradingSystem.Grades1To6 ? (1.0, 6.0) : (0.0, 15.0);
|
|
return new TargetGradeResult(required, required >= min && required <= max);
|
|
}
|
|
}
|