seeds/ine_catalog.sql // // The output is self-contained and idempotent — it creates `catalog_courses` // if needed and upserts every course — so it can be applied to any install, // before or after a code deploy, as many times as you like: // // mysql coursehub < seeds/ine_catalog.sql && php scan.php // // The scan is what matches library folders to the seeded rows (course_meta). // seeds/ is git-ignored: the file carries the vendor's course descriptions. require_once __DIR__ . '/../includes/functions.php'; if (PHP_SAPI !== 'cli') { http_response_code(404); exit; } $src = $argv[1] ?? ''; if ($src === '' || !is_file($src)) { fwrite(STDERR, "usage: php tools/build-catalog-seed.php > seeds/ine_catalog.sql\n"); exit(1); } ini_set('memory_limit', '512M'); $raw = file_get_contents($src); $all = json_decode($raw, true); if (!is_array($all)) { fwrite(STDERR, "not a JSON array: $src\n"); exit(1); } // Some exports wrap the list. if (isset($all['results']) && is_array($all['results'])) $all = $all['results']; if (isset($all['courses']) && is_array($all['courses'])) $all = $all['courses']; /** MySQL string literal (backslash escaping, the server's default mode). */ function sqlQuote(?string $v): string { if ($v === null) return 'NULL'; return "'" . str_replace( ["\\", "'", "\0", "\n", "\r", "\x1a"], ["\\\\", "\\'", "\\0", "\\n", "\\r", "\\Z"], $v ) . "'"; } $json = fn(array $a) => json_encode($a, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $rows = []; $skipped = 0; foreach ($all as $entry) { if (!is_array($entry)) { $skipped++; continue; } $f = catalogFields($entry); if ($f['id'] === null || $f['name'] === null || $f['slug'] === null) { $skipped++; continue; } $rows[$f['id']] = [ sqlQuote($f['id']), sqlQuote($f['name']), sqlQuote($f['slug']), sqlQuote(folderKeyFor($f['name'])), sqlQuote($f['vendor']), sqlQuote($f['difficulty']), sqlQuote($json($f['tags'])), sqlQuote($f['short_description']), sqlQuote($f['description']), sqlQuote($json($f['learning_paths'])), sqlQuote($json($f['categories'])), $f['duration_seconds'] === null ? 'NULL' : (string)$f['duration_seconds'], sqlQuote($f['url']), ]; } ksort($rows); // stable output: the same export always produces the same file $cols = 'id, name, slug, folder_key, vendor, difficulty, tags, short_description, description,' . ' learning_paths, categories, duration_seconds, url'; $upd = implode(', ', array_map( fn($c) => "$c = VALUES($c)", ['name', 'slug', 'folder_key', 'vendor', 'difficulty', 'tags', 'short_description', 'description', 'learning_paths', 'categories', 'duration_seconds', 'url'] )); echo "-- CourseHub catalogue seed — generated by tools/build-catalog-seed.php\n"; echo '-- source: ' . basename($src) . ' (modified ' . gmdate('Y-m-d H:i:s', filemtime($src) ?: 0) . " UTC)\n"; echo '-- courses: ' . count($rows) . ($skipped ? " ($skipped entries skipped)" : '') . "\n"; echo "-- Safe to re-run: creates the table if needed and upserts by id.\n"; echo "SET NAMES utf8mb4;\n"; echo catalogCoursesDdl() . ";\n"; foreach (array_chunk(array_values($rows), 50) as $chunk) { echo "INSERT INTO catalog_courses ($cols) VALUES\n"; echo implode(",\n", array_map(fn($r) => '(' . implode(', ', $r) . ')', $chunk)); echo "\nON DUPLICATE KEY UPDATE $upd;\n"; } fwrite(STDERR, 'wrote ' . count($rows) . " courses\n");