diff --git a/App/php/api/results.php b/App/php/api/results.php index 6b81987..88ca3d2 100644 --- a/App/php/api/results.php +++ b/App/php/api/results.php @@ -369,6 +369,16 @@ if ($view === 'fleiss') { default: Response::error('range muss day|week|weeks|month|year sein'); } + // Aktueller Bucket-Index (für LAUFENDE, noch nicht abgegebene Sessions). + $nowBucketIdx = -1; + switch ($range) { + case 'day': $nowBucketIdx = (int)date('G'); break; // Stunde 0–23 + case 'week': $nowBucketIdx = (int)date('N') - 1; break; // Mo=0 … So=6 + case 'month': $nowBucketIdx = (int)date('j') - 1; break; // Tag 1–31 + case 'year': $nowBucketIdx = (int)date('n') - 1; break; // Monat 1–12 + case 'weeks': $nowBucketIdx = isset($bmap) ? ($bmap[(int)date('oW')] ?? -1) : -1; break; + } + // Optional Modul-Filter $params = [$classId]; $modSql = ''; @@ -463,6 +473,39 @@ if ($view === 'fleiss') { $moduleAgg[$sim]['totalSec'] += $sec; } + // 5b) LAUFENDE Sessions (noch kein Assessment abgegeben) in den aktuellen + // Bucket einspielen — als „live" markiert, damit die Säule dort blinkt. + if ($nowBucketIdx >= 0) { + $liveRows = $db->fetchAll( + "SELECT ls.student_id, ls.module_id, + UNIX_TIMESTAMP(ls.started_at) AS started_ts, + UNIX_TIMESTAMP(ls.last_seen) AS last_seen_ts + FROM live_sessions ls + WHERE ls.class_id = ? AND ls.last_seen > NOW() - INTERVAL 90 SECOND", + [$classId] + ); + foreach ($liveRows as $lr) { + $sid = (int)$lr['student_id']; + if (!isset($byStudent[$sid])) continue; + $sim = $lr['module_id']; + if ($moduleId !== 'ALL' && $moduleId !== '' && $sim !== $moduleId) continue; + $sec = max(0, (int)$lr['last_seen_ts'] - (int)$lr['started_ts']); + $b = &$byStudent[$sid]['buckets'][$nowBucketIdx]; + $b['sum'] += $sec; + $byMod = (array)$b['byModule']; + $byMod[$sim] = ($byMod[$sim] ?? 0) + $sec; + $b['byModule'] = $byMod; + $b['live'] = true; // Client lässt diese Säule blinken + $b['liveModule'] = $sim; + unset($b); + $byStudent[$sid]['totalSec'] += $sec; + if (!isset($moduleAgg[$sim])) { + $info = $moduleMap[$sim] ?? null; + $moduleAgg[$sim] = ['id'=>$sim,'name'=>$info?$info['name']:$sim,'icon'=>$info?$info['icon']:'📘','color'=>$info?$info['color']:'#888','totalSec'=>0]; + } + } + } + // 6) Global-Max-Bucket-Sec für klassenweite Säulen-Skalierung foreach ($byStudent as $sid => &$st) { foreach ($st['buckets'] as $b) { diff --git a/App/teacher.html b/App/teacher.html index 2c2cd4e..c27f948 100644 --- a/App/teacher.html +++ b/App/teacher.html @@ -3758,13 +3758,11 @@ async function loadResults() { host.innerHTML = '
Lade Ergebnisse…
'; // Drei parallele API-Calls: Fleiß (NEU oben) + Matrix + Activity (alt) - var [fleiss, matrix, activity, live] = await Promise.all([ + var [fleiss, matrix, activity] = await Promise.all([ api('results?class_id=' + currentClassId + '&view=fleiss&range=' + _resultsState.fleissRange + '&module_id=' + encodeURIComponent(_resultsState.fleissModule)), api('results?class_id=' + currentClassId + '&view=matrix'), api('results?class_id=' + currentClassId + '&view=activity&range=' + _resultsState.range + '&groupBy=' + (_resultsState.groupBy || 'module')), - api('live?class_id=' + currentClassId), ]); - _resultsState.live = Array.isArray(live) ? live : []; if (matrix.error) { host.innerHTML = ''+esc(matrix.error)+'
'; return; } if (activity.error) { host.innerHTML = ''+esc(activity.error)+'
'; return; } @@ -3779,41 +3777,16 @@ async function loadResults() { function renderResults() { var host = document.getElementById(_resultsHost) || document.getElementById('tab-results'); host.innerHTML = '' - + '' + '' + '' + '' + ''; - renderResultsLive(); renderFleiss(); renderMatrix(); renderActivity(); renderModuleCards(); } -// „Jetzt live"-Streifen ganz oben in Gesamt: laufende Spiele (aus live_sessions), -// als langsam blinkende Blöcke — sonst fehlen sie hier (Fleiß baut nur auf fertigen Läufen). -function renderResultsLive() { - var host = document.getElementById('res-live'); if (!host) return; - var live = (_resultsState.live || []).filter(function(ls){ return ls.last_seen_ms && (Date.now() - ls.last_seen_ms) < 90000; }); - if (!live.length) { host.innerHTML = ''; return; } - var html = '