Update Saqel Platform: 2026-09-08 13:43:36

This commit is contained in:
Hamza-Ayed
2026-09-08 13:43:36 +03:00
parent 0f424062fe
commit bb37b31cab
76 changed files with 3617 additions and 3059 deletions
+92 -93
View File
@@ -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"
. "للتفاصيل ادخل إلى تطبيق صَقِل بحساب ولي الأمر.";
}
}