Update Saqel Platform: 2026-09-08 13:43:36
This commit is contained in:
@@ -3,116 +3,115 @@
|
||||
namespace App\Services;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Core\Security;
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* SAQEL ENTERPRISE (EDTECH 2.0) - MONTHLY PARENT REPORT DISPATCHER
|
||||
* ==============================================================================
|
||||
*
|
||||
* ملف: ParentReportService.php
|
||||
* الهدف المعماري:
|
||||
* توليد وإرسال بطاقات الأداء الرقمية الشهرية لأولياء الأمور عبر الواتساب وبوابة نبيه:
|
||||
* 1. حصر نسبة الحضور ومشاهدة الحصص الرقمية.
|
||||
* 2. عدد المهارات العلاجية المتقنة من دفتر الأخطاء الذكي.
|
||||
* 3. نتائج الامتحانات الموحدة ومستوى الجاهزية للتوجيهي.
|
||||
* 4. رابط رقمي مؤمّن ومباشر لولي الأمر.
|
||||
*/
|
||||
class ParentReportService
|
||||
{
|
||||
/**
|
||||
* توليد ومضة التقرير الشهري للطالب
|
||||
*/
|
||||
public static function compileMonthlyReport(int $studentId): array
|
||||
{
|
||||
// Sample student data or from DB
|
||||
$student = [
|
||||
'id' => $studentId,
|
||||
'full_name' => 'زيد حمزة الغويري',
|
||||
'school_name' => 'مدرسة الثقافة العسكرية الثانوية للبنين - الزرقاء',
|
||||
'grade_level' => 'الأول ثانوي العلمي (توجيهي 2008)',
|
||||
'guardian_phone'=> '0798583052',
|
||||
'month' => 'آب / أيلول 2026',
|
||||
];
|
||||
|
||||
$reportMetrics = [
|
||||
'attendance_rate' => '96%',
|
||||
'lessons_completed' => 28,
|
||||
'socratic_engagement' => '94%',
|
||||
'remediation_mastery' => '8 من أصل 10 فجوات معرفية تم شفاؤها وإتقانها 🏆',
|
||||
'unified_exam_score' => '88 / 100 (مستوى ممتاز)',
|
||||
'general_readiness' => '91.5%',
|
||||
'portal_magic_link' => 'https://saqel.intaleqapp.com/guardian/report?token=sec_' . bin2hex(random_bytes(8)),
|
||||
];
|
||||
|
||||
$formattedWhatsAppMessage = self::buildWhatsAppMessage($student, $reportMetrics);
|
||||
|
||||
return [
|
||||
'student' => $student,
|
||||
'metrics' => $reportMetrics,
|
||||
'whatsapp_message' => $formattedWhatsAppMessage,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* إرسال التقرير الشهري الفوري عبر بوابة نبيه
|
||||
*/
|
||||
public static function dispatchReportToGuardian(int $studentId): array
|
||||
{
|
||||
$report = self::compileMonthlyReport($studentId);
|
||||
$phone = $report['student']['guardian_phone'];
|
||||
$message = $report['whatsapp_message'];
|
||||
|
||||
// Dispatch via NabehService if available
|
||||
$dispatchStatus = 'dispatched_successfully';
|
||||
try {
|
||||
// NabehService::sendWhatsAppMessage($phone, $message);
|
||||
} catch (\Throwable $e) {
|
||||
$dispatchStatus = 'queued_local';
|
||||
$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('لا يوجد ولي أمر مخوّل ومرتبط بهذا الطالب');
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'message' => 'تم إرسال ومضة التقرير الشهري لولي الأمر بنجاح عبر الواتساب 📲',
|
||||
'recipient' => $phone,
|
||||
'dispatch_status'=> $dispatchStatus,
|
||||
'preview' => $message,
|
||||
'dispatched_at' => date('Y-m-d H:i:s'),
|
||||
$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
|
||||
{
|
||||
// Batch simulated dispatch
|
||||
$studentsCount = 450;
|
||||
$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' => 'success',
|
||||
'message' => "تمت جدولة وبث {$studentsCount} تقرير شهري لأولياء أمور طلبة المدرسة بنجاح عبر بوابة نبيه",
|
||||
'school_id' => $schoolId,
|
||||
'total_dispatched' => $studentsCount,
|
||||
'failed_count' => 0,
|
||||
'delivery_rate' => '100%',
|
||||
'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
|
||||
{
|
||||
return "🇯🇴 *تقرير التحصيل الأكاديمي الشهري — منصة صَقِل* 🇯🇴\n" .
|
||||
"مديرية التربية والتعليم والثقافة العسكرية\n\n" .
|
||||
"حضرة ولي أمر الطالب: *{$student['full_name']}* المحترم\n" .
|
||||
"المدرسة: {$student['school_name']}\n" .
|
||||
"المرحلة: {$student['grade_level']}\n" .
|
||||
"عن شهر: {$student['month']}\n\n" .
|
||||
"📊 *ملخص الإنجاز والجاهزية الأكاديمية:*\n" .
|
||||
"• نسبة الالتزام بالحضور: {$metrics['attendance_rate']}\n" .
|
||||
"• الحصص المنجزة: {$metrics['lessons_completed']} حصة\n" .
|
||||
"• دفتر الأخطاء الذكي: {$metrics['remediation_mastery']}\n" .
|
||||
"• نتيجة الامتحان الموحد الأخير: {$metrics['unified_exam_score']}\n" .
|
||||
"• مؤشر الجاهزية للتوجيهي: {$metrics['general_readiness']}\n\n" .
|
||||
"🔗 للاطلاع على كشف التفاصيل والمسارات العلاجية المنفذة:\n" .
|
||||
"{$metrics['portal_magic_link']}\n\n" .
|
||||
"_صَقِل: شراكة وطنية لترسيخ التميز الأكاديمي والسيادة الرقمية._";
|
||||
$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"
|
||||
. "للتفاصيل ادخل إلى تطبيق صَقِل بحساب ولي الأمر.";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user