Notenverwaltung (Kapitel 2) und Mitarbeits-Assistent

Notenübersicht der Gruppe (Matrix, Gesamt-Spalte, Sortierung, Halbjahresfilter),
Einzelnoten-Pflege samt Sammelerfassung, Gewichtungsschema mit Voreinstellung je
Gruppentyp, Zeugnisnoten-Berechnung mit Übersteuern/Festschreiben/Export und
Notenentwicklung im Schülerdetail.

Dazu Anwesenheits-/Hausaufgaben-Tracking je Mitarbeit-Sitzung, ein neuer
Mitarbeits-Assistent (Zeitleiste mit Abschnitten, automatischer Notenvorschlag,
Zusammenzug zur Halbjahresnote) und eine Dashboard-Kachel für offene
Entschuldigungen. Außerdem: verbliebene englische Begriffe in Auswahlfeldern
und Buttons auf Deutsch umgestellt.
This commit is contained in:
2026-08-12 19:19:22 +02:00
parent a1e722ace1
commit febcb0855a
34 changed files with 2503 additions and 39 deletions
+57
View File
@@ -64,6 +64,16 @@ public class GradingService
return CalculateGrade(percent, 100, key);
}
public string? ValidateGradingScheme(GradingScheme scheme)
{
if (scheme.ExamsPercent < 0 || scheme.ParticipationPercent < 0 || scheme.OtherPercent < 0)
return "Anteile dürfen nicht negativ sein.";
var sum = scheme.ExamsPercent + scheme.ParticipationPercent + scheme.OtherPercent;
return Math.Abs(sum - 100.0) > 0.01
? $"Die Anteile müssen zusammen 100 % ergeben (aktuell {sum.ToString("0.#")} %)."
: null;
}
public double WeightedAverage(List<(string Grade, double Weight)> grades)
{
var numeric = grades
@@ -73,4 +83,51 @@ public class GradingService
var total = numeric.Sum(g => g.Weight);
return total == 0 ? 0 : numeric.Sum(g => g.Value!.Value * g.Weight) / total;
}
/// Rundet einen rechnerischen Notenwert auf eine ganze Note/Punktzahl.
/// "Kaufmännisch" rundet bei genau 0,5 immer vom Nullpunkt weg (Standard).
/// "Pädagogisch" rundet bei genau 0,5 in Richtung der besseren Note
/// (Grades1To6: kleinere Zahl ist besser → abrunden; Points0To15: größere Zahl ist besser → aufrunden).
public string RoundToGrade(double value, GradingSystem system, RoundingRule rule)
{
int rounded;
var floor = Math.Floor(value);
var isExactHalf = Math.Abs(value - floor - 0.5) < 0.0001;
if (rule == RoundingRule.Pedagogical && isExactHalf)
{
var betterIsLower = system == GradingSystem.Grades1To6;
rounded = betterIsLower ? (int)floor : (int)floor + 1;
}
else
{
rounded = (int)Math.Round(value, MidpointRounding.AwayFromZero);
}
var (min, max) = system == GradingSystem.Grades1To6 ? (1, 6) : (0, 15);
return Math.Clamp(rounded, min, max).ToString();
}
/// Berechnet die Zeugnisnote (2.4.1) aus den drei Leistungsbereichen gemäß Gewichtungsschema.
/// Bereiche ohne Werte werden ausgelassen; die verbleibenden Prozentanteile werden neu normiert.
/// Liefert null, wenn in keinem Bereich Werte vorliegen.
public string? CalculateReportGrade(
List<(string Grade, double Weight)> examGrades,
List<(string Grade, double Weight)> participationGrades,
List<(string Grade, double Weight)> otherGrades,
GradingScheme scheme, GradingSystem system, RoundingRule rounding)
{
var buckets = new List<(double Avg, double Percent)>();
if (examGrades.Count > 0) buckets.Add((WeightedAverage(examGrades), scheme.ExamsPercent));
if (participationGrades.Count > 0) buckets.Add((WeightedAverage(participationGrades), scheme.ParticipationPercent));
if (otherGrades.Count > 0) buckets.Add((WeightedAverage(otherGrades), scheme.OtherPercent));
var totalPercent = buckets.Sum(b => b.Percent);
if (buckets.Count == 0 || totalPercent <= 0) return null;
var weighted = buckets.Sum(b => b.Avg * b.Percent) / totalPercent;
return RoundToGrade(weighted, system, rounding);
}
}
public enum RoundingRule { Commercial, Pedagogical }