Modul-Uebersicht: relevante Kennzahlen pro App korrigiert + Quoten

Alle $MODULE_HIGHLIGHTS gegen echte Daten (Klasse 102) verifiziert und auf die
tatsaechlichen Feldnamen umgestellt (viele waren veraltet/leer, z.B. farmer,
sonnensystem, energiemanager, busfahrt, logistik, heli, tourismus*).

- kofferdetektiv NEU: Geloest-Quote, Erstversuch, Fehlversuche, Oe Hinweise
- weltkueche/fluss NEU
- fluggesellschaft NEU (aus Submit-Schema)
- Berechnete Quoten via num/den -> Prozent (Erfolgsquote statt Rohzahl):
  kofferdetektiv, weltkueche, busfahrt, logistik, energiemanager, eu-werkstatt,
  sonnensystem, tourismustal, fluggesellschaft
- heli: Planung/Start/Landung/Level; sim-metrics: Level + Oe Hinweise ergaenzt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 21:22:50 +02:00
parent ff285e9af3
commit bfc90b78de
2 changed files with 73 additions and 47 deletions
+70 -46
View File
@@ -25,9 +25,11 @@ if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
$teacherId = Session::requireTeacher();
$db = Database::get();
// Pro Modul die wichtigsten Felder aus assessments.results-JSON.
// agg = min|max ("besser ist niedriger" vs "besser ist höher")
// format = percent|integer (Anzeige-Hint), unit = optionale Einheit hinter dem Wert.
// Pro Modul die wichtigsten Kennzahlen aus assessments.results-JSON (verifiziert
// gegen echte Daten, Klasse 102). agg = min|max ("besser niedriger/höher").
// Berechnete Quote: statt 'key' → 'num'+'den' (Wert = num/den·100, in %).
// unit = Einheit hinter dem Wert. HINWEIS: Feldnamen konsistent mit dem
// Client-Schema SIM_METRICS in App/assets/js/sim-metrics.js halten.
$MODULE_HIGHLIGHTS = [
'klima' => [
['key'=>'final_co2', 'label'=>'CO₂', 'agg'=>'min', 'unit'=>'ppm'],
@@ -35,60 +37,74 @@ $MODULE_HIGHLIGHTS = [
['key'=>'final_budget', 'label'=>'Budget', 'agg'=>'max', 'unit'=>'Mio'],
['key'=>'final_flooded_pct', 'label'=>'Überflutet','agg'=>'min', 'unit'=>'%'],
],
'klima-3d' => [
['key'=>'final_co2', 'label'=>'CO₂', 'agg'=>'min', 'unit'=>'ppm'],
['key'=>'final_temperature', 'label'=>'ΔTemp', 'agg'=>'min', 'unit'=>'°C'],
['key'=>'final_budget', 'label'=>'Budget', 'agg'=>'max', 'unit'=>'Mio'],
['key'=>'final_flooded_pct', 'label'=>'Überflutet','agg'=>'min', 'unit'=>'%'],
'kofferdetektiv' => [
['key'=>'solve_quote', 'num'=>'cases_solved', 'den'=>'cases_total', 'label'=>'Gelöst-Quote', 'agg'=>'max', 'unit'=>'%'],
['key'=>'correct_first_try', 'label'=>'Erstversuch', 'agg'=>'max'],
['key'=>'wrong_guesses', 'label'=>'Fehlversuche', 'agg'=>'min'],
['key'=>'hints_used_avg', 'label'=>'Ø Hinweise', 'agg'=>'min'],
],
'weltkueche' => [
['key'=>'dish_quote', 'num'=>'completedDishes', 'den'=>'totalDishes', 'label'=>'Gerichte-Quote', 'agg'=>'max', 'unit'=>'%'],
['key'=>'mistakesInDish', 'label'=>'Fehlklicks', 'agg'=>'min'],
],
'fluss' => [
['key'=>'biodiv', 'label'=>'Biodiversität', 'agg'=>'max', 'unit'=>'%'],
['key'=>'economy', 'label'=>'Wirtschaft', 'agg'=>'max', 'unit'=>'%'],
['key'=>'flood', 'label'=>'Hochwasser', 'agg'=>'min', 'unit'=>'%'],
['key'=>'food', 'label'=>'Ernährung', 'agg'=>'max', 'unit'=>'%'],
],
'farmer' => [
['key'=>'results.money', 'label'=>'Geld', 'agg'=>'max', 'unit'=>''],
['key'=>'results.success_rate', 'label'=>'Trefferquote','agg'=>'max','format'=>'percent'],
['key'=>'results.year_reached', 'label'=>'Jahr', 'agg'=>'max'],
['key'=>'results.co2_kg', 'label'=>'CO₂', 'agg'=>'min', 'unit'=>'kg'],
['key'=>'results.crops_used', 'label'=>'Sorten', 'agg'=>'max'],
],
'eu-werkstatt' => [
['key'=>'firstTryHits', 'label'=>'Erste Wahl','agg'=>'max'],
['key'=>'totalAttempts', 'label'=>'Versuche', 'agg'=>'min'],
['key'=>'muelleimerCount','label'=>'Mülleimer', 'agg'=>'min'],
],
'sonnensystem' => [
['key'=>'firstTryHits', 'label'=>'Erste Wahl','agg'=>'max'],
['key'=>'totalAttempts', 'label'=>'Versuche', 'agg'=>'min'],
],
'energiemanager' => [
['key'=>'blocksCorrect', 'label'=>'Blöcke ok', 'agg'=>'max'],
['key'=>'deficit', 'label'=>'Defizit', 'agg'=>'min'],
['key'=>'hit_rate', 'label'=>'Trefferquote', 'agg'=>'max', 'unit'=>'%'],
['key'=>'year', 'label'=>'Jahr', 'agg'=>'max'],
['key'=>'crops_used', 'label'=>'Sorten', 'agg'=>'max'],
],
'busfahrt' => [
['key'=>'cities_correct', 'label'=>'Treffer', 'agg'=>'max'],
['key'=>'tries_avg', 'label'=>'Ø Versuche','agg'=>'min'],
['key'=>'order_quote', 'num'=>'wonOrders', 'den'=>'totalOrders', 'label'=>'Aufträge', 'agg'=>'max', 'unit'=>'%'],
['key'=>'quiz_quote', 'num'=>'quizFirstTry', 'den'=>'totalOrders', 'label'=>'Quiz 1. Versuch','agg'=>'max', 'unit'=>'%'],
['key'=>'avgTapKm', 'label'=>'Ø Abweichung', 'agg'=>'min', 'unit'=>'km'],
],
'logistik' => [
['key'=>'contracts_done', 'label'=>'Aufträge', 'agg'=>'max'],
['key'=>'co2_total', 'label'=>'CO₂', 'agg'=>'min', 'unit'=>'kg'],
['key'=>'profit', 'label'=>'Gewinn', 'agg'=>'max', 'unit'=>'€'],
['key'=>'deliver_quote', 'num'=>'ordersDelivered', 'den'=>'totalOrders', 'label'=>'Geliefert', 'agg'=>'max', 'unit'=>'%'],
['key'=>'ordersLate', 'label'=>'Verspätet', 'agg'=>'min'],
['key'=>'finalBalance', 'label'=>'Bilanz', 'agg'=>'max', 'unit'=>'€'],
],
'energiemanager' => [
['key'=>'block_quote', 'num'=>'blocksBalanced', 'den'=>'blocksTotal', 'label'=>'Blöcke ok', 'agg'=>'max', 'unit'=>'%'],
['key'=>'day_quote', 'num'=>'daysPlayed', 'den'=>'daysTotal', 'label'=>'Tage geschafft','agg'=>'max', 'unit'=>'%'],
['key'=>'totalGap', 'label'=>'Restdefizit', 'agg'=>'min'],
],
'eu-werkstatt' => [
['key'=>'firsttry_quote', 'num'=>'firstTryHits', 'den'=>'stepsTotal', 'label'=>'Erste Wahl', 'agg'=>'max', 'unit'=>'%'],
['key'=>'totalAttempts', 'label'=>'Versuche', 'agg'=>'min'],
['key'=>'muelleimerCount','label'=>'Verworfen', 'agg'=>'min'],
],
'sonnensystem' => [
['key'=>'task_quote', 'num'=>'completed', 'den'=>'total', 'label'=>'Gelöst-Quote', 'agg'=>'max', 'unit'=>'%'],
['key'=>'completed', 'label'=>'Aufgaben', 'agg'=>'max'],
],
'heli' => [
['key'=>'routes_completed','label'=>'Routen', 'agg'=>'max'],
['key'=>'avg_accuracy', 'label'=>'Genauigkeit','agg'=>'max','format'=>'percent'],
],
'tourismusregion' => [
['key'=>'results.score', 'label'=>'Score', 'agg'=>'max'],
['key'=>'results.avgStars', 'label'=>'Ø Sterne', 'agg'=>'max'],
['key'=>'results.revenue', 'label'=>'Wertschöpfung','agg'=>'max', 'unit'=>'€'],
['key'=>'results.env', 'label'=>'Umwelt', 'agg'=>'max'],
['key'=>'results.goalsDone','label'=>'Ziele', 'agg'=>'max'],
['key'=>'planPct', 'label'=>'Planung', 'agg'=>'max', 'unit'=>'%'],
['key'=>'startScore', 'label'=>'Start', 'agg'=>'max'],
['key'=>'landingScore', 'label'=>'Landung', 'agg'=>'max'],
['key'=>'level', 'label'=>'Level', 'agg'=>'max'],
],
'tourismustal' => [
['key'=>'results.money', 'label'=>'Konto', 'agg'=>'max', 'unit'=>''],
['key'=>'results.maxGuests', 'label'=>'Gäste-Rekord','agg'=>'max'],
['key'=>'results.maxSummerGuests', 'label'=>'Sommer-Rekord','agg'=>'max'],
['key'=>'results.nature', 'label'=>'Naturwert', 'agg'=>'max', 'unit'=>'%'],
['key'=>'results.goalsDone', 'label'=>'Ziele', 'agg'=>'max'],
['key'=>'goal_quote', 'num'=>'goalsDone', 'den'=>'goalsTotal', 'label'=>'Ziele', 'agg'=>'max', 'unit'=>'%'],
['key'=>'satisfaction', 'label'=>'Zufriedenheit', 'agg'=>'max', 'unit'=>'%'],
['key'=>'nature', 'label'=>'Naturwert', 'agg'=>'max', 'unit'=>'%'],
['key'=>'money', 'label'=>'Konto', 'agg'=>'max', 'unit'=>''],
],
'tourismusregion' => [
['key'=>'score', 'label'=>'Gesamtscore', 'agg'=>'max', 'unit'=>'%'],
],
'fluggesellschaft' => [
['key'=>'flight_order_quote', 'num'=>'wonOrders', 'den'=>'totalOrders', 'label'=>'Aufträge', 'agg'=>'max', 'unit'=>'%'],
['key'=>'flight_quiz_quote', 'num'=>'quizFirstTry', 'den'=>'totalOrders', 'label'=>'Quiz 1. Versuch','agg'=>'max', 'unit'=>'%'],
['key'=>'co2Kg', 'label'=>'CO₂', 'agg'=>'min', 'unit'=>'kg'],
['key'=>'earnings', 'label'=>'Gewinn', 'agg'=>'max', 'unit'=>'€'],
],
];
$MODULE_HIGHLIGHTS['klima-3d'] = $MODULE_HIGHLIGHTS['klima'];
// Hilfsfunktion: holt einen Wert aus einem JSON-Pfad ("results.money" → $json['results']['money']).
function _hl_get($json, $path) {
@@ -650,7 +666,15 @@ if ($view === 'module') {
if (!is_array($j)) continue;
if (!isset($perStudent[$sid])) $perStudent[$sid] = [];
foreach ($highlights as $h) {
$val = _hl_get($j, $h['key']);
if (isset($h['num'])) {
// Berechnete Quote: num/den in Prozent (0..100)
$nu = _hl_get($j, $h['num']);
$de = _hl_get($j, $h['den']);
$val = ($nu !== null && $de !== null && (float)$de != 0.0)
? round((float)$nu / (float)$de * 100) : null;
} else {
$val = _hl_get($j, $h['key']);
}
if ($val === null) continue;
$val = is_numeric($val) ? (float)$val : $val;
if (!isset($perStudent[$sid][$h['key']])) {