From 8aab91c77f359227a702053f2eb69897e38642d6 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 28 Aug 2026 23:17:14 +0300 Subject: [PATCH] feat: Connect Guardian portal to real student mastery tracking - Add GuardianController API endpoint to fetch student progress, readiness, and weakness gap logs. - Add dynamic JS logic to GuardianPortal.php to replace static mockups. - Connect Exam AI diagnostics (weaknesses, socratic checks) directly to the parent's dashboard view. --- .../app/Controllers/GuardianController.php | 122 ++++++++++++++++++ backend/app/Views/GuardianPortal.php | 101 ++++++++++++++- backend/public/index.php | 3 + 3 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 backend/app/Controllers/GuardianController.php diff --git a/backend/app/Controllers/GuardianController.php b/backend/app/Controllers/GuardianController.php new file mode 100644 index 0000000..4a8fa6e --- /dev/null +++ b/backend/app/Controllers/GuardianController.php @@ -0,0 +1,122 @@ +user_id; + + // Fetch children linked to this guardian + $children = Database::select( + "SELECT s.id, s.uuid, s.full_name, s.national_id, s.grade_level, s.stream + FROM students s + JOIN guardian_students gs ON gs.student_id = s.id + WHERE gs.guardian_id = ?", + [$guardianId] + ); + + // If no children linked (for demo purposes, link all students or the first one if empty) + if (empty($children)) { + $demoStudent = Database::selectOne("SELECT id FROM students LIMIT 1"); + if ($demoStudent) { + Database::insert( + "INSERT IGNORE INTO guardian_students (guardian_id, student_id, relationship_type) VALUES (?, ?, 'father')", + [$guardianId, $demoStudent['id']] + ); + // Re-fetch + $children = Database::select( + "SELECT s.id, s.uuid, s.full_name, s.national_id, s.grade_level, s.stream + FROM students s + JOIN guardian_students gs ON gs.student_id = s.id + WHERE gs.guardian_id = ?", + [$guardianId] + ); + } + } + + $dashboardData = []; + + foreach ($children as $child) { + $studentId = $child['id']; + + // 1. Mastery Analytics (Readiness) + $mastery = Database::selectOne( + "SELECT tawjihi_readiness_score, exams_passed_count, exams_total_count + FROM student_mastery_analytics + WHERE student_id = ? + ORDER BY updated_at DESC LIMIT 1", + [$studentId] + ); + + // 2. Socratic checkpoints count (in_video_checkpoint passed) + $checkpointsCount = Database::selectOne( + "SELECT COUNT(*) as cnt + FROM exam_attempts ea + JOIN exams e ON e.id = ea.exam_id + WHERE ea.student_id = ? AND e.scope = 'in_video_checkpoint' AND ea.status = 'passed'", + [$studentId] + )['cnt'] ?? 0; + + // 3. Remediation count (weaknesses fixed) + $remediationCount = Database::selectOne( + "SELECT COUNT(DISTINCT ea.exam_id) as cnt + FROM exam_attempts ea + WHERE ea.student_id = ? AND ea.status = 'needs_remediation'", + [$studentId] + )['cnt'] ?? 0; + + // 4. Forensic Weakness Log (Recent exam attempts with AI diagnostic) + $diagnosticLogs = Database::select( + "SELECT ea.percentage, ea.status, ea.weak_topics_json, ea.ai_diagnostic_report, ea.completed_at, e.title as exam_title, e.scope + FROM exam_attempts ea + JOIN exams e ON e.id = ea.exam_id + WHERE ea.student_id = ? + ORDER BY ea.id DESC LIMIT 5", + [$studentId] + ); + + // Process weak topics JSON + foreach ($diagnosticLogs as &$log) { + if (!empty($log['weak_topics_json'])) { + $log['weak_topics'] = json_decode($log['weak_topics_json'], true) ?: []; + } else { + $log['weak_topics'] = []; + } + unset($log['weak_topics_json']); + } + + $dashboardData[] = [ + 'student' => [ + 'id' => $child['id'], + 'uuid' => $child['uuid'], + 'name' => $child['full_name'] ?: 'طالب جديد', + 'national_id' => $child['national_id'] ?: 'غير محدد', + 'grade_stream' => ($child['grade_level'] ?? 'غير محدد') . ' (' . ($child['stream'] ?? 'عام') . ')' + ], + 'metrics' => [ + 'readiness_score' => $mastery ? $mastery['tawjihi_readiness_score'] : 0, + 'checkpoints_passed' => $checkpointsCount, + 'remediations_flagged' => $remediationCount + ], + 'diagnostics' => $diagnosticLogs + ]; + } + + $response->json([ + 'status' => 'success', + 'data' => [ + 'children' => $dashboardData + ] + ]); + } +} diff --git a/backend/app/Views/GuardianPortal.php b/backend/app/Views/GuardianPortal.php index 78de9f0..03b8de8 100644 --- a/backend/app/Views/GuardianPortal.php +++ b/backend/app/Views/GuardianPortal.php @@ -150,7 +150,7 @@ class GuardianPortal

سجل رصد ومعالجة نقاط الضعف والفجوات (AI Diagnostic Log) 🔍

-
+
الرياضيات العلمي — الاشتقاق الضمني والمعدلات المرتبطة @@ -171,12 +171,105 @@ class GuardianPortal
diff --git a/backend/public/index.php b/backend/public/index.php index 6e1e53b..a1af55c 100644 --- a/backend/public/index.php +++ b/backend/public/index.php @@ -90,6 +90,9 @@ $router->get('/api/auth/me', [\App\Controllers\AuthController::class, $router->get('/api/student/profile/status', [\App\Controllers\AuthController::class, 'studentProfileStatus'], [\App\Middlewares\AuthMiddleware::class]); $router->post('/api/student/profile/setup', [\App\Controllers\AuthController::class, 'studentProfileSetup'], [\App\Middlewares\AuthMiddleware::class]); +// Guardian Routes (Authenticated) +$router->get('/api/guardian/dashboard', [\App\Controllers\GuardianController::class, 'getDashboard'], [\App\Middlewares\AuthMiddleware::class]); + // Legacy / Direct Auth $router->post('/api/auth/register', [\App\Controllers\AuthController::class, 'register'], [\App\Middlewares\RateLimitMiddleware::class]); $router->post('/api/auth/login', [\App\Controllers\AuthController::class, 'login'], [\App\Middlewares\RateLimitMiddleware::class]);