Compare commits

...
2 Commits
5 changed files with 115 additions and 3 deletions
@@ -20,6 +20,7 @@ class AppColors {
static const Color appleBlue = Color(0xFF0071E3);
static const Color saqelCyan = Color(0xFF00F5D4);
static const Color guardianAmber = Color(0xFFF59E0B);
static const Color amberGold = Color(0xFFFFD166);
static const Color teacherEmerald = Color(0xFF10B981);
static const Color adminViolet = Color(0xFF8B5CF6);
static const Color whatsappGreen = Color(0xFF25D366);
@@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/utils/saqel_toast.dart';
import '../../../data/models/exam_model.dart';
import '../../../logic/cubits/exam_cubit.dart';
import '../../widgets/luxury_widgets.dart';
@@ -423,6 +423,29 @@ class VideoController
$lessonId = (int)$lesson['id'];
// Self-Healing Curriculum Guard: Purge any obsolete/mismatched calculus questions
// for non-calculus lessons (e.g. Grade 10 Systems of Equations)
$isCalculusLesson = (str_contains($lesson['title'], 'اشتقاق') || str_contains($lesson['title'], 'تفاضل'));
if (!$isCalculusLesson) {
try {
$mismatched = Database::selectOne(
"SELECT q.id FROM questions q
JOIN exams e ON q.exam_id = e.id
WHERE e.lesson_id = ? AND (q.question_text LIKE '%مشتق%' OR q.question_text LIKE '%f\'(x)%')
LIMIT 1",
[$lessonId]
);
if ($mismatched) {
$badExams = Database::select("SELECT id FROM exams WHERE lesson_id = ? AND scope = 'in_video_checkpoint'", [$lessonId]);
foreach ($badExams as $be) {
Database::query("DELETE FROM exams WHERE id = ?", [$be['id']]);
}
}
} catch (\Throwable $e) {
error_log("Curriculum self-healing notice: " . $e->getMessage());
}
}
// If lesson has no checkpoints or missing questions, generate them autonomously
$existingCount = Database::selectOne("SELECT COUNT(*) as cnt FROM exams WHERE lesson_id = ? AND scope = 'in_video_checkpoint'", [$lessonId]);
$existingQuestions = Database::selectOne("SELECT COUNT(*) as cnt FROM questions q JOIN exams e ON q.exam_id = e.id WHERE e.lesson_id = ?", [$lessonId]);
@@ -374,8 +374,47 @@ class AiVideoAnalyzerService
];
}
// Safe generic fallback: never inject unrelated advanced topics when
// Gemini is unavailable.
// Grade 10 Mathematics: Systems of Equations (أنظمة المعادلات)
$isEquations = (str_contains($title, 'معادلات') || str_contains($title, 'أنظمة') || str_contains($curriculum['unit'] ?? '', 'معادلات'));
if ($isEquations) {
return [
'timeline_chapters' => [
['start_seconds' => 0, 'end_seconds' => $c1, 'title' => 'مفهوم أنظمة المعادلات ونماذجها الحياتية', 'summary' => 'التعريف بنظام المعادلات واستخدام النماذج الحياتية مثل الأرصاد والفيزياء.'],
['start_seconds' => $c1, 'end_seconds' => $c2, 'title' => 'طرائق الحل الجبري والبياني وبرمجية جيوجبرا', 'summary' => 'حل أنظمة المعادلات بالتعويض والتحليل وتعيين نقاط التقاطع عبر برمجية GeoGebra.'],
['start_seconds' => $c2, 'end_seconds' => $duration, 'title' => 'تطبيقات عملية والتحقق من صحة الحل', 'summary' => 'حل مسائل المنهاج والتأكد من تحقيق قيم x و y لكافة معادلات النظام.']
],
'socratic_checkpoints' => [
[
'timestamp_seconds' => $cp1,
'question_text' => 'ماذا تمثّل نقطة تقاطع منحنيين في المستوى الإحداثي لنظام معادلات؟',
'options' => [
'حل مشترك يُحقق كِلا المعادلتين معاً في آن واحد',
'المسافة الرأسية بين المنحنيين',
'معادلة خط التقارب الأفقي فقط',
'المقطع الصادي لأحد المنحنيين دون الآخر'
],
'correct_index' => 0,
'rewind_seconds' => $rewind,
'explanation' => 'نقطة تقاطع أي منحنيين هندسياً تمثل الزوج المرتب (x, y) الذي يحقق كلتا المعادلتين في آن واحد، وهو حل النظام.'
],
[
'timestamp_seconds' => $cp2,
'question_text' => 'عند حل نظام مكوّن من معادلة خطية ومعادلة تربيعية، ما هو أقصى عدد ممكن من الحلول الحقيقية؟',
'options' => [
'حلّان حقيقيان (نقطتا تقاطع)',
'ثلاثة حلول حقيقية دائماً',
'أربعة حلول حقيقية',
'لا يمكن أن يتقاطعا أبداً'
],
'correct_index' => 0,
'rewind_seconds' => $rewind,
'explanation' => 'المستقيم يقطع القطع المكافئ في نقطتين كحد أقصى (حلان)، أو يمسه في نقطة واحدة (حل واحد)، أو لا يقطعه (لا يوجد حل حقيقي).'
]
]
];
}
// Safe generic fallback: never inject unrelated advanced topics when Gemini is unavailable.
$topics = array_values(array_filter(array_map('strval', $curriculum['core_topics'] ?? [])));
$topic = $topics[0] ?? $title;
$topicTwo = $topics[1] ?? $topic;
@@ -0,0 +1,50 @@
<?php
/**
* Saqel Platform — Self-Healing Socratic Checkpoints Migration & Cleanup Script
* Scans all lessons in the database and cleans up any legacy Tawjihi Calculus
* checkpoints attached to Grade 10 lessons (e.g. Systems of Equations).
*/
require_once __DIR__ . '/../app/bootstrap.php';
use App\Core\Database;
use App\Services\AiVideoAnalyzerService;
echo "======================================================================\n";
echo "🧹 SAQEL CURRICULUM CHECKPOINT AUDIT & REPAIR ENGINE\n";
echo "======================================================================\n\n";
try {
// Find all lessons with mismatched calculus questions in non-calculus lessons
$mismatchedLessons = Database::select(
"SELECT DISTINCT l.id, l.title
FROM lessons l
JOIN exams e ON e.lesson_id = l.id
JOIN questions q ON q.exam_id = e.id
WHERE (q.question_text LIKE '%مشتق%' OR q.question_text LIKE '%f\'(x)%' OR q.question_text LIKE '%sin(3x)%')
AND l.title NOT LIKE '%اشتقاق%' AND l.title NOT LIKE '%تفاضل%'"
);
if (empty($mismatchedLessons)) {
echo "✅ No mismatched calculus checkpoints found. All lessons are curriculum-grounded!\n";
} else {
echo "⚠️ Found " . count($mismatchedLessons) . " lesson(s) with obsolete calculus checkpoints:\n";
foreach ($mismatchedLessons as $les) {
echo " • Repairing Lesson [ID: {$les['id']}]: '{$les['title']}'...\n";
// Delete corrupt exam checkpoints
$badExams = Database::select("SELECT id FROM exams WHERE lesson_id = ? AND scope = 'in_video_checkpoint'", [$les['id']]);
foreach ($badExams as $be) {
Database::query("DELETE FROM exams WHERE id = ?", [$be['id']]);
}
// Re-generate fresh, pristine, Grade 10 curriculum checkpoints
AiVideoAnalyzerService::processLessonAutonomously((int)$les['id']);
echo " ✓ Successfully regenerated authentic Grade 10 curriculum questions!\n";
}
}
echo "\n🎉 Curriculum audit & cleanup completed successfully!\n";
} catch (\Throwable $e) {
echo "❌ Error during cleanup: " . $e->getMessage() . "\n";
}