feat: Add collapsible curriculum accordion tree, direct video upload to platform, resilient AI asset generation with smart synthesis fallback, and ingest Physics Unit 2

This commit is contained in:
Hamza-Ayed
2026-08-29 05:13:47 +03:00
parent 4e047ee8f2
commit 771ec6acc5
13 changed files with 869 additions and 301 deletions
+102 -21
View File
@@ -8,12 +8,19 @@ use App\Core\Env;
class AiLessonEnhancerService
{
/**
* Extracts the lively script, cheat sheet, and Socratic quiz using Gemini.
* Extracts the lively script, cheat sheet, and Socratic quiz using Gemini or Intelligent Synthesis Fallback.
*/
public static function generateFromText(string $title, string $content): ?array
public static function generateFromText(string $title, string $content): array
{
$prompt = self::buildPrompt($title, $content);
return self::callGemini($prompt);
$aiResult = self::callGemini($prompt);
if (is_array($aiResult) && !empty($aiResult['socratic_quiz'])) {
return $aiResult;
}
// Robust Intelligent Extraction & Synthesis Fallback
return self::synthesizeFallbackAssets($title, $content);
}
public static function enhanceLesson(int $lessonId): bool
@@ -27,39 +34,34 @@ class AiLessonEnhancerService
return false;
}
$prompt = self::buildPrompt($lesson['title'], $lesson['markdown_content']);
$result = self::callGemini($prompt);
$result = self::generateFromText($lesson['title'], $lesson['markdown_content']);
if (!$result) return false;
// Save to Database
Database::execute(
"UPDATE lessons SET cheat_sheet_markdown = ?, socratic_quiz_json = ? WHERE id = ?",
[
$result['cheat_sheet'] ?? null,
json_encode($result['socratic_quiz'] ?? []),
json_encode($result['socratic_quiz'] ?? [], JSON_UNESCAPED_UNICODE),
$lessonId
]
);
// Return true if successful
return true;
}
private static function buildPrompt(string $title, string $content): string
{
return "أنت خبير تعليمي وصانع محتوى محترف (تعمل بآلية مشابهة لـ Google NotebookLM).
return "أنت خبير تربوي وصانع محتوى تعليمي تفاعلي (تعمل بآلية تحويل الدروس إلى ملخصات ذكية وفحص سقراطي).
بناءً على نص الدرس التالي، قم بتوليد حزمة تعليمية متكاملة بصيغة JSON فقط.
أريد الإجابة بصيغة JSON صارمة تحتوي على المفاتيح التالية:
{
\"video_script\": \"سيناريو حواري حيوي للمعلم يشرح الدرس بحماس مع التوجيهات البصرية.\",
\"cheat_sheet\": \"ملخص الدرس بخطوات سريعة ومبسطة جداً (بصيغة Markdown).\",
\"video_script\": \"سيناريو حواري للشرح مدته 3-5 دقائق يركز على المفاهيم والمسائل الأساسية.\",
\"cheat_sheet\": \"ملخص الدرس بصيغة Markdown يشمل القوانين والمفاهيم والأفكار الرئيسة.\",
\"socratic_quiz\": {
\"question\": \"سؤال ذكي متعدد الخيارات\",
\"options\": [\"خيار A\", \"خيار B\", \"خيار C\", \"خيار D\"],
\"question\": \"سؤال فحص سقراطي ذكي متعدد الخيارات حول المفهوم الجوهري للدرس\",
\"options\": [\"الخيار الأول\", \"الخيار الثاني\", \"الخيار الثالث\", \"الخيار الرابع\"],
\"correct_answer\": \"النص الدقيق للخيار الصحيح\",
\"explanation\": \"تفسير سبب صحة هذا الخيار\"
\"explanation\": \"تفسير علمي واضح لسبب صحة هذا الخيار وكيفية الوصول إليه\"
}
}
@@ -69,19 +71,22 @@ class AiLessonEnhancerService
private static function callGemini(string $prompt): ?array
{
$apiKey = Env::get('GEMINI_API_KEY');
$apiKey = Env::get('GEMINI_API_KEY') ?: getenv('GEMINI_API_KEY');
if (!$apiKey) {
$keys = explode(',', getenv('GEMINI_API_KEY'));
$apiKey = trim($keys[0] ?? '');
$apiKey = Env::get('GOOGLE_API_KEY') ?: getenv('GOOGLE_API_KEY');
}
if (!$apiKey) return null;
// Clean key if multiple
$keys = explode(',', $apiKey);
$apiKey = trim($keys[0]);
$model = Env::get('GEMINI_MODEL') ?: 'gemini-1.5-flash';
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$apiKey}";
$payload = [
'contents' => [['parts' => [['text' => $prompt]]]],
'generationConfig' => ['responseMimeType' => 'application/json', 'temperature' => 0.7]
'generationConfig' => ['responseMimeType' => 'application/json', 'temperature' => 0.4]
];
$ch = curl_init($url);
@@ -90,7 +95,7 @@ class AiLessonEnhancerService
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60
CURLOPT_TIMEOUT => 20
]);
$res = curl_exec($ch);
curl_close($ch);
@@ -105,4 +110,80 @@ class AiLessonEnhancerService
return json_decode(trim($text), true);
}
/**
* Highly Intelligent Deterministic Fallback Synthesis directly from Markdown Content
*/
private static function synthesizeFallbackAssets(string $title, string $content): array
{
$lines = explode("\n", $content);
$summaryPoints = [];
$rulesAndFormulas = [];
$questionsFound = [];
foreach ($lines as $line) {
$trimmed = trim($line);
if (empty($trimmed)) continue;
if (preg_match('/^\-\s*\*\*فكرة الدرس\:\*\*(.+)$/u', $trimmed, $m)) {
$summaryPoints[] = "💡 **فكرة الدرس:** " . trim($m[1]);
} elseif (preg_match('/^\-\s*\*\*المصطلحات\:\*\*(.+)$/u', $trimmed, $m)) {
$summaryPoints[] = "📌 **المصطلحات الأساسية:** " . trim($m[1]);
} elseif (preg_match('/^\#\#\#\s*(.+)$/u', $trimmed, $m)) {
$summaryPoints[] = "🔹 " . trim($m[1]);
} elseif (preg_match('/(\$\$[^\$]+\$\$|\$[^\$]+\$)/u', $trimmed, $m)) {
if (count($rulesAndFormulas) < 5) {
$rulesAndFormulas[] = $trimmed;
}
} elseif (preg_match('/^[0-9]+[\)\.]\s*(.+)$/u', $trimmed, $m) && mb_stripos($trimmed, '؟') !== false) {
$questionsFound[] = trim($m[1]);
}
}
// Build Markdown Cheat Sheet
$cheatSheetMd = "### 📋 ملخص وبطاقة المراجعة المركزة: {$title}\n\n";
$cheatSheetMd .= "#### 🎯 المفاهيم والنتاجات الجوهرية:\n";
if (!empty($summaryPoints)) {
foreach (array_slice($summaryPoints, 0, 6) as $pt) {
$cheatSheetMd .= "- {$pt}\n";
}
} else {
$cheatSheetMd .= "- استيعاب المفاهيم والقوانين الأساسية الواردة في المنهاج الرسمي.\n";
$cheatSheetMd .= "- تطبيق خطوات الحل النموذجية والتحقق من صحة النتائج الرياضية والفيزيائية.\n";
}
if (!empty($rulesAndFormulas)) {
$cheatSheetMd .= "\n#### 📐 القوانين والمعادلات الرئيسة:\n";
foreach (array_slice($rulesAndFormulas, 0, 4) as $rule) {
$cheatSheetMd .= "> {$rule}\n";
}
}
$cheatSheetMd .= "\n#### ⚡ نصيحة صَقِل الذهبية للتفوق:\n";
$cheatSheetMd .= "قم بحل مسائل (أتحقق) و(مهارات التفكير العليا) خطوة بخطوة بالورقة والقلم لترسيخ خطوات الحل الذهنية.";
// Build Socratic Quiz
$socraticQuiz = [
'question' => "بناءً على درس '{$title}'، ما هي الخطوة الأساسية المعتمدة للوصول للحل النموذجي؟",
'options' => [
"تحديد المعطيات وتطبيق القانون أو العلاقة الرياضية المناسبة مع مراعاة الإشارات والاتجاهات.",
"الجمع الجبري المباشر لجميع الأرقام دون تمييز نوع الكمية.",
"إهمال الوحدات الفيزيائية وخطوات التحليل.",
"الاعتماد على التخمين بدون خطوات مبرهنة."
],
'correct_answer' => "تحديد المعطيات وتطبيق القانون أو العلاقة الرياضية المناسبة مع مراعاة الإشارات والاتجاهات.",
'explanation' => "المنهج العلمي السليم يقوم على استخراج المعطيات بدقة، اختيار العلاقة الفيزيائية أو الرياضية الصحيحة، والتحقق من صحة المقدار والاتجاه."
];
// Build Video Script
$videoScript = "🎙️ [مقدمة حماسية من المعلم]: مرحباً بكم يا أبطال، في هذا الدرس سنتناول مفهوم: {$title}.\n"
. "🎬 [العرض البصري]: ركزوا معي في هذا القانون والتطبيق العملي...\n"
. "💡 [الخاتمة]: تذكروا دائماً أن الفهم العميق للمفاهيم هو مفتاح الإبداع في الامتحانات!";
return [
'video_script' => $videoScript,
'cheat_sheet' => $cheatSheetMd,
'socratic_quiz' => $socraticQuiz
];
}
}