edu-senex/edu/api/import.php

146 Zeilen
5,1 KiB
PHP

<?php
/**
* Content Import - reads Markdown files from content/ and upserts into DB.
*/
function import_all_content($pdo) {
$stats = ['decks' => 0, 'cards' => 0, 'tutorials' => 0, 'cheatsheets' => 0];
$flashcard_dir = CONTENT_DIR . '/flashcards';
if (is_dir($flashcard_dir)) {
$sort = 0;
foreach (glob($flashcard_dir . '/deck-*.md') as $file) {
$count = import_flashcard_deck($pdo, $file, $sort++);
$stats['decks']++;
$stats['cards'] += $count;
}
}
$tutorial_dir = CONTENT_DIR . '/tutorials';
if (is_dir($tutorial_dir)) {
$sort = 0;
foreach (glob($tutorial_dir . '/*.md') as $file) {
$basename = basename($file);
if ($basename === 'onboarding-path.md' || $basename === 'progress.md') continue;
import_tutorial($pdo, $file, $sort++);
$stats['tutorials']++;
}
}
$cheatsheet_dir = CONTENT_DIR . '/cheatsheets';
if (is_dir($cheatsheet_dir)) {
foreach (glob($cheatsheet_dir . '/*-cheatsheet.md') as $file) {
import_cheatsheet($pdo, $file);
$stats['cheatsheets']++;
}
}
return $stats;
}
function import_flashcard_deck($pdo, $filepath, $deck_sort) {
$content = file_get_contents($filepath);
$basename = basename($filepath);
preg_match('/^# Flashcard Deck: (.+)$/m', $content, $m);
$deck_name = $m[1] ?? str_replace(['deck-', '.md'], '', $basename);
$slug = slugify($deck_name);
$stmt = $pdo->prepare("
INSERT INTO decks (slug, name, sort_order, source_file, updated_at)
VALUES (:slug, :name, :sort, :src, NOW())
ON CONFLICT (slug) DO UPDATE SET name = :name, sort_order = :sort, source_file = :src, updated_at = NOW()
RETURNING id
");
$stmt->execute([':slug' => $slug, ':name' => $deck_name, ':sort' => $deck_sort, ':src' => $basename]);
$deck_id = $stmt->fetchColumn();
// Parse cards
preg_match_all(
'/## Karte (\d+) \| (\w+)\s*\n\*\*Q:\*\*\s*(.+?)(?:\n)\*\*A:\*\*\s*(.+?)(?=\n---|\n## Karte|\n## Spaced|\Z)/s',
$content, $matches, PREG_SET_ORDER
);
// Delete old cards for this deck, then re-insert
$pdo->prepare("DELETE FROM cards WHERE deck_id = :did")->execute([':did' => $deck_id]);
$card_count = 0;
foreach ($matches as $i => $match) {
$level = strtolower(trim($match[2]));
$question = trim($match[3]);
$answer = trim($match[4]);
$stmt = $pdo->prepare("
INSERT INTO cards (deck_id, question, answer, level, sort_order, source_file)
VALUES (:did, :q, :a, :lvl, :sort, :src)
");
$stmt->execute([
':did' => $deck_id, ':q' => $question, ':a' => $answer,
':lvl' => $level, ':sort' => $i, ':src' => $basename
]);
$card_count++;
}
$pdo->prepare("UPDATE decks SET card_count = :c WHERE id = :id")
->execute([':c' => $card_count, ':id' => $deck_id]);
return $card_count;
}
function import_tutorial($pdo, $filepath, $sort_order) {
$content = file_get_contents($filepath);
$basename = basename($filepath);
preg_match('/^# (.+)$/m', $content, $m);
$title = $m[1] ?? str_replace('.md', '', $basename);
$slug = slugify($basename);
preg_match('/\*\*Dauer\*\*:\s*ca\.\s*(\d+)/i', $content, $dm);
$duration = $dm[1] ?? null;
preg_match('/\*\*Ziel\*\*:\s*(.+)$/m', $content, $desc);
$description = $desc[1] ?? '';
$stmt = $pdo->prepare("
INSERT INTO tutorials (slug, title, description, content_md, duration_min, sort_order, source_file, updated_at)
VALUES (:slug, :title, :desc, :content, :dur, :sort, :src, NOW())
ON CONFLICT (slug) DO UPDATE SET
title = :title, description = :desc, content_md = :content,
duration_min = :dur, sort_order = :sort, source_file = :src, updated_at = NOW()
");
$stmt->execute([
':slug' => $slug, ':title' => $title, ':desc' => $description,
':content' => $content, ':dur' => $duration,
':sort' => $sort_order, ':src' => $basename
]);
}
function import_cheatsheet($pdo, $filepath) {
$content = file_get_contents($filepath);
$basename = basename($filepath);
preg_match('/^# (.+)$/m', $content, $m);
$title = $m[1] ?? str_replace(['-cheatsheet', '.md'], ['', ''], $basename);
$slug = slugify($basename);
$category = ucfirst(str_replace(['-cheatsheet.md', '-'], ['', ' '], $basename));
$stmt = $pdo->prepare("
INSERT INTO cheatsheets (slug, title, category, content_md, source_file, updated_at)
VALUES (:slug, :title, :cat, :content, :src, NOW())
ON CONFLICT (slug) DO UPDATE SET
title = :title, category = :cat, content_md = :content, source_file = :src, updated_at = NOW()
");
$stmt->execute([
':slug' => $slug, ':title' => $title, ':cat' => $category,
':content' => $content, ':src' => $basename
]);
}
function slugify($text) {
$text = str_replace(['.md', 'deck-'], '', $text);
$text = strtolower($text);
$text = preg_replace('/[^a-z0-9\-]/', '-', $text);
$text = preg_replace('/-+/', '-', $text);
return trim($text, '-');
}