Implement multi-teacher video selector, in-app curriculum document reader, 15-question Unit 1 exam bank, and interactive science simulation engine
This commit is contained in:
@@ -55,10 +55,16 @@ class ExamController
|
||||
*/
|
||||
public function getExamDetails(Request $request, Response $response): void
|
||||
{
|
||||
self::ensureSchema();
|
||||
$examId = (int)$request->getParam('id');
|
||||
$isTeacher = ($request->role === 'teacher' || $request->role === 'super_admin');
|
||||
|
||||
$exam = Database::selectOne("SELECT * FROM exams WHERE id = ? LIMIT 1", [$examId]);
|
||||
if (!$exam || $examId === 1 || (str_contains($exam['title'] ?? '', 'الوحدة الأولى') && count(Database::select("SELECT id FROM questions WHERE exam_id = ?", [$examId])) < 15)) {
|
||||
$examId = self::seedUnit1ComprehensiveExam();
|
||||
$exam = Database::selectOne("SELECT * FROM exams WHERE id = ? LIMIT 1", [$examId]);
|
||||
}
|
||||
|
||||
if (!$exam) {
|
||||
$response->status(404)->json(['status' => 'error', 'message' => 'الامتحان غير موجود']);
|
||||
return;
|
||||
@@ -357,4 +363,201 @@ class ExamController
|
||||
error_log("ExamController ensureSchema notice: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static function seedUnit1ComprehensiveExam(): int
|
||||
{
|
||||
self::ensureSchema();
|
||||
|
||||
$exam = Database::selectOne(
|
||||
"SELECT id FROM exams WHERE (scope = 'unit_comprehensive' OR scope = 'unit_exam' OR id = 1) AND (title LIKE '%الوحدة الأولى%' OR id = 1) LIMIT 1"
|
||||
);
|
||||
|
||||
$examId = $exam ? (int)$exam['id'] : 0;
|
||||
if (!$examId) {
|
||||
$examId = (int)Database::insert(
|
||||
"INSERT INTO exams (uuid, course_id, lesson_id, creator_type, scope, title, passing_percentage, total_points, duration_minutes, is_mandatory, is_published)
|
||||
VALUES (UUID(), 1, 0, 'ai_adaptive', 'unit_comprehensive', 'اختبار الفهم الشامل: الوحدة الأولى — أنظمة المعادلات', 70.00, 100, 45, 1, 1)"
|
||||
);
|
||||
} else {
|
||||
Database::query(
|
||||
"UPDATE exams SET title = 'اختبار الفهم الشامل: الوحدة الأولى — أنظمة المعادلات', duration_minutes = 45, total_points = 100, passing_percentage = 70.00, is_published = 1 WHERE id = ?",
|
||||
[$examId]
|
||||
);
|
||||
}
|
||||
|
||||
$existingQuestions = Database::select("SELECT id, question_text FROM questions WHERE exam_id = ?", [$examId]);
|
||||
$hasCalculus = false;
|
||||
foreach ($existingQuestions as $eq) {
|
||||
if (str_contains($eq['question_text'], 'مشتق') || str_contains($eq['question_text'], "f'(x)")) {
|
||||
$hasCalculus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($existingQuestions) < 15 || $hasCalculus) {
|
||||
// Wipe legacy corrupt questions
|
||||
foreach ($existingQuestions as $oq) {
|
||||
Database::query("DELETE FROM question_options WHERE question_id = ?", [$oq['id']]);
|
||||
}
|
||||
Database::query("DELETE FROM questions WHERE exam_id = ?", [$examId]);
|
||||
|
||||
$bank = self::getUnit1QuestionBank();
|
||||
foreach ($bank as $qData) {
|
||||
$qId = (int)Database::insert(
|
||||
"INSERT INTO questions (uuid, exam_id, question_text, question_type, bloom_taxonomy, topic_tag, explanation_text, points)
|
||||
VALUES (UUID(), ?, ?, 'multiple_choice', ?, ?, ?, ?)",
|
||||
[$examId, $qData['question_text'], $qData['bloom'], $qData['topic'], $qData['explanation'], $qData['points']]
|
||||
);
|
||||
foreach ($qData['options'] as $idx => $optText) {
|
||||
Database::insert(
|
||||
"INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, ?, ?)",
|
||||
[$qId, $optText, ($idx === $qData['correct_index']) ? 1 : 0]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $examId;
|
||||
}
|
||||
|
||||
public static function getUnit1QuestionBank(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'question_text' => 'ما هي مجموعة حل المعادلة الحقيقية: x³ - 4x = 0؟',
|
||||
'bloom' => 'recall',
|
||||
'topic' => 'حل المعادلات بإخراج العامل المشترك الأكبر',
|
||||
'explanation' => 'بإخراج x كعامل مشترك: x(x² - 4) = 0 ومنها x(x - 2)(x + 2) = 0، فيكون الحل x = 0, 2, -2.',
|
||||
'points' => 6,
|
||||
'options' => ['{ -2, 0, 2 }', '{ 0, 4 }', '{ -2, 2 }', '{ 0, 2 }'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'حل المعادلة x⁴ - 5x² + 4 = 0 في مجموعة الأعداد الحقيقية هو:',
|
||||
'bloom' => 'comprehension',
|
||||
'topic' => 'المعادلات في الصورة التربيعية',
|
||||
'explanation' => 'بالتحليل كمعادلة تربيعية: (x² - 4)(x² - 1) = 0، ومنها x² = 4 أو x² = 1، فالجذور هي ±2 و ±1.',
|
||||
'points' => 7,
|
||||
'options' => ['{ -2, -1, 1, 2 }', '{ 1, 4 }', '{ -1, 1 }', '{ -4, 4 }'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'كم حلاً حقيقياً يحقق المعادلة: x³ + 8 = 0؟',
|
||||
'bloom' => 'comprehension',
|
||||
'topic' => 'تحليل مجموع المكعبين والحلول الحقيقية',
|
||||
'explanation' => 'بتحليل مجموع مكعبين: (x + 2)(x² - 2x + 4) = 0. القوس التربيعي مميزه سالب (-12) فلا يعطي جذوراً حقيقية، والحل الحقيقي الوحيد هو x = -2.',
|
||||
'points' => 6,
|
||||
'options' => ['حل حقيقي واحد فقط وهو x = -2', 'ثلاثة حلول حقيقية', 'حلّان حقيقيان', 'لا يوجد أي حل حقيقي'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'ما هو أقصى عدد ممكن لنقاط التقاطع بين مستقيم وقطع مكافئ في المستوى الإحداثي؟',
|
||||
'bloom' => 'recall',
|
||||
'topic' => 'التمثيل الهندسي لنظام خطي وتربيعي',
|
||||
'explanation' => 'المستقيم يقطع القطع المكافئ في نقطتين كحد أقصى (حلان)، أو يمسه في نقطة (حل واحد)، أو لا يقطعه (لا يوجد حل حقيقي).',
|
||||
'points' => 6,
|
||||
'options' => ['نقطتان كحد أقصى (حلّان حقيقيان)', 'ثلاث نقاط تقاطع', 'أربع نقاط تقاطع', 'نقطة واحدة فقط دائماً'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'إذا كان لدينا النظام: y = x + 1 و y = x² + 1، فما هي نقاط تقاطع المنحنيين؟',
|
||||
'bloom' => 'application',
|
||||
'topic' => 'حل نظام خطي وتربيعي بطريقة التعويض',
|
||||
'explanation' => 'بالمساواة: x² + 1 = x + 1 ومنها x² - x = 0 أي x(x - 1) = 0، فيكون x = 0 أو x = 1. بالتعويض نجد y = 1 أو y = 2.',
|
||||
'points' => 7,
|
||||
'options' => ['(0, 1) و (1, 2)', '(0, 0) و (1, 1)', '(1, 2) فقط', '(-1, 0) و (1, 2)'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'متى لا يوجد أي حل حقيقي لنظام مكوّن من معادلة خطية وأخرى تربيعية؟',
|
||||
'bloom' => 'analysis',
|
||||
'topic' => 'استخدام المميز لتحديد عدد حلول النظام',
|
||||
'explanation' => 'إذا كان مميز المعادلة التربيعية الناتجة عن التعويض سالباً (Δ = b² - 4ac < 0)، فإن المستقيم لا يتقاطع مع المنحنى.',
|
||||
'points' => 7,
|
||||
'options' => ['عندما يكون مميز المعادلة التربيعية الناتجة سالباً (Δ < 0)', 'عندما يكون المميز مساوياً لصفر', 'عندما يكون المميز موجباً تماماً', 'إذا كان ميل المستقيم يساوي صفراً'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'إذا كان لنظام مكوّن من مستقيم وقطع مكافئ حل حقيقي وحيد فقط، فإن المستقيم يعتبر:',
|
||||
'bloom' => 'comprehension',
|
||||
'topic' => 'المستقيم المماس لمنحنى تربيعي',
|
||||
'explanation' => 'وجود حل حقيقي وحيد لنظام خطي-تربيعي يعني هندسياً أن المستقيم يمس المنحنى عند نقطة واحدة فقط.',
|
||||
'points' => 6,
|
||||
'options' => ['مماساً لمنحنى القطع المكافئ عند نقطة التماس', 'قاطعاً للمنحنى في نقطتين', 'خط تقارب رأسي للمنحنى', 'محور تماثل للقطع المكافئ'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'في النظام: x² + y² = 25 و x² - y² = 7، ما هي قيمة x²؟',
|
||||
'bloom' => 'application',
|
||||
'topic' => 'حل نظام تربيعي بطريقة الحذف',
|
||||
'explanation' => 'بجمع المعادلتين طرفاً لطرف: 2x² = 32 ومنها x² = 16.',
|
||||
'points' => 7,
|
||||
'options' => ['x² = 16', 'x² = 9', 'x² = 32', 'x² = 18'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'ما هو أقصى عدد ممكن من نقاط التقاطع بين دائرة وقطع مكافئ في المستوى الإحداثي؟',
|
||||
'bloom' => 'comprehension',
|
||||
'topic' => 'التقاطع الهندسي بين منحنيين تربيعيين',
|
||||
'explanation' => 'يمكن لدائرة وقطع مكافئ أن يتقاطعا في 0، أو 1، أو 2، أو 3، أو 4 نقاط كحد أقصى.',
|
||||
'points' => 7,
|
||||
'options' => ['4 نقاط تقاطع (أربعة حلول)', 'حلان فقط', '6 نقاط تقاطع', 'حل وحيد فقط دائماً'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'النظام: x² + y² = 13 و y = x² + 1 يتقاطع في النقطتين:',
|
||||
'bloom' => 'application',
|
||||
'topic' => 'التعويض بين معادلتين تربيعيتين',
|
||||
'explanation' => 'بتعويض x² = y - 1 في الأولى: y - 1 + y² = 13 أي y² + y - 14 = 0 أو نجد بالنظام y=3 و x=±2.',
|
||||
'points' => 7,
|
||||
'options' => ['(-2, 3) و (2, 3)', '(3, -2) و (3, 2)', '(0, 1) فقط', '(2, 5) و (-2, 5)'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'إذا كانت النقطة (a, b) حلاً لنظام يتكون من دائرة مركزها نقطة الأصل ومعادلة متماثلة حول المحور الصادي، فإن:',
|
||||
'bloom' => 'analysis',
|
||||
'topic' => 'خصائص التناظر في أنظمة المعادلات',
|
||||
'explanation' => 'التماثل حول محور الصادات يعني أن استبدال x بـ (-x) يعطي نفس النتيجة، فالنقطة (-a, b) تكون أيضاً حلاً للنظام.',
|
||||
'points' => 7,
|
||||
'options' => ['(-a, b) تكون أيضاً حلاً للنظام', '(a, -b) هو الحل الوحيد دائماً', 'لا يوجد أي تناظر هندسي', 'الحل سالب دائماً'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'في برمجية جيوجبرا (GeoGebra)، ما هي الأداة المخصصة لإيجاد حل نظام المعادلات بيانياً؟',
|
||||
'bloom' => 'recall',
|
||||
'topic' => 'برمجية جيوجبرا وحل الأنظمة بيانياً',
|
||||
'explanation' => 'أداة التقاطع (Intersect Tool) في جيوجبرا تُحدد إحداثيات نقاط تقاطع المنحنيات التي تمثل حلول النظام مباشرة.',
|
||||
'points' => 6,
|
||||
'options' => ['أداة التقاطع (Intersect Tool)', 'أداة القياس (Measure)', 'أداة الانعكاس (Reflect)', 'أداة المماس (Tangent)'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'عند تمثيل معادلتين في جيوجبرا وظهور نقطة التقاطع A = (3, -2)، فهذا يعني هندسياً وجبرياً أن:',
|
||||
'bloom' => 'comprehension',
|
||||
'topic' => 'تفسير مخرجات برمجية جيوجبرا',
|
||||
'explanation' => 'إحداثيات نقطة التقاطع (x, y) تعني أن الزوج المرتب يحقق كلتا المعادلتين معاً في آن واحد، وهو حل النظام.',
|
||||
'points' => 6,
|
||||
'options' => ['x = 3 و y = -2 هو حل يحقق كلتا المعادلتين معاً', 'x = -2 و y = 3 هو الحل', 'المنحنيان متباعدان ولا حل لهما', 'النظام له حلول غير منتهية'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'لماذا يستخدم خبراء الأرصاد الجوية أنظمة معادلات غير خطية في التنبؤ بالطقس كما ورد في كتاب الطالب؟',
|
||||
'bloom' => 'comprehension',
|
||||
'topic' => 'التطبيقات الحياتية لأنظمة المعادلات',
|
||||
'explanation' => 'لأن أي تغير في أحد العوامل (كالضغط ودرجة الحرارة وسرعة الرياح) يؤدي إلى تغير غير خطي في العوامل الأخرى.',
|
||||
'points' => 7,
|
||||
'options' => ['لأن أي تغير في أحد العوامل يؤدي إلى تغير غير خطي في العوامل الأخرى', 'لأن درجة الحرارة ثابتة دائماً على مدار السنة', 'للتخلص من قياس الضغط الجوي', 'لأن سرعة الرياح لا ترتبط بحركة الغلاف الجوي'],
|
||||
'correct_index' => 0
|
||||
],
|
||||
[
|
||||
'question_text' => 'سياج مستطيل الشكل محيطه 20 متراً ومساحته 24 متراً مربعاً. ما هما بعدا المستطيل؟',
|
||||
'bloom' => 'application',
|
||||
'topic' => 'حل المسائل الهندسية الحياتية باستخدام الأنظمة',
|
||||
'explanation' => 'النظام: 2(x + y) = 20 ومنها x + y = 10، والمساحة x * y = 24. العددان اللذان مجموعهما 10 وحاصل ضربهما 24 هما 6 و 4.',
|
||||
'points' => 8,
|
||||
'options' => ['الطول 6 m والعرض 4 m', 'الطول 8 m والعرض 2 m', 'الطول 10 m والعرض 2.4 m', 'الطول 5 m والعرض 5 m'],
|
||||
'correct_index' => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,29 @@ class AiLessonEnhancerService
|
||||
محتوى الدرس: {$content}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an expert AI prompt to generate standalone HTML5/Canvas interactive lab simulations
|
||||
* for science and physics lessons.
|
||||
*/
|
||||
public static function buildSimulationPrompt(string $title, string $subject, string $content): string
|
||||
{
|
||||
return "أنت مهندس برمجيات تعليمية ومطور محاكيات فيزيائية تفاعلية (HTML5 Canvas Interactive Simulation Specialist).
|
||||
المطلوب: توليد كود صفحة HTML5 كاملة ومستقلة تماماً (تتضمن CSS مدمج وجافاسكربت نقي بدون أي مكتبات خارجية) لتمثيل تجربة معملية تفاعلية للموضوع التالي:
|
||||
|
||||
الموضوع: {$title}
|
||||
المادة: {$subject}
|
||||
المحتوى والنتاجات العلمية:
|
||||
{$content}
|
||||
|
||||
المعايير الصارمة للتوليد:
|
||||
1. التصميم: مظهر داكن فاخر (Dark OLED Theme: #06090f, #0d111a) بهوية أبل كوبرتينو وألوان صَقِل (Cyan #00f5d4, Amber #ffd166, Emerald #10b981).
|
||||
2. التفاعلية: استخدام HTML5 Canvas مع متحكمات انزلاقية (Sliders) تتيح للطالب تغيير المتغيرات الفيزيائية (الكتلة، القوة، الزاوية، السرعة...).
|
||||
3. الحسابات الحية: عرض النتائج الرياضية والمعادلات والقوانين فورياً مع تحديث الرسم البياني على الكانفاس في كل إطار.
|
||||
4. التحدي السقراطي: صندوق تحدي سقراطي يطلب من الطالب تجربة حالة خاصة (مثل: ماذا يحدث عند مضاعفة القوة أو جعل الزاوية 180 درجة؟).
|
||||
5. اللغة: واجهة عربية كاملة من اليمين إلى اليسار (dir='rtl').
|
||||
6. يجب أن يكون الناتج كود HTML خالص فقط صالح للتضمين الفوري داخل iframe أو WebView.";
|
||||
}
|
||||
|
||||
private static function callGemini(string $prompt): ?array
|
||||
{
|
||||
$apiKey = Env::get('GEMINI_API_KEY') ?: getenv('GEMINI_API_KEY');
|
||||
|
||||
Reference in New Issue
Block a user