From b80640c59c2bcaf966d5f083e74a4bb30b1f4bdd Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 17 Apr 2026 06:59:52 +0000 Subject: [PATCH] adding centers assessment --- assets/css/custom.css | 59 +++++- includes/app.php | 2 +- includes/cycles.php | 48 +++-- student_completion_certificates.php | 299 ++++++++++++++++++++++++++++ students.php | 17 +- 5 files changed, 400 insertions(+), 25 deletions(-) create mode 100644 student_completion_certificates.php diff --git a/assets/css/custom.css b/assets/css/custom.css index dfb57b7..49ea438 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -21,7 +21,7 @@ --radius-sm: 10px; --radius-md: 14px; --radius-lg: 18px; - --font-ui: "IBM Plex Sans Arabic", "Segoe UI", Tahoma, Arial, sans-serif; + --font-ui: "Cairo", "Segoe UI", Tahoma, Arial, sans-serif; } * { @@ -1662,8 +1662,11 @@ textarea.form-control { html, body { - width: 297mm; - min-height: 210mm; + margin: 0 !important; + padding: 0 !important; + width: auto !important; + min-height: auto !important; + height: auto !important; background: #fff !important; } @@ -1672,6 +1675,12 @@ textarea.form-control { print-color-adjust: exact; } + .app-shell, + main { + margin: 0 !important; + padding: 0 !important; + } + .site-header, .site-footer, .certificate-toolbar, @@ -1704,9 +1713,9 @@ textarea.form-control { .completion-certificate-card { border: 2px solid rgba(180, 146, 42, 0.62); border-radius: 1rem; - height: 190mm; - min-height: 190mm; - max-height: 190mm; + height: 188mm; + min-height: 188mm; + max-height: 188mm; overflow: hidden; page-break-inside: avoid; break-inside: avoid-page; @@ -1898,3 +1907,41 @@ textarea.form-control { } } +.completion-batch-page .certificate-toolbar { + position: sticky; + top: 1rem; + z-index: 5; +} + +.batch-certificate-stack { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.completion-batch-item { + page-break-inside: avoid; + break-inside: avoid-page; +} + +@media print { + .completion-batch-page { + padding: 0 !important; + } + + .batch-certificate-stack { + gap: 0 !important; + } + + .completion-batch-item { + margin: 0 !important; + page-break-after: always; + break-after: page; + } + + .completion-batch-item:last-child { + page-break-after: auto; + break-after: auto; + } +} + diff --git a/includes/app.php b/includes/app.php index 5a2e44c..b2fab68 100644 --- a/includes/app.php +++ b/includes/app.php @@ -1413,7 +1413,7 @@ function render_page_start(string $pageTitle, string $active = 'home', string $p - + diff --git a/includes/cycles.php b/includes/cycles.php index d8f5247..a839e82 100644 --- a/includes/cycles.php +++ b/includes/cycles.php @@ -1984,10 +1984,8 @@ function school_student_certificate_summary(int $centerApplicationId, int $cycle ]); $rows = $stmt->fetchAll(); - $weightedScore = 0.0; - $weightedTotal = 0.0; - $plainPercentageSum = 0.0; - $plainPercentageCount = 0; + $subjectBuckets = []; + $completedAssessments = 0; $scoreTotal = 0.0; $maxScoreTotal = 0.0; $latestAssessedOn = ''; @@ -2011,15 +2009,21 @@ function school_student_certificate_summary(int $centerApplicationId, int $cycle $percentage = round(($score / $maxScore) * 100, 2); $weight = max(0.0, (float) ($row['weight_percentage'] ?? 0)); - if ($weight > 0) { - $weightedScore += $percentage * $weight; - $weightedTotal += $weight; - } - $plainPercentageSum += $percentage; - $plainPercentageCount++; + $completedAssessments++; $scoreTotal += $score; $maxScoreTotal += $maxScore; + $subjectId = (int) ($row['subject_id'] ?? 0); + $subjectKey = $subjectId > 0 ? 'subject_' . $subjectId : 'assessment_' . (int) ($row['assessment_type_id'] ?? 0); + if (!isset($subjectBuckets[$subjectKey])) { + $subjectBuckets[$subjectKey] = [ + 'score_total' => 0.0, + 'max_score_total' => 0.0, + ]; + } + $subjectBuckets[$subjectKey]['score_total'] += $score; + $subjectBuckets[$subjectKey]['max_score_total'] += $maxScore; + $assessedOn = (string) ($row['assessed_on'] ?? ''); if ($assessedOn !== '' && ($latestAssessedOn === '' || strtotime($assessedOn) > strtotime($latestAssessedOn))) { $latestAssessedOn = $assessedOn; @@ -2038,16 +2042,26 @@ function school_student_certificate_summary(int $centerApplicationId, int $cycle ]; } - $overallPercentage = 0.0; - if ($weightedTotal > 0) { - $overallPercentage = round($weightedScore / $weightedTotal, 2); - } elseif ($plainPercentageCount > 0) { - $overallPercentage = round($plainPercentageSum / $plainPercentageCount, 2); + $subjectPercentageSum = 0.0; + $subjectCount = 0; + foreach ($subjectBuckets as $bucket) { + $subjectMaxScore = (float) ($bucket['max_score_total'] ?? 0); + if ($subjectMaxScore <= 0) { + continue; + } + + $subjectPercentage = ((float) ($bucket['score_total'] ?? 0) / $subjectMaxScore) * 100; + $subjectPercentageSum += max(0.0, min(100.0, $subjectPercentage)); + $subjectCount++; } - $summary['completed_assessments'] = $plainPercentageCount; + $overallPercentage = $subjectCount > 0 + ? round($subjectPercentageSum / $subjectCount, 2) + : 0.0; + + $summary['completed_assessments'] = $completedAssessments; $summary['missing_assessments'] = max(0, $summary['active_assessments'] - $summary['completed_assessments'] - $summary['absent_assessments'] - $summary['excused_assessments']); - $summary['has_results'] = $plainPercentageCount > 0; + $summary['has_results'] = $completedAssessments > 0; $summary['overall_percentage'] = $overallPercentage; $summary['score_total'] = round($scoreTotal, 2); $summary['max_score_total'] = round($maxScoreTotal, 2); diff --git a/student_completion_certificates.php b/student_completion_certificates.php new file mode 100644 index 0000000..1b38061 --- /dev/null +++ b/student_completion_certificates.php @@ -0,0 +1,299 @@ + 'completion', + 'title' => 'Successful Completion', + 'title_ar' => 'إتمام ناجح', + 'completion_line_ar' => 'أتمّ/أتمّت الدورة بنجاح واستفاد/استفادت من محتواها العلمي.', + ]; + + $template = (string) ($settings['completion_certificate_template'] ?? 'modern'); + if (!in_array($template, ['modern', 'classic'], true)) { + $template = 'modern'; + } + + $centerName = trim((string) ($application['center_name'] ?? '')); + if ($centerName === '') { + $centerName = (string) ($settings['app_name'] ?? project_name()); + } + + $centerLogo = trim((string) ($application['logo'] ?? '')); + if ($centerLogo === '') { + $centerLogo = trim((string) ($settings['app_logo'] ?? '')); + } + + $tagline = trim((string) ($settings['completion_certificate_tagline'] ?? '')); + if ($tagline === '') { + $tagline = 'شهادة إتمام وتكريم'; + } + + $overallPercentage = (float) ($certificate['overall_percentage'] ?? 0); + $percentageLabel = $hasResults ? completion_certificate_decimal_value($overallPercentage) . '%' : '—'; + $fallbackMessage = sprintf( + 'تشهد إدارة %s بأن الطالب/الطالبة %s قد أتمّ/أتمّت متطلبات %s %s.', + $centerName, + (string) ($student['full_name'] ?? 'الطالب/الطالبة'), + $cycleLabel, + $honor['title_ar'] + ); + + $bodyMessage = completion_certificate_message_text( + (string) ($settings['completion_certificate_message'] ?? ''), + [ + '{student}' => (string) ($student['full_name'] ?? ''), + '{center}' => $centerName, + '{cycle}' => $cycleLabel, + '{performance}' => (string) ($performance['label_ar'] ?? 'مميز'), + '{honor}' => (string) ($honor['title_ar'] ?? 'بنجاح'), + '{percentage}' => $hasResults ? completion_certificate_decimal_value($overallPercentage) : '0', + ], + $fallbackMessage + ); + + return [ + 'certificate' => $certificate, + 'student' => $student, + 'performance' => $performance, + 'has_results' => $hasResults, + 'honor' => $honor, + 'template' => $template, + 'center_name' => $centerName, + 'center_logo' => $centerLogo, + 'tagline' => $tagline, + 'percentage_label' => $percentageLabel, + 'body_message' => $bodyMessage, + 'issue_date_label' => completion_certificate_date_label((string) ($certificate['latest_assessed_on'] ?? '')), + ]; +} + +$flash = consume_flash(); +$settings = get_app_settings(); +$applicationId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT) ?: 0; +$requestedCycleId = filter_input(INPUT_GET, 'cycle', FILTER_VALIDATE_INT) ?: 0; +$autoprint = filter_input(INPUT_GET, 'autoprint', FILTER_VALIDATE_INT) ?: 0; +$search = clean_text($_GET['search'] ?? '', 255); +$filters = [ + 'gender' => clean_text($_GET['gender'] ?? '', 50), + 'grade_level' => clean_text($_GET['grade_level'] ?? '', 50), + 'enrollment_status' => clean_text($_GET['enrollment_status'] ?? '', 50), +]; + +$application = $applicationId > 0 ? get_application($applicationId) : null; +$isApprovedSchool = $application && (string) $application['status'] === 'approved'; +$cycleContext = ['cycles' => [], 'selected' => null, 'active' => null, 'read_only' => false]; +$selectedCycle = null; +$selectedCycleId = 0; +$cycleLabel = 'الدورة الحالية'; +$certificateItems = []; + +if ($application && $isApprovedSchool) { + $cycleContext = resolve_school_cycle_context((int) $application['id'], $application, $requestedCycleId); + $selectedCycle = $cycleContext['selected']; + $selectedCycleId = $selectedCycle ? (int) ($selectedCycle['id'] ?? 0) : 0; + $cycleLabel = $selectedCycle ? (string) ($selectedCycle['cycle_name'] ?? 'الدورة الحالية') : $cycleLabel; + + if ($selectedCycleId > 0) { + $students = list_school_students_by_cycle((int) $application['id'], $selectedCycleId, $search, 0, 0, $filters); + foreach ($students as $studentRow) { + $viewModel = completion_certificate_view_model($application, $settings, $selectedCycleId, $cycleLabel, (int) ($studentRow['id'] ?? 0)); + if ($viewModel !== null) { + $certificateItems[] = $viewModel; + } + } + } +} + +if (!$application || !$isApprovedSchool || $selectedCycleId <= 0) { + http_response_code(404); +} + +$studentsUrl = $application ? school_page_url('students.php', (int) $application['id'], $selectedCycleId) : 'students.php'; +$pageTitle = $application ? 'تنزيل شهادات الإتمام: ' . (string) ($application['center_name'] ?? '') : 'تنزيل شهادات الإتمام'; +$pageDescription = 'صفحة مجمعة لطباعة أو حفظ جميع شهادات الإتمام والتكريم كملف PDF واحد للمركز والدورة المختارة.'; + +render_page_start($pageTitle, 'approved', $pageDescription); +render_flash($flash); +?> +
+
+
+
+
جميع شهادات الإتمام والتكريم
+
شهادة. سيتم فتح نافذة الطباعة لتتمكن من الحفظ كملف PDF واحد.
+
+
+ العودة إلى الطلاب + + + +
+
+ + +
+
المركز غير موجود
+

تحقق من الرابط ثم أعد المحاولة من صفحة الطلاب.

+ المراكز المعتمدة +
+ +
+
الشهادات غير متاحة حالياً
+

يمكن إصدار شهادات الإتمام للمراكز المعتمدة فقط.

+
+ +
+
لا توجد دورة مختارة
+

اختر دورة صحيحة من صفحة الطلاب ثم أعد فتح تنزيل الشهادات.

+ العودة إلى سجل الطلاب +
+ +
+
لا توجد شهادات مطابقة حالياً
+

قد تكون الفلاتر الحالية لا تُظهر أي طالب، أو لا توجد سجلات في هذه الدورة بعد.

+ العودة إلى سجل الطلاب +
+ +
+ + +
+ + + +
+
+
+ + + + + +
+
Certificate of Completion
+

+

+
+
+
+ + +
+
+ +
+

تُمنح هذه الشهادة إلى

+
+ +
رقم الطالب:
+ + +

+

+
+ +
+
+ التقدير النهائي +
+ +
+
+
+
+ +
+
+ الدورة + +
+
+ تاريخ الإصدار + +
+
+ نسبة الأداء + +
+
+ التقييمات المكتملة + +
+
+
+ + +
+ لا توجد تقييمات مرصودة لهذا الطالب بعد، لذلك تم إصدار شهادة إتمام مختصرة بدون مرتبة أداء رقمية. +
+ + +
+
+ مدير/ة المركز + +
+
+ اعتماد الشهادة + +
+
+
+
+ +
+ +
+
+ + + + diff --git a/students.php b/students.php index e31067f..12512a0 100644 --- a/students.php +++ b/students.php @@ -96,6 +96,18 @@ $metrics = $isApprovedSchool && $selectedCycleId > 0 ? school_student_metrics_by $expectedCapacity = $application ? (int) ($application['expected_students'] ?? 0) : 0; $remainingSeats = max(0, $expectedCapacity - $metrics['total']); +$bulkCertificateParams = ['autoprint' => '1']; +if ($search !== '') { + $bulkCertificateParams['search'] = $search; +} +foreach ($filters as $filterKey => $filterValue) { + if ($filterValue !== '') { + $bulkCertificateParams[$filterKey] = $filterValue; + } +} +$bulkCompletionCertificatesUrl = $application && $selectedCycleId > 0 + ? school_page_url('student_completion_certificates.php', (int) $application['id'], $selectedCycleId) . '&' . http_build_query($bulkCertificateParams) + : '#'; $pageTitle = $application ? 'تسجيل الطلاب: ' . (string) $application['center_name'] . ($selectedCycle ? ' — ' . $cycleLabel : '') : 'تسجيل الطلاب'; $pageDescription = 'صفحة مستقلة لتسجيل الطلاب وإدارة كشف المدرسة بعد الاعتماد، مع ربط كل البيانات بالدورة الموسمية النشطة أو المؤرشفة.'; $approvedSchoolUrl = $application ? school_page_url('approved_school.php', (int) $application['id'], $selectedCycleId) : 'approved_school.php'; @@ -231,8 +243,11 @@ render_flash($flash);
كشف المدرسة
الطلاب المسجلون حالياً في هذه المدرسة فقط.
-
+
طلاب / طالبات + 0 && $totalStudents > 0): ?> + تنزيل كل الشهادات PDF +