Files
saqel/tools/validate_virtual_lab_specs.php

62 lines
2.1 KiB
PHP

<?php
declare(strict_types=1);
$root = dirname(__DIR__);
$manifestPath = $root . '/backend/storage/curriculum/_incoming/grade_10/manifest.accepted.json';
$specRoot = $root . '/docs/virtual_labs/grade_10';
$manifest = json_decode((string) file_get_contents($manifestPath), true, 512, JSON_THROW_ON_ERROR);
$expected = [];
$errors = [];
foreach ($manifest['lessons'] ?? [] as $lesson) {
$expected[(string) $lesson['id']] = (string) $lesson['file'];
}
$seen = [];
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($specRoot));
foreach ($iterator as $file) {
if (!$file->isFile() || !str_ends_with($file->getFilename(), '.lab.md')) {
continue;
}
$text = (string) file_get_contents($file->getPathname());
if (!preg_match('/^curriculum_lesson_id:\s*(.+)$/m', $text, $idMatch)) {
$errors[] = "Missing curriculum_lesson_id: {$file->getPathname()}";
continue;
}
$id = trim($idMatch[1]);
if (isset($seen[$id])) {
$errors[] = "Duplicate lesson id {$id}";
}
$seen[$id] = true;
if (!isset($expected[$id])) {
$errors[] = "Unknown lesson id {$id}";
}
if (!preg_match('/^source_markdown:\s*(.+)$/m', $text, $sourceMatch) || trim($sourceMatch[1]) !== ($expected[$id] ?? null)) {
$errors[] = "Wrong source binding for {$id}";
}
foreach (['## 3. فكرة اللعبة التعليمية', '## 4. المشهد المتحرك', '## 7. محطات فحص الفهم', '## 11. سيناريوهات التحقق الإلزامية'] as $required) {
if (!str_contains($text, $required)) {
$errors[] = "Missing section '{$required}' in {$id}";
}
}
}
foreach (array_keys($expected) as $id) {
if (!isset($seen[$id])) {
$errors[] = "Missing spec for {$id}";
}
}
if (count($seen) !== count($expected)) {
$errors[] = 'Count mismatch: expected ' . count($expected) . ', found ' . count($seen);
}
if ($errors !== []) {
fwrite(STDERR, implode("\n", $errors) . "\n");
exit(1);
}
fwrite(STDOUT, 'Validated ' . count($seen) . " one-to-one lesson lab specifications.\n");