118 lines
5.7 KiB
PHP
118 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Core\Database;
|
|
use App\Core\Security;
|
|
|
|
class ParentReportService
|
|
{
|
|
public static function compileMonthlyReport(int $studentId): array
|
|
{
|
|
$student = Database::selectOne(
|
|
"SELECT s.id, s.full_name, s.grade_level, s.stream, sc.name AS school_name,
|
|
ai.phone_number AS guardian_phone
|
|
FROM students s
|
|
LEFT JOIN schools sc ON sc.id = s.school_id
|
|
JOIN guardian_students gs ON gs.student_id = s.id AND gs.can_view_analytics = 1
|
|
JOIN guardians g ON g.id = gs.guardian_id
|
|
JOIN auth_identities ai ON ai.id = g.identity_id AND ai.status = 'active'
|
|
WHERE s.id = ? ORDER BY gs.id LIMIT 1",
|
|
[$studentId]
|
|
);
|
|
if (!$student) {
|
|
throw new \RuntimeException('لا يوجد ولي أمر مخوّل ومرتبط بهذا الطالب');
|
|
}
|
|
|
|
$learning = Database::selectOne(
|
|
"SELECT COUNT(*) AS lessons_started,
|
|
SUM(CASE WHEN is_completed = 1 THEN 1 ELSE 0 END) AS lessons_completed,
|
|
COALESCE(AVG(completion_percentage), 0) AS average_completion
|
|
FROM lesson_progress WHERE student_id = ?",
|
|
[$studentId]
|
|
);
|
|
$mastery = Database::selectOne(
|
|
"SELECT COALESCE(AVG(tawjihi_readiness_score), 0) AS readiness,
|
|
COALESCE(SUM(exams_passed_count), 0) AS exams_passed,
|
|
COALESCE(SUM(exams_total_count), 0) AS exams_total
|
|
FROM student_mastery_analytics WHERE student_id = ?",
|
|
[$studentId]
|
|
);
|
|
$errors = Database::selectOne(
|
|
"SELECT SUM(CASE WHEN status = 'mastered' THEN 1 ELSE 0 END) AS mastered,
|
|
COUNT(*) AS total_errors FROM student_error_notebook WHERE student_id = ?",
|
|
[$studentId]
|
|
);
|
|
$latestExam = Database::selectOne(
|
|
"SELECT percentage FROM exam_attempts WHERE student_id = ? AND completed_at IS NOT NULL ORDER BY completed_at DESC LIMIT 1",
|
|
[$studentId]
|
|
);
|
|
|
|
$student['guardian_phone'] = Security::decrypt((string)$student['guardian_phone']);
|
|
$student['month'] = date('Y-m');
|
|
$metrics = [
|
|
'average_lesson_completion' => round((float)($learning['average_completion'] ?? 0), 2),
|
|
'lessons_completed' => (int)($learning['lessons_completed'] ?? 0),
|
|
'lessons_started' => (int)($learning['lessons_started'] ?? 0),
|
|
'remediation_mastered' => (int)($errors['mastered'] ?? 0),
|
|
'remediation_total' => (int)($errors['total_errors'] ?? 0),
|
|
'latest_exam_score' => $latestExam ? (float)$latestExam['percentage'] : null,
|
|
'exams_passed' => (int)($mastery['exams_passed'] ?? 0),
|
|
'exams_total' => (int)($mastery['exams_total'] ?? 0),
|
|
'general_readiness' => round((float)($mastery['readiness'] ?? 0), 2),
|
|
];
|
|
|
|
return ['student' => $student, 'metrics' => $metrics, 'whatsapp_message' => self::buildWhatsAppMessage($student, $metrics)];
|
|
}
|
|
|
|
public static function dispatchReportToGuardian(int $studentId): array
|
|
{
|
|
try {
|
|
$report = self::compileMonthlyReport($studentId);
|
|
$result = (new NabehService())->sendMessage($report['student']['guardian_phone'], $report['whatsapp_message']);
|
|
if (!($result['success'] ?? false)) {
|
|
return ['status' => 'error', 'message' => $result['error'] ?? 'تعذر إرسال التقرير عبر نبيه'];
|
|
}
|
|
return ['status' => 'success', 'message' => 'تم إرسال التقرير الفعلي لولي الأمر', 'dispatched_at' => date('Y-m-d H:i:s')];
|
|
} catch (\Throwable $e) {
|
|
return ['status' => 'error', 'message' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
public static function dispatchBatchReports(int $schoolId): array
|
|
{
|
|
$students = Database::select(
|
|
"SELECT DISTINCT s.id FROM students s JOIN guardian_students gs ON gs.student_id = s.id AND gs.can_view_analytics = 1 WHERE s.school_id = ?",
|
|
[$schoolId]
|
|
);
|
|
$sent = 0;
|
|
$failed = 0;
|
|
foreach ($students as $student) {
|
|
$result = self::dispatchReportToGuardian((int)$student['id']);
|
|
($result['status'] ?? 'error') === 'success' ? $sent++ : $failed++;
|
|
}
|
|
return [
|
|
'status' => $failed === 0 ? 'success' : 'partial',
|
|
'school_id' => $schoolId,
|
|
'total_dispatched' => $sent,
|
|
'failed_count' => $failed,
|
|
'total_eligible' => count($students),
|
|
];
|
|
}
|
|
|
|
private static function buildWhatsAppMessage(array $student, array $metrics): string
|
|
{
|
|
$latestExam = $metrics['latest_exam_score'] === null ? 'لا توجد محاولة مكتملة' : $metrics['latest_exam_score'] . '%';
|
|
return "تقرير صَقِل الشهري — {$student['month']}\n"
|
|
. "الطالب: {$student['full_name']}\n"
|
|
. "المدرسة: " . ($student['school_name'] ?: 'غير مرتبطة') . "\n"
|
|
. "الصف: {$student['grade_level']} / {$student['stream']}\n\n"
|
|
. "الحصص المكتملة: {$metrics['lessons_completed']} من {$metrics['lessons_started']}\n"
|
|
. "متوسط إكمال الحصص: {$metrics['average_lesson_completion']}%\n"
|
|
. "المعالجات المتقنة: {$metrics['remediation_mastered']} من {$metrics['remediation_total']}\n"
|
|
. "آخر امتحان: {$latestExam}\n"
|
|
. "مؤشر الجاهزية: {$metrics['general_readiness']}%\n\n"
|
|
. "للتفاصيل ادخل إلى تطبيق صَقِل بحساب ولي الأمر.";
|
|
}
|
|
}
|