1e51ef7def
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/ - 12 code-workspace-Files - STATUS-*.md - viele M/D/R-Änderungen an bereits getrackten Files - .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
114 lines
4.6 KiB
PHP
114 lines
4.6 KiB
PHP
<?php
|
|
/**
|
|
* GET /api/modules/available
|
|
*
|
|
* Liefert für die eingeloggte Schüler*in zwei Listen:
|
|
* - assignments: aktuelle Modulaufträge der Klasse (fixe Schwierigkeit)
|
|
* - freeModules: Module, die für die Klasse generell freigegeben sind
|
|
*
|
|
* Liest Modul-Stammdaten aus module_info_v2 (V2-Registrierung).
|
|
* Aufträge aus class_assignments_v2.
|
|
* Freigaben aus V1-class_modules (wird in V2 weiter genutzt).
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/../../bootstrap.php';
|
|
|
|
v2_validate_method('GET');
|
|
$auth = v2_auth_require('student');
|
|
$studentId = (int) ($auth['sub'] ?? 0);
|
|
$classId = (int) ($auth['class_id'] ?? 0);
|
|
|
|
// ─── Aktive Aufträge (V2) ────────────────────────────────────────
|
|
$assignmentsRaw = v2_db_all("
|
|
SELECT a.id, a.module_slug, a.difficulty, a.starts_at, a.ends_at,
|
|
mi.display_name, mi.display_name_easy, mi.icon, mi.card_image_url,
|
|
mi.short_description, mi.short_description_easy,
|
|
mi.estimated_duration_min
|
|
FROM class_assignments_v2 a
|
|
LEFT JOIN module_info_v2 mi ON mi.slug = a.module_slug
|
|
WHERE a.class_id = :cid AND a.active = 1
|
|
AND a.starts_at <= NOW()
|
|
AND (a.ends_at IS NULL OR a.ends_at > NOW())
|
|
ORDER BY a.created_at DESC
|
|
", [':cid' => $classId]);
|
|
|
|
// ─── Freie Module (V1-Freigaben weiter genutzt) ──────────────────
|
|
// V1: class_modules.module_id matcht module_info_v2.slug
|
|
$freeRaw = [];
|
|
try {
|
|
$freeRaw = v2_db_all("
|
|
SELECT mi.slug, mi.display_name, mi.display_name_easy,
|
|
mi.icon, mi.card_image_url, mi.short_description,
|
|
mi.short_description_easy, mi.estimated_duration_min,
|
|
mi.status
|
|
FROM module_info_v2 mi
|
|
WHERE mi.status IN ('beta', 'stable')
|
|
AND mi.slug IN (
|
|
SELECT module_id FROM class_modules WHERE class_id = :cid AND enabled = 1
|
|
)
|
|
ORDER BY mi.display_name
|
|
", [':cid' => $classId]);
|
|
} catch (Throwable $e) {
|
|
// Fallback: alle 'stable' V2-Module wenn class_modules nicht existiert / leer ist
|
|
$freeRaw = v2_db_all("
|
|
SELECT slug, display_name, display_name_easy, icon, card_image_url,
|
|
short_description, short_description_easy,
|
|
estimated_duration_min, status
|
|
FROM module_info_v2
|
|
WHERE status IN ('beta', 'stable')
|
|
ORDER BY display_name
|
|
");
|
|
}
|
|
|
|
// ─── Level-Unlocks ───────────────────────────────────────────────
|
|
$unlocks = v2_db_all("
|
|
SELECT module_slug, highest_unlocked_level, highest_completed_level
|
|
FROM student_level_unlock_v2
|
|
WHERE student_id = :sid
|
|
", [':sid' => $studentId]);
|
|
$unlocksBySlug = [];
|
|
foreach ($unlocks as $u) {
|
|
$unlocksBySlug[$u['module_slug']] = [
|
|
'highestUnlocked' => $u['highest_unlocked_level'],
|
|
'highestCompleted' => $u['highest_completed_level'],
|
|
];
|
|
}
|
|
|
|
// ─── Output formen ───────────────────────────────────────────────
|
|
$assignments = array_map(function ($a) {
|
|
return [
|
|
'assignmentId' => (int) $a['id'],
|
|
'moduleSlug' => $a['module_slug'],
|
|
'difficulty' => $a['difficulty'],
|
|
'displayName' => $a['display_name'] ?? $a['module_slug'],
|
|
'displayNameEasy' => $a['display_name_easy'] ?? $a['module_slug'],
|
|
'icon' => $a['icon'] ?? '📘',
|
|
'cardImageUrl' => $a['card_image_url'],
|
|
'shortDescription' => $a['short_description'] ?? '',
|
|
'shortDescriptionEasy' => $a['short_description_easy'] ?? '',
|
|
'estimatedDurationMin' => (int) ($a['estimated_duration_min'] ?? 0),
|
|
'endsAt' => $a['ends_at'],
|
|
];
|
|
}, $assignmentsRaw);
|
|
|
|
$freeModules = array_map(function ($m) use ($unlocksBySlug) {
|
|
return [
|
|
'slug' => $m['slug'],
|
|
'displayName' => $m['display_name'],
|
|
'displayNameEasy' => $m['display_name_easy'],
|
|
'icon' => $m['icon'] ?? '📘',
|
|
'cardImageUrl' => $m['card_image_url'],
|
|
'shortDescription' => $m['short_description'] ?? '',
|
|
'shortDescriptionEasy' => $m['short_description_easy'] ?? '',
|
|
'estimatedDurationMin' => (int) ($m['estimated_duration_min'] ?? 0),
|
|
'status' => $m['status'],
|
|
'unlocked' => $unlocksBySlug[$m['slug']] ?? ['highestUnlocked' => 'L1', 'highestCompleted' => null],
|
|
];
|
|
}, $freeRaw);
|
|
|
|
v2_response_ok([
|
|
'assignments' => $assignments,
|
|
'freeModules' => $freeModules,
|
|
]);
|