Complete Socratic adaptive exams in Flutter, Guardian WhatsApp alerts, and curriculum robustness
This commit is contained in:
@@ -85,6 +85,7 @@ class ExamController
|
||||
*/
|
||||
public function submitExam(Request $request, Response $response): void
|
||||
{
|
||||
self::ensureAttemptSchema();
|
||||
$studentId = $request->user_id;
|
||||
$examId = (int)$request->getParam('id');
|
||||
$body = $request->getBody();
|
||||
@@ -213,6 +214,40 @@ class ExamController
|
||||
updated_at = NOW()
|
||||
", [$studentId, $courseId, $subjectId, $avgMastery, $tawjihiIndex, $passedExams, $totalExams]);
|
||||
|
||||
// Trigger Guardian Alert via Nabeh WhatsApp if remediation needed
|
||||
if (!$passed && !empty($weakTopics)) {
|
||||
try {
|
||||
$studentInfo = Database::selectOne(
|
||||
"SELECT s.full_name, sub.name AS subject_name
|
||||
FROM students s
|
||||
LEFT JOIN courses c ON c.id = ?
|
||||
LEFT JOIN subjects sub ON sub.id = c.subject_id
|
||||
WHERE s.id = ? LIMIT 1",
|
||||
[$courseId, $studentId]
|
||||
);
|
||||
$guardian = Database::selectOne(
|
||||
"SELECT ai.phone_number
|
||||
FROM guardian_students gs
|
||||
JOIN guardians g ON g.id = gs.guardian_id
|
||||
JOIN auth_identities ai ON ai.id = g.auth_identity_id
|
||||
WHERE gs.student_id = ? LIMIT 1",
|
||||
[$studentId]
|
||||
);
|
||||
if ($guardian && !empty($guardian['phone_number'])) {
|
||||
$nabeh = new \App\Services\NabehService();
|
||||
$weakTopicStr = implode('، ', array_keys($weakTopics));
|
||||
$nabeh->sendGuardianRemedialAlert(
|
||||
(string)$guardian['phone_number'],
|
||||
$studentInfo['full_name'] ?? 'الطالب',
|
||||
$studentInfo['subject_name'] ?? 'المبحث المقرر',
|
||||
$weakTopicStr
|
||||
);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
error_log("Guardian remedial alert notice: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$response->json([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
@@ -230,6 +265,23 @@ class ExamController
|
||||
]);
|
||||
}
|
||||
|
||||
/** Additive production migration for installations created before completed_at. */
|
||||
private static function ensureAttemptSchema(): void
|
||||
{
|
||||
try {
|
||||
$column = Database::selectOne(
|
||||
"SELECT COUNT(*) AS cnt FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'exam_attempts'
|
||||
AND COLUMN_NAME = 'completed_at' LIMIT 1"
|
||||
);
|
||||
if (empty($column['cnt'])) {
|
||||
Database::query("ALTER TABLE exam_attempts ADD COLUMN completed_at TIMESTAMP NULL DEFAULT NULL AFTER ai_diagnostic_report");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
error_log('Exam schema migration note: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Student Mastery & Tawjihi Readiness Analytics
|
||||
* GET /api/student/progress/mastery?course_id=1
|
||||
|
||||
Reference in New Issue
Block a user