Tag-Ansicht: echte Spieldauer + Ampelfarbe nach Erfolg + Sim-Symbol
Bug: fertig gespielte Laeufe (Farmer-Endbericht = 1 Assessment) erschienen als kurzer "Abbruch"-Balken (Dauer 0). Fix: - live.php summary: nutzt duration_ms -> Balken decken die echte Spielzeit ab und fluchten am Start; kein Falsch-Abbruch mehr. Neues Feld `score` (0-100) je Session (Score/Sterne); laufende Farmer-Sessions bekommen hit_rate als Live-Score. - teacher.html Tag-Balken: einheitliche Hoehe (Hoehe raus), Farbe = Ampel nach Erfolg (gruen->rot), App-Farbe ersetzt durch Sim-Symbol; Klick = Details. Legende angepasst. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -141,6 +141,14 @@ if ($method === 'GET') {
|
||||
if (($pl['phase'] ?? '') === 'completed') return true;
|
||||
return null;
|
||||
};
|
||||
// Erfolgs-Score 0–100 (für die Ampelfarbe der Tages-Balken). Sterne → %,
|
||||
// sonst der Sim-Score (Farmer/Weltküche liefern `score`).
|
||||
$extractScore = function($row) {
|
||||
$r = $row['results'] ? json_decode($row['results'], true) : [];
|
||||
foreach ([$r['score'] ?? null, $r['results']['score'] ?? null] as $c) if (is_numeric($c)) return max(0, min(100, (int)round($c)));
|
||||
if (isset($r['stars']) && is_numeric($r['stars'])) return (int)round($r['stars'] / 5 * 100);
|
||||
return null;
|
||||
};
|
||||
|
||||
// Gruppieren: student_id × sim_id × lesson_key → eine ODER MEHRERE Sessions.
|
||||
// Wichtig: bei Lücke > SESSION_GAP_SEC zwischen aufeinanderfolgenden Submits
|
||||
@@ -197,6 +205,8 @@ if ($method === 'GET') {
|
||||
'startMs' => $ts * 1000,
|
||||
'endMs' => $ts * 1000,
|
||||
'stars' => null,
|
||||
'score' => null,
|
||||
'durationMs' => 0,
|
||||
'completed' => null,
|
||||
'rowCount' => 0,
|
||||
];
|
||||
@@ -205,6 +215,10 @@ if ($method === 'GET') {
|
||||
$currentSess['rowCount']++;
|
||||
$st = $extractStars($row);
|
||||
if ($st !== null && ($currentSess['stars'] === null || $st > $currentSess['stars'])) $currentSess['stars'] = $st;
|
||||
$scv = $extractScore($row);
|
||||
if ($scv !== null && ($currentSess['score'] === null || $scv > $currentSess['score'])) $currentSess['score'] = $scv;
|
||||
$dmv = (int)($row['duration_ms'] ?? 0);
|
||||
if ($dmv > $currentSess['durationMs']) $currentSess['durationMs'] = $dmv;
|
||||
$cp = $extractCompleted($row);
|
||||
if ($cp !== null) $currentSess['completed'] = $cp;
|
||||
$prevTs = $ts;
|
||||
@@ -227,6 +241,14 @@ if ($method === 'GET') {
|
||||
];
|
||||
}
|
||||
$durSec = max(0, intval(($s['endMs'] - $s['startMs']) / 1000));
|
||||
// Echte Spieldauer aus duration_ms — ein einzelnes Assessment (z. B. der
|
||||
// Farmer-Endbericht) hätte sonst Dauer ~0 und würde faelschlich als
|
||||
// „Abbruch" gewertet. Startzeit entsprechend zurückdatieren, damit der
|
||||
// Balken die ganze Spielzeit abdeckt (und gleich startende Läufe fluchten).
|
||||
if (!empty($s['durationMs']) && $s['durationMs'] > $durSec * 1000) {
|
||||
$durSec = intval($s['durationMs'] / 1000);
|
||||
$s['startMs'] = $s['endMs'] - $s['durationMs'];
|
||||
}
|
||||
// Success-Klassifikation
|
||||
$success = 'unknown';
|
||||
if ($s['completed'] === false) $success = 'aborted';
|
||||
@@ -234,6 +256,8 @@ if ($method === 'GET') {
|
||||
if ($s['stars'] >= 4) $success = 'good';
|
||||
elseif ($s['stars'] >= 2) $success = 'mid';
|
||||
else $success = 'bad';
|
||||
} elseif ($s['score'] !== null) {
|
||||
$success = $s['score'] >= 65 ? 'good' : ($s['score'] >= 40 ? 'mid' : 'bad');
|
||||
} elseif ($s['completed'] === true) $success = 'good';
|
||||
elseif ($durSec < 60) $success = 'aborted';
|
||||
else $success = 'running';
|
||||
@@ -247,6 +271,7 @@ if ($method === 'GET') {
|
||||
'endMs' => $s['endMs'],
|
||||
'durationSec' => $durSec,
|
||||
'stars' => $s['stars'],
|
||||
'score' => $s['score'],
|
||||
'completed' => $s['completed'],
|
||||
'success' => $success,
|
||||
'rowCount' => $s['rowCount'],
|
||||
@@ -314,6 +339,8 @@ if ($method === 'GET') {
|
||||
$startMs = ((int)$lr['started_ts']) * 1000;
|
||||
$endMs = ((int)$lr['last_seen_ts']) * 1000;
|
||||
$durSec = max(0, intval(($endMs - $startMs) / 1000));
|
||||
// Live-Erfolgs-Score für die Ampelfarbe: Farmer = aktuelle Trefferquote.
|
||||
$liveScore = ($simId === 'farmer' && isset($state['hit_rate'])) ? max(0, min(100, (int)$state['hit_rate'])) : null;
|
||||
$byStudent[$sid]['sessions'][] = [
|
||||
'simId' => $simId,
|
||||
'simTitle' => $mi['title'] ?? $simId,
|
||||
@@ -324,6 +351,7 @@ if ($method === 'GET') {
|
||||
'endMs' => $endMs,
|
||||
'durationSec' => $durSec,
|
||||
'stars' => null,
|
||||
'score' => $liveScore,
|
||||
'completed' => null,
|
||||
'success' => 'running',
|
||||
'rowCount' => 1,
|
||||
|
||||
Reference in New Issue
Block a user