Übersicht: Empfehlung nur aus AKTUELLER Leistung (Recency)

Denkfehler behoben — eine Empfehlung reagiert auf das Zuletzt-Gesehene, nicht auf
einen Allzeit-Rückblick. live.php cockpit liefert jetzt recentErfolg (Ø letzte 3
Läufe), earlyErfolg (frühere Läufe), daysSince, recentPlays. Triage rechnet darauf:
>14 Tage weg → Nachfassen; recent schwach → Fördern; recent+viel → Begleiten; recent
stark, kaum gefordert → Fordern; recent > früher → Bestätigen. Neue Spalte „Zuletzt",
Legende erklärt: Erfolg = letzte Läufe, Verlauf = nur Historie. 4b-Daten mit
kontrollierter Recency neu (jede Gruppe besetzt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 15:11:35 +02:00
parent 84386e93d0
commit 1f00d83232
3 changed files with 343 additions and 24 deletions
+37
View File
@@ -605,6 +605,39 @@ if ($method === 'GET') {
$benchByStudent[$sid]['n']++;
}
// 4b) RECENCY — Grundlage für die Interventions-Empfehlung: nur die AKTUELLE
// Leistung zählt (kein Allzeit-Rückblick). Aus student_results:
// recentErfolg = Ø der letzten 3 Läufe · daysSince = Tage seit letztem Lauf
// recentPlays = Läufe der letzten 14 Tage
$recentByStudent = [];
foreach ($db->fetchAll(
"SELECT student_id,
DATEDIFF(NOW(), MAX(completed_at)) AS days_since,
SUM(completed_at >= NOW() - INTERVAL 14 DAY) AS recent_plays
FROM student_results WHERE class_id = ? GROUP BY student_id",
[$classId]) as $r) {
$recentByStudent[(int)$r['student_id']] = [
'daysSince' => $r['days_since'] !== null ? (int)$r['days_since'] : null,
'recentPlays' => (int)$r['recent_plays'],
'recentErfolg' => null,
];
}
foreach ($db->fetchAll(
"SELECT student_id,
ROUND(AVG(CASE WHEN rn <= 3 THEN score END)) AS recent_erfolg,
ROUND(AVG(CASE WHEN rn > 3 THEN score END)) AS early_erfolg
FROM (
SELECT student_id, score,
ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY completed_at DESC, id DESC) rn
FROM student_results WHERE class_id = ?
) x GROUP BY student_id",
[$classId]) as $r) {
$sid = (int)$r['student_id'];
if (!isset($recentByStudent[$sid])) $recentByStudent[$sid] = ['daysSince'=>null,'recentPlays'=>0,'recentErfolg'=>null];
$recentByStudent[$sid]['recentErfolg'] = $r['recent_erfolg'] !== null ? (int)$r['recent_erfolg'] : null;
$recentByStudent[$sid]['earlyErfolg'] = $r['early_erfolg'] !== null ? (int)$r['early_erfolg'] : null;
}
// 5) needsHelp-Heuristik aus Live-State (pro aktivem Schüler:in)
$needsHelp = function($simId, $state) {
if (!is_array($state)) return false;
@@ -701,6 +734,10 @@ if ($method === 'GET') {
'lastSeen' => $lastSeen,
'lastSeenTs' => $lastSeen ? strtotime($lastSeen) : null,
'benchmarkPct' => $bench,
'recentErfolg' => $recentByStudent[$sid]['recentErfolg'] ?? null,
'earlyErfolg' => $recentByStudent[$sid]['earlyErfolg'] ?? null,
'daysSince' => $recentByStudent[$sid]['daysSince'] ?? null,
'recentPlays' => $recentByStudent[$sid]['recentPlays'] ?? 0,
'activeSim' => $active ? $active['module_id'] : null,
'activeSimName'=> $active && isset($moduleMap[$active['module_id']]) ? $moduleMap[$active['module_id']]['title'] : null,
'activeSimIcon'=> $active && isset($moduleMap[$active['module_id']]) ? $moduleMap[$active['module_id']]['icon'] : null,