$q) { $qA = $q; $qA['question_number'] = $idx + 1; $formAQuestions[] = $qA; // Perturb for Form B (shuffled options + slightly varied numbers) $qB = $q; $qB['question_number'] = $idx + 1; // If math/physics, apply number perturbation if (isset($q['is_numerical']) && $q['is_numerical']) { $qB['question_text'] = str_replace(['20', '4', '5'], ['30', '6', '5'], $q['question_text']); } // Shuffle options for Form B $opts = $qB['options']; $correctText = $opts[$qB['correct_index']]; shuffle($opts); $qB['options'] = $opts; $qB['correct_index'] = array_search($correctText, $opts); $formBQuestions[] = $qB; } // Shuffle question sequence in Form B shuffle($formBQuestions); foreach ($formBQuestions as $newIdx => &$qItem) { $qItem['question_number'] = $newIdx + 1; } return [ 'exam_uuid' => $examUuid, 'subject' => $subject, 'grade_level' => $gradeLevel, 'forms' => [ 'form_a' => [ 'form_code' => 'FORM_A_ALPHA', 'barcode' => 'SAQEL-EXAM-A-' . substr($examUuid, 0, 8), 'questions' => $formAQuestions, 'total_score' => 100, 'objective_score' => 70, 'written_steps_score' => 30, ], 'form_b' => [ 'form_code' => 'FORM_B_BETA', 'barcode' => 'SAQEL-EXAM-B-' . substr($examUuid, 0, 8), 'questions' => $formBQuestions, 'total_score' => 100, 'objective_score' => 70, 'written_steps_score' => 30, ] ], 'table_of_specifications' => [ 'remembering' => '20%', 'understanding' => '30%', 'application' => '35%', 'higher_order' => '15%', ] ]; } /** * كشف الشذوذ الإحصائي ومكافحة الغش (خوارزمية الذكاء الإحصائي) */ public static function evaluateExamSessionIntegrity(array $studentSubmissions): array { $anomalies = []; $errorClusteringMap = []; foreach ($studentSubmissions as $submission) { $studentId = $submission['student_id']; $studentName = $submission['student_name']; $seatNumber = $submission['seat_number'] ?? 'قاعة 1'; $timeSpentSeconds = $submission['time_spent_seconds'] ?? 1800; $score = $submission['score'] ?? 0; $answers = $submission['answers'] ?? []; // Map question_id => selected_option // 1. Impossible Speed Check (مؤشر السرعة المستحيلة) // If solving 20 complex questions in less than 300 seconds (<15s per question) with score > 85% if ($timeSpentSeconds < 300 && $score >= 85) { $anomalies[] = [ 'type' => 'impossible_speed', 'severity' => 'critical', 'title' => 'مؤشر السرعة المستحيلة (Impossible Speed)', 'student_id' => $studentId, 'student_name' => $studentName, 'seat_number' => $seatNumber, 'details' => "أنهى الطالب الامتحان في {$timeSpentSeconds} ثانية فقط بمعدل 12 ثانية لكل مسألة تفاضل وحصل على {$score}%، وهو ما يتجاوز سرعة القراءة البشرية المجردة.", 'time_spent' => "{$timeSpentSeconds} ثانية", 'recommended_action' => 'استعراض التسجيل البانورامي للقاعة في الدقيقة 02:40 والتحقق من جهاز الطالب.' ]; } // 2. Historical Leap Check (القفزة التاريخية المفاجئة) $historicalAverage = $submission['historical_average'] ?? 45.0; if (($score - $historicalAverage) >= 45.0 && $timeSpentSeconds < 900) { $anomalies[] = [ 'type' => 'historical_leap', 'severity' => 'warning', 'title' => 'قفزة المعدل التاريخية المفاجئة (Historical Leap)', 'student_id' => $studentId, 'student_name' => $studentName, 'seat_number' => $seatNumber, 'details' => "قفز تحصيل الطالب من معدل تراكمي ({$historicalAverage}%) إلى ({$score}%) في امتحان وزاري موحد، مع إنهاء مبكر للامتحان.", 'recommended_action' => 'مطابقة ورقة الخطوات الإنشائية الورقية بخط يد الطالب مع الإجابات المدخلة.' ]; } // Track identical wrong answers for clustering check foreach ($answers as $qId => $ans) { if (isset($ans['is_correct']) && !$ans['is_correct']) { $key = "q_{$qId}_ans_{$ans['selected_option']}"; $errorClusteringMap[$key][] = [ 'student_id' => $studentId, 'student_name' => $studentName, 'seat_number' => $seatNumber, ]; } } } // 3. Error Clustering Check (مؤشر تكتل الأخطاء المتطابقة) // If 2 or more adjacent students make the exact same obscure wrong choices foreach ($errorClusteringMap as $key => $students) { if (count($students) >= 2) { $names = array_column($students, 'student_name'); $seats = array_column($students, 'seat_number'); $anomalies[] = [ 'type' => 'error_clustering', 'severity' => 'critical', 'title' => 'تكتل الأخطاء المتطابقة (Identical Error Clustering)', 'student_name' => implode(' و ', $names), 'seat_number' => implode(' و ', $seats), 'details' => "تطابق غريب في اختيار نفس الخيار الخاطئ النادر في 3 مسائل حسابية معقدة بين مقاعد متجاورة.", 'recommended_action' => 'الرجوع فوراً للقطات الكاميرا البانورامية للمقاعد المذكورة.' ]; } } return [ 'status' => 'success', 'anomalies_detected' => count($anomalies), 'integrity_score' => max(100 - (count($anomalies) * 15), 40), 'anomalies' => $anomalies, ]; } private static function getCurriculumQuestionPool(string $subject): array { return [ [ 'question_text' => 'أثرت قوة أفقية مقدارها 20 نيوتن على جسم كتلته 4 كغ على سطح أملس. ما تسارع الجسم؟', 'options' => ['5 م/ث²', '80 م/ث²', '0.2 م/ث²', '16 م/ث²'], 'correct_index' => 0, 'is_numerical' => true, 'bloom_level' => 'تطبيق', ], [ 'question_text' => 'متجهان A و B مقدار كل منهما 6 وحدات والزاوية بينهما 90 درجة، حاصل ضربهما القياسي يساوي:', 'options' => ['صفر', '36 وحدة', '18 وحدة', '6 وحدات'], 'correct_index' => 0, 'is_numerical' => false, 'bloom_level' => 'فهم', ], [ 'question_text' => 'ما هو التفسير الفيزيائي لاندفاع الراكب إلى الأمام عند توقف الحافلة فجأة؟', 'options' => ['القصور الذاتي ومقاومة التغير في الحالة الحركية', 'زيادة قوة الاحتكاك', 'نقصان تسارع الجاذبية', 'تأثير قوة الدفع العكسية'], 'correct_index' => 0, 'is_numerical' => false, 'bloom_level' => 'فهم واستنتاج', ], [ 'question_text' => 'إذا تضاعفت سرعة سيارة متحركة إلى المثلين، فإن طاقتها الحركية (KE):', 'options' => ['تتضاعف 4 مرات', 'تتضاعف مرتين فقط', 'تبقى ثابتة', 'تقل إلى النصف'], 'correct_index' => 0, 'is_numerical' => true, 'bloom_level' => 'تحليل وتفكير عليا', ], ]; } }