<?php
namespace App\Controller;
use App\Entity\Exams;
use App\Entity\Quarter;
use App\Entity\TheClass;
use App\Repository\AbsenceRepository;
use App\Repository\AbsenceBlameConfigRepository;
use App\Repository\AbsenceExclusionConfigRepository;
use App\Repository\AbsencePunishConfigRepository;
use App\Repository\AbsenceRetainedConfigRepository;
use App\Repository\AbsenceWarningConfigRepository;
use App\Repository\DisciplineExclusionRepository;
use App\Repository\ExamsRepository;
use App\Repository\GlobalDisciplineEnabledConfigRepository;
use App\Repository\JustifyAbsenceRepository;
use App\Repository\LateRepository;
use App\Repository\QuarterRepository;
use App\Repository\StudentRepository;
use App\Service\CurrentSchoolYearService;
use App\Service\GenerateSpatiePdf;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/discProff/absence-stats/class/{class}')]
class AbsenceStatsController extends AbstractController
{
// ── SÉQUENCE ──────────────────────────────────────────────────────────────
#[Route('/exam/{exam}', name: 'app_absence_stats_exam', methods: ['GET'])]
public function byExam(
TheClass $class, Exams $exam,
StudentRepository $studentRepo,
AbsenceRepository $absenceRepo,
JustifyAbsenceRepository $justifiedAbsenceRepo,
LateRepository $lateRepo,
GlobalDisciplineEnabledConfigRepository $globalRepo,
AbsenceWarningConfigRepository $warningRepo,
AbsenceBlameConfigRepository $blameRepo,
AbsenceRetainedConfigRepository $retainedRepo,
AbsencePunishConfigRepository $punishRepo,
AbsenceExclusionConfigRepository $exclusionConfigRepo,
DisciplineExclusionRepository $exclusionRepo,
CurrentSchoolYearService $yearService
): Response {
$year = $yearService->getCurrent();
$school = $this->getUser()->getSchool();
$rows = $this->buildRows(
$studentRepo->findByClass($class->getId(), $school->getId(), $year),
$school, $year, $class, null, 'exam', $exam,
$absenceRepo, $justifiedAbsenceRepo, $lateRepo,
$globalRepo, $warningRepo, $blameRepo, $retainedRepo,
$punishRepo, $exclusionConfigRepo, $exclusionRepo
);
return $this->render('absence_stats/index.html.twig', [
'class' => $class,
'exam' => $exam,
'year' => $year,
'rows' => $rows,
'mode' => 'exam',
'title' => $exam->getName(),
]);
}
#[Route('/exam/{exam}/pdf', name: 'app_absence_stats_exam_pdf', methods: ['GET'])]
public function byExamPdf(
TheClass $class, Exams $exam,
StudentRepository $studentRepo,
AbsenceRepository $absenceRepo,
JustifyAbsenceRepository $justifiedAbsenceRepo,
LateRepository $lateRepo,
GlobalDisciplineEnabledConfigRepository $globalRepo,
AbsenceWarningConfigRepository $warningRepo,
AbsenceBlameConfigRepository $blameRepo,
AbsenceRetainedConfigRepository $retainedRepo,
AbsencePunishConfigRepository $punishRepo,
AbsenceExclusionConfigRepository $exclusionConfigRepo,
DisciplineExclusionRepository $exclusionRepo,
CurrentSchoolYearService $yearService,
GenerateSpatiePdf $pdfService
): Response {
$year = $yearService->getCurrent();
$school = $this->getUser()->getSchool();
$rows = $this->buildRows(
$studentRepo->findByClass($class->getId(), $school->getId(), $year),
$school, $year, $class, null, 'exam', $exam,
$absenceRepo, $justifiedAbsenceRepo, $lateRepo,
$globalRepo, $warningRepo, $blameRepo, $retainedRepo,
$punishRepo, $exclusionConfigRepo, $exclusionRepo
);
$html = $this->renderView('absence_stats/pdf.html.twig', [
'class' => $class, 'exam' => $exam, 'year' => $year,
'school' => $school, 'rows' => $rows,
'mode' => 'exam', 'title' => $exam->getName(),
]);
return $pdfService->downloadResponse($html, 'absences-' . $exam->getName() . '-' . $class->getName() . '.pdf', true);
}
// ── TRIMESTRE ─────────────────────────────────────────────────────────────
#[Route('/quarter/{quarter}', name: 'app_absence_stats_quarter', methods: ['GET'])]
public function byQuarter(
TheClass $class, Quarter $quarter,
StudentRepository $studentRepo,
AbsenceRepository $absenceRepo,
JustifyAbsenceRepository $justifiedAbsenceRepo,
LateRepository $lateRepo,
GlobalDisciplineEnabledConfigRepository $globalRepo,
AbsenceWarningConfigRepository $warningRepo,
AbsenceBlameConfigRepository $blameRepo,
AbsenceRetainedConfigRepository $retainedRepo,
AbsencePunishConfigRepository $punishRepo,
AbsenceExclusionConfigRepository $exclusionConfigRepo,
DisciplineExclusionRepository $exclusionRepo,
CurrentSchoolYearService $yearService
): Response {
$year = $yearService->getCurrent();
$school = $this->getUser()->getSchool();
$rows = $this->buildRows(
$studentRepo->findByClass($class->getId(), $school->getId(), $year),
$school, $year, $class, $quarter, 'quarter', null,
$absenceRepo, $justifiedAbsenceRepo, $lateRepo,
$globalRepo, $warningRepo, $blameRepo, $retainedRepo,
$punishRepo, $exclusionConfigRepo, $exclusionRepo
);
return $this->render('absence_stats/index.html.twig', [
'class' => $class,
'quarter' => $quarter,
'year' => $year,
'rows' => $rows,
'mode' => 'quarter',
'title' => $quarter->getName(),
]);
}
#[Route('/quarter/{quarter}/pdf', name: 'app_absence_stats_quarter_pdf', methods: ['GET'])]
public function byQuarterPdf(
TheClass $class, Quarter $quarter,
StudentRepository $studentRepo,
AbsenceRepository $absenceRepo,
JustifyAbsenceRepository $justifiedAbsenceRepo,
LateRepository $lateRepo,
GlobalDisciplineEnabledConfigRepository $globalRepo,
AbsenceWarningConfigRepository $warningRepo,
AbsenceBlameConfigRepository $blameRepo,
AbsenceRetainedConfigRepository $retainedRepo,
AbsencePunishConfigRepository $punishRepo,
AbsenceExclusionConfigRepository $exclusionConfigRepo,
DisciplineExclusionRepository $exclusionRepo,
CurrentSchoolYearService $yearService,
GenerateSpatiePdf $pdfService
): Response {
$year = $yearService->getCurrent();
$school = $this->getUser()->getSchool();
$rows = $this->buildRows(
$studentRepo->findByClass($class->getId(), $school->getId(), $year),
$school, $year, $class, $quarter, 'quarter',null,
$absenceRepo, $justifiedAbsenceRepo, $lateRepo,
$globalRepo, $warningRepo, $blameRepo, $retainedRepo,
$punishRepo, $exclusionConfigRepo, $exclusionRepo
);
$html = $this->renderView('absence_stats/pdf.html.twig', [
'class' => $class, 'quarter' => $quarter, 'year' => $year,
'school' => $school, 'rows' => $rows,
'mode' => 'quarter', 'title' => $quarter->getName(),
]);
return $pdfService->downloadResponse($html, 'absences-' . $quarter->getName() . '-' . $class->getName() . '.pdf', true);
}
// ── ANNUEL ────────────────────────────────────────────────────────────────
#[Route('/annual', name: 'app_absence_stats_annual', methods: ['GET'])]
public function byAnnual(
TheClass $class,
StudentRepository $studentRepo,
AbsenceRepository $absenceRepo,
JustifyAbsenceRepository $justifiedAbsenceRepo,
LateRepository $lateRepo,
GlobalDisciplineEnabledConfigRepository $globalRepo,
AbsenceWarningConfigRepository $warningRepo,
AbsenceBlameConfigRepository $blameRepo,
AbsenceRetainedConfigRepository $retainedRepo,
AbsencePunishConfigRepository $punishRepo,
AbsenceExclusionConfigRepository $exclusionConfigRepo,
DisciplineExclusionRepository $exclusionRepo,
CurrentSchoolYearService $yearService
): Response {
$year = $yearService->getCurrent();
$school = $this->getUser()->getSchool();
$rows = $this->buildRows(
$studentRepo->findByClass($class->getId(), $school->getId(), $year),
$school, $year, $class, null, 'annual', null,
$absenceRepo, $justifiedAbsenceRepo, $lateRepo,
$globalRepo, $warningRepo, $blameRepo, $retainedRepo,
$punishRepo, $exclusionConfigRepo, $exclusionRepo
);
return $this->render('absence_stats/index.html.twig', [
'class' => $class,
'year' => $year,
'rows' => $rows,
'mode' => 'annual',
'title' => 'Bilan Annuel',
]);
}
#[Route('/annual/pdf', name: 'app_absence_stats_annual_pdf', methods: ['GET'])]
public function byAnnualPdf(
TheClass $class,
StudentRepository $studentRepo,
AbsenceRepository $absenceRepo,
JustifyAbsenceRepository $justifiedAbsenceRepo,
LateRepository $lateRepo,
GlobalDisciplineEnabledConfigRepository $globalRepo,
AbsenceWarningConfigRepository $warningRepo,
AbsenceBlameConfigRepository $blameRepo,
AbsenceRetainedConfigRepository $retainedRepo,
AbsencePunishConfigRepository $punishRepo,
AbsenceExclusionConfigRepository $exclusionConfigRepo,
DisciplineExclusionRepository $exclusionRepo,
CurrentSchoolYearService $yearService,
GenerateSpatiePdf $pdfService
): Response {
$year = $yearService->getCurrent();
$school = $this->getUser()->getSchool();
$rows = $this->buildRows(
$studentRepo->findByClass($class->getId(), $school->getId(), $year),
$school, $year, $class, null, 'annual',null,
$absenceRepo, $justifiedAbsenceRepo, $lateRepo,
$globalRepo, $warningRepo, $blameRepo, $retainedRepo,
$punishRepo, $exclusionConfigRepo, $exclusionRepo
);
$html = $this->renderView('absence_stats/pdf.html.twig', [
'class' => $class, 'year' => $year, 'school' => $school,
'rows' => $rows, 'mode' => 'annual', 'title' => 'Bilan Annuel',
]);
return $pdfService->downloadResponse($html, 'absences-annuel-' . $class->getName() . '.pdf', true);
}
// ── HELPER PRIVÉ ──────────────────────────────────────────────────────────
private function buildRows(
array $students, $school, $year, TheClass $class,
?Quarter $quarter, string $mode, ?\App\Entity\Exams $exam = null,
AbsenceRepository $absenceRepo,
JustifyAbsenceRepository $justifiedAbsenceRepo,
LateRepository $lateRepo,
GlobalDisciplineEnabledConfigRepository $globalRepo,
AbsenceWarningConfigRepository $warningRepo,
AbsenceBlameConfigRepository $blameRepo,
AbsenceRetainedConfigRepository $retainedRepo,
AbsencePunishConfigRepository $punishRepo,
AbsenceExclusionConfigRepository $exclusionConfigRepo,
DisciplineExclusionRepository $exclusionRepo
): array {
$global = $globalRepo->findOneBy(['school' => $school, 'year' => $year]);
$rows = [];
foreach ($students as $student) {
$sid = $student->getId();
// ── Absences ──
if ($mode === 'exam') {
$totalAbsence = $absenceRepo->countByExamAndByStudent($sid, $exam->getId(), $school->getId(), $year)[0]['total'];
$justifiedAbsence = $justifiedAbsenceRepo->countByExamAndByStudent($sid, $exam->getId(), $school->getId())[0]['total'];
$lates = $lateRepo->findBy(['student' => $student, 'year' => $year, 'school' => $school, 'exam' => $exam]);
} elseif ($mode === 'quarter' && $quarter !== null) {
$totalAbsence = $absenceRepo->countByQuarterAndByStudent($sid, $quarter->getId(), $school->getId(), $year)[0]['total'];
$justifiedAbsence = $justifiedAbsenceRepo->countByQuarterAndByStudent($sid, $quarter->getId(), $school->getId())[0]['total'];
$lates = $lateRepo->findByStudentAndByQuarter($sid, $quarter->getId(), $school->getId(), $year);
} else {
// annuel
$totalAbsence = $absenceRepo->countAllByStudent($sid, $school->getId(), $year)[0]['total'];
$justifiedAbsence = $justifiedAbsenceRepo->countAllByStudent($sid, $school->getId())[0]['total'];
$lates = $lateRepo->findBy(['student' => $student, 'year' => $year, 'school' => $school]);
}
$injustified = $totalAbsence - $justifiedAbsence;
// ── Retards ──
$lateHours = 0;
foreach ($lates as $l) {
$interval = $l->getStartTime()->diff($l->getEndTime());
$lateHours += ($interval->h * 60 + $interval->i) / 60;
}
// ── Discipline ──
$warningNumber = 0;
if ($global->isWarning()) {
foreach ($warningRepo->findBy(['school' => $school, 'year' => $year]) as $w) {
if ($injustified >= $w->getStart() && $injustified <= $w->getEnd()) { $warningNumber += $w->getWarning(); }
}
}
$blameNumber = 0;
if ($global->isBlame()) {
foreach ($blameRepo->findBy(['school' => $school, 'year' => $year]) as $b) {
if ($injustified >= $b->getStart() && $injustified <= $b->getEnd()) { $blameNumber += $b->getBlame(); }
}
}
$retainedNumber = 0;
if ($global->isRetained()) {
foreach ($retainedRepo->findBy(['school' => $school, 'year' => $year]) as $r) {
if ($injustified >= $r->getStart() && $injustified <= $r->getEnd()) { $retainedNumber += $r->getRetained(); }
}
}
$punishNumber = 0;
if ($global->isPunish()) {
foreach ($punishRepo->findBy(['school' => $school, 'year' => $year]) as $p) {
if ($injustified >= $p->getStart() && $injustified <= $p->getEnd()) { $punishNumber += $p->getPunish(); }
}
}
$exclusionNumber = 0;
foreach ($exclusionConfigRepo->findBy(['school' => $school, 'year' => $year]) as $d) {
if ($injustified >= $d->getStart() && $injustified <= $d->getEnd()) { $exclusionNumber += $d->getDayForInterval(); }
}
$criteria = ['school' => $school, 'classe' => $class, 'year' => $year, 'student' => $student];
if ($mode === 'quarter' && $quarter !== null) {
$criteria['quarter'] = $quarter;
} else {
$criteria['annual'] = true;
}
foreach ($exclusionRepo->findBy($criteria) as $exclu) {
$exclusionNumber += $exclu->getNumber();
}
$rows[] = [
'student' => $student,
'totalAbsence' => $totalAbsence,
'justifiedAbsence' => $justifiedAbsence,
'injustified' => $injustified,
'lateHours' => round($lateHours, 1),
'warningNumber' => $warningNumber,
'blameNumber' => $blameNumber,
'retainedNumber' => $retainedNumber,
'punishNumber' => $punishNumber,
'exclusionNumber' => $exclusionNumber,
];
}
// Trier par nombre d'absences injustifiées décroissant
usort($rows, fn($a, $b) => $b['injustified'] <=> $a['injustified']);
return $rows;
}
}