3.1.3: Aspekt-Typen Scale3/Binary/Points in Raster und Dialog unterstützt
Neue Klasse ParticipationRatingScale (Core) ist die einzige Quelle für Rohwerte je Aspekt-Typ. Scale3/Binary liegen bewusst direkt auf derselben -2..+2-Achse wie Scale5 (nur mit weniger Zwischenschritten), damit die bestehende Gewichtung/Mittelwertbildung zur Mitarbeitsnote unverändert kompatibel bleibt. Points ist grundverschieden (echter Zählwert 0..MaxPoints, neues Feld auf ParticipationAspect) und wird nur zur Aggregation linear auf dieselbe Achse normiert. Ohne diese Normierung hätte ein Punkte-Aspekt die Mitarbeitsnote verfälscht: drei Stellen summierten bisher den Rohwert direkt (ParticipationGradeDialog- ViewModel.Recompute, ParticipationWizardViewModels.WeightedRating und .ComputeSuggestion) - alle drei sind jetzt auf die Normierung umgestellt. Raster: Punkte-Aspekte zeigen ein NumericUpDown statt fester Stufen-Buttons. Schnelleingabe-Dialog: Zifferntasten/+/- sind jetzt typabhängig, Legende zeigt live die für den aktuellen Aspekt gültigen Tasten. Aspekt-Verwaltung um "Max. Punkte"-Feld ergänzt (nur bei Typ "Punkte" sichtbar). Bewusst nicht angefasst: die Trendlinien-Visualisierung im Mitarbeits-Assistenten bleibt fest auf die drei Standardaspekte zugeschnitten - eigener, größerer Umbau. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -157,4 +157,43 @@ public class ParticipationGradeAggregationTests
|
||||
.Where(g => g.Category == GradeCategory.Participation).ToList();
|
||||
Assert.Single(annaGrades);
|
||||
}
|
||||
|
||||
/// 3.1.3: ein Punkte-Aspekt darf nicht als roher Zahlenwert in die Mittelwertbildung
|
||||
/// einfließen (z.B. "4 Punkte" wäre auf der -2..+2-Achse ein Extremwert weit über +2) —
|
||||
/// er muss vorher auf dieselbe Achse normiert werden wie Scale5/Scale3/Binary.
|
||||
[Fact]
|
||||
public void Recompute_PunkteAspekt_WirdVorDerMittelungAufMinus2Bis2AchseNormiert()
|
||||
{
|
||||
var groupId = Guid.NewGuid();
|
||||
var anna = new Student { FirstName = "Anna", LastName = "Beispiel" };
|
||||
var students = new FakeStudents([anna]);
|
||||
var memberships = new FakeMemberships([]);
|
||||
|
||||
var aspects = new FakeAspects();
|
||||
aspects.Save(new ParticipationAspect
|
||||
{
|
||||
GroupId = groupId, Key = "raised_hand", Label = "Meldungen",
|
||||
ValueType = AspectValueType.Points, MaxPoints = 4,
|
||||
});
|
||||
|
||||
var session = new ParticipationSession { GroupId = groupId, Date = new DateOnly(2025, 9, 1) };
|
||||
var sessions = new FakeSessions([session]);
|
||||
|
||||
var entries = new FakeEntries();
|
||||
// 2 von 4 Punkten -> Mitte der Skala, normiert genau auf 0.
|
||||
entries.Add(new ParticipationEntry
|
||||
{
|
||||
SessionId = session.Id, StudentId = anna.Id,
|
||||
Ratings = [new() { Key = "raised_hand", Value = 2 }],
|
||||
});
|
||||
|
||||
var grades = new FakeGrades();
|
||||
var grading = new GradingService();
|
||||
var vm = new ParticipationGradeDialogViewModel(sessions, entries, aspects, students, memberships,
|
||||
grades, grading, groupId, "2025/26", GradingSystem.Grades1To6);
|
||||
|
||||
var row = Assert.Single(vm.Rows);
|
||||
Assert.Equal(0.0, double.Parse(row.AverageDisplay.Replace(',', '.'),
|
||||
System.Globalization.CultureInfo.InvariantCulture), precision: 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Desktop.ViewModels.Groups;
|
||||
using Xunit;
|
||||
|
||||
namespace LehrerApp.Desktop.Tests;
|
||||
|
||||
/// Tests für 3.1.3: RatingCell muss je AspectValueType unterschiedliche gültige Rohwerte und
|
||||
/// Schrittweiten für Hoch/Runter (Raster) durchsetzen, nicht mehr pauschal -2..+2 wie zuvor.
|
||||
public sealed class RatingCellTests
|
||||
{
|
||||
[Fact]
|
||||
public void CycleUp_Scale3_SpringtZwischenDenDreiStufen()
|
||||
{
|
||||
var cell = new RatingCell(Guid.NewGuid(), "k", null, AspectValueType.Scale3);
|
||||
|
||||
cell.CycleUpCommand.Execute(null);
|
||||
Assert.Equal(-2, cell.Value);
|
||||
|
||||
cell.CycleUpCommand.Execute(null);
|
||||
Assert.Equal(0, cell.Value);
|
||||
|
||||
cell.CycleUpCommand.Execute(null);
|
||||
Assert.Equal(2, cell.Value);
|
||||
|
||||
cell.CycleUpCommand.Execute(null); // bleibt am oberen Ende
|
||||
Assert.Equal(2, cell.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CycleUp_Binary_ToggeltNurZwischenDenBeidenExtremwerten()
|
||||
{
|
||||
var cell = new RatingCell(Guid.NewGuid(), "k", null, AspectValueType.Binary);
|
||||
|
||||
cell.CycleUpCommand.Execute(null);
|
||||
Assert.Equal(-2, cell.Value);
|
||||
|
||||
cell.CycleUpCommand.Execute(null);
|
||||
Assert.Equal(2, cell.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetValue_Points_KapptAufMaxPoints()
|
||||
{
|
||||
var cell = new RatingCell(Guid.NewGuid(), "k", null, AspectValueType.Points, maxPoints: 4);
|
||||
|
||||
cell.SetValue(99);
|
||||
|
||||
Assert.Equal(4, cell.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CycleUp_Points_ZaehltEinzelnHochBisMaxPoints()
|
||||
{
|
||||
var cell = new RatingCell(Guid.NewGuid(), "k", 3, AspectValueType.Points, maxPoints: 4);
|
||||
|
||||
cell.CycleUpCommand.Execute(null);
|
||||
Assert.Equal(4, cell.Value);
|
||||
|
||||
cell.CycleUpCommand.Execute(null); // bleibt am Maximum
|
||||
Assert.Equal(4, cell.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CycleDown_Points_ZaehltEinzelnRunterBisNull()
|
||||
{
|
||||
var cell = new RatingCell(Guid.NewGuid(), "k", 1, AspectValueType.Points, maxPoints: 4);
|
||||
|
||||
cell.CycleDownCommand.Execute(null);
|
||||
Assert.Equal(0, cell.Value);
|
||||
|
||||
cell.CycleDownCommand.Execute(null); // bleibt bei 0
|
||||
Assert.Equal(0, cell.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisplayLabel_Scale5_UnveraendertGegenueberBisherigemVerhalten()
|
||||
{
|
||||
var cell = new RatingCell(Guid.NewGuid(), "k", 2);
|
||||
Assert.Equal("++", cell.DisplayLabel);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user