Files
saqel/backend/scripts/curriculum_markdown_parser.php
T

140 lines
5.1 KiB
PHP

<?php
require_once __DIR__ . '/../app/bootstrap.php';
use App\Services\CurriculumService;
/**
* Intelligent Markdown Curriculum Parser
* Splits a complete textbook markdown into units, lessons, outcomes, and resources.
*/
function parseAndIngestTextbookMarkdown(string $rawMarkdown, array $metadata = []): array
{
$lines = explode("\n", $rawMarkdown);
$gradeName = $metadata['grade_name'] ?? 'الصف العاشر';
$gradeKey = $metadata['grade_key'] ?? 'grade_10';
$subName = $metadata['subject_name'] ?? 'الرياضيات';
$subKey = $metadata['subject_key'] ?? 'math_10';
$semName = $metadata['semester_name'] ?? 'الفصل الدراسي الأول';
$semKey = $metadata['semester_key'] ?? 'semester_1';
$units = [];
$currentUnit = null;
$currentLesson = null;
$lessonBuffer = [];
$flushLesson = function() use (&$currentUnit, &$currentLesson, &$lessonBuffer) {
if ($currentUnit && $currentLesson) {
$content = trim(implode("\n", $lessonBuffer));
if (!empty($content)) {
$currentLesson['markdown_content'] = $content;
$currentUnit['lessons'][] = $currentLesson;
}
$currentLesson = null;
$lessonBuffer = [];
}
};
$flushUnit = function() use (&$units, &$currentUnit, $flushLesson) {
$flushLesson();
if ($currentUnit && !empty($currentUnit['lessons'])) {
$units[] = $currentUnit;
$currentUnit = null;
}
};
$unitCounter = 0;
$lessonCounter = 0;
$arabicOrdinalMap = [
'الأولى' => 1, 'الاولى' => 1, '1' => 1, 'one' => 1,
'الثانية' => 2, 'الثانيه' => 2, '2' => 2, 'two' => 2,
'الثالثة' => 3, 'الثالثه' => 3, '3' => 3, 'three' => 3,
'الرابعة' => 4, 'الرابعه' => 4, '4' => 4, 'four' => 4,
'الخامسة' => 5, 'الخامسه' => 5, '5' => 5, 'five' => 5,
'السادسة' => 6, 'السادسه' => 6, '6' => 6, 'six' => 6,
'السابعة' => 7, 'السابعه' => 7, '7' => 7, 'seven' => 7,
'الثامنة' => 8, 'الثامنه' => 8, '8' => 8, 'eight' => 8,
'التاسعة' => 9, 'التاسعه' => 9, '9' => 9, 'nine' => 9,
'العاشرة' => 10, 'العاشره' => 10, '10' => 10, 'ten' => 10,
];
foreach ($lines as $line) {
$trimmed = trim($line);
// Detect Unit (# UNIT or # الوحدة)
if (preg_match('/^#\s+(UNIT|الوحدة)\s*([0-9\-\:A-Za-z\s\p{Arabic}]+)(.*)$/ui', $trimmed, $m) || preg_match('/^#\s+([A-Za-z0-9\s]+(?:SPOT|SKILLS|REFERENCE|LIST))/ui', $trimmed, $m2)) {
$flushUnit();
$unitTitle = trim(preg_replace('/^#\s+/u', '', $trimmed));
// Try to extract unit number from title
$detectedNum = null;
foreach ($arabicOrdinalMap as $word => $num) {
if (mb_stripos($unitTitle, $word) !== false) {
$detectedNum = $num;
break;
}
}
if ($detectedNum !== null) {
$unitCounter = $detectedNum;
} else {
$unitCounter++;
}
$unitKey = sprintf('unit_%02d', $unitCounter);
$currentUnit = [
'unit_key' => $unitKey,
'unit_name' => $unitTitle,
'lessons' => []
];
$lessonCounter = 0;
continue;
}
// Detect Lesson (## Lesson or ## الدرس or ## معمل or ## مقدمة or ## اختبار or ## )
if (preg_match('/^##\s+(Lesson|الدرس|قسم|المحطة|قواعد|تمارين|معمل|مقدمة|مشروع|اختبار)\s*([0-9\-\:A-Za-z\s\p{Arabic}]+)(.*)$/ui', $trimmed, $m) || (preg_match('/^##\s+(.+)$/u', $trimmed, $mAlt) && $currentUnit)) {
$flushLesson();
$lessonCounter++;
$lessonTitle = trim(preg_replace('/^##\s+/u', '', $trimmed));
$lesId = sprintf('lesson_%02d', $lessonCounter);
if (!$currentUnit) {
$unitCounter++;
$currentUnit = [
'unit_key' => sprintf('unit_%02d', $unitCounter),
'unit_name' => 'الوحدة ' . $unitCounter,
'lessons' => []
];
}
$currentLesson = [
'lesson_id' => $lesId,
'title' => $lessonTitle,
'outcomes' => [$lessonTitle]
];
$lessonBuffer = [$trimmed];
continue;
}
if ($currentLesson) {
$lessonBuffer[] = $line;
}
}
$flushUnit();
$extracted = [
'grade_name' => $gradeName,
'grade_key' => $gradeKey,
'subject_name' => $subName,
'subject_key' => $subKey,
'semester_name' => $semName,
'semester_key' => $semKey,
'units' => $units
];
return CurriculumService::mergeExtractedCurriculum($extracted);
}