src/Controller/StatisticsController.php line 196

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  4. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  5. use App\Service\SchoolYearService;
  6. use App\Repository\ClassRoomRepository;
  7. use App\Repository\StudentRepository;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Symfony\Component\HttpFoundation\Response;
  10. use Knp\Component\Pager\PaginatorInterface;
  11. use Symfony\Component\Routing\Annotation\Route;
  12. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
  13. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use App\Entity\ClassRoom;
  16. use App\Entity\SchoolYear;
  17. use Symfony\Component\HttpFoundation\JsonResponse;
  18. use Knp\Snappy\Pdf;
  19. /**
  20. * Sequence controller.
  21. *
  22. * @Route("/admin/stats")
  23. */
  24. class StatisticsController extends AbstractController
  25. {
  26. private SchoolYearService $schoolYearService;
  27. private ClassRoomRepository $repo;
  28. private StudentRepository $stdRepo;
  29. private $em;
  30. public function __construct(EntityManagerInterface $em,StudentRepository $stdRepo,ClassRoomRepository $repo,SchoolYearService $schoolYearService)
  31. {
  32. $this->em = $em;
  33. $this->repo = $repo;
  34. $this->schoolYearService = $schoolYearService;
  35. $this->stdRepo = $stdRepo;
  36. }
  37. /**
  38. * Displays a graph updated.
  39. *
  40. * @Route("/update", name="admin_graph_update", options = { "expose" = true })
  41. * @Method("GET")
  42. * @Template()
  43. */
  44. public function updateGraphs(Request $request): JsonResponse
  45. {
  46. // URL de redirection et paramètres
  47. $url = $this->generateUrl('admin_statistics', ['id' => intval($request->query->get('id'))]);
  48. return new JsonResponse(['url' => $url]);
  49. }
  50. /**
  51. * Displays a pdf of students grouping by gender.
  52. *
  53. * @Route("printgr/{id}", name="admin_stat_print_gender_room", defaults={"id"=0} )
  54. * @Method("GET")
  55. * @Template()
  56. */
  57. public function genderRoomPdf( Pdf $pdf, int $id=0): Response
  58. {
  59. $year = $this->schoolYearService->sessionYearById();
  60. $rooms = $this->repo->findAll();
  61. if($id > 0){
  62. $rooms = $this->repo->findBy(array("id" => $id));
  63. $this->viewGender($id);
  64. } else {
  65. $this->viewGender();
  66. }
  67. $connection = $this->em->getConnection();
  68. $gender_datas = $connection->executeQuery("SELECT * FROM V_GENDER_ROOM ")->fetchAll();
  69. $html = $this->render('statistics/pdf/gender_room.html.twig', [
  70. "rooms"=>$rooms,
  71. 'year' => $year,
  72. "gender_datas"=>$gender_datas,
  73. ]);
  74. return new Response(
  75. $pdf->getOutputFromHtml($html),
  76. 200,
  77. array(
  78. 'Content-Type' => 'application/pdf',
  79. 'Content-Disposition' => 'inline; filename="stat_gender_room' .( count($rooms)==1 ? $rooms[0]->getName():"") . '.pdf"'
  80. )
  81. );
  82. }
  83. /**
  84. * Displays a pdf of students grouping by gender.
  85. *
  86. * @Route("printagr/{id}", name="admin_stat_print_age_room", defaults={"id"=0} )
  87. * @Method("GET")
  88. * @Template()
  89. */
  90. public function ageGroupRoomPdf( Pdf $pdf, int $id=0): Response
  91. {
  92. $year = $this->schoolYearService->sessionYearById();
  93. $rooms = $this->repo->findAll();
  94. if($id > 0){
  95. $rooms = $this->repo->findBy(array("id" => $id));
  96. $this->viewAgeGroup($id);
  97. } else {
  98. $this->viewAgeGroup();
  99. }
  100. $connection = $this->em->getConnection();
  101. $age_group_datas = $connection->executeQuery("SELECT * FROM V_AGE_GROUP_ROOM ")->fetchAll();
  102. $html = $this->render('statistics/pdf/age_group_room.html.twig', [
  103. "rooms"=>$rooms,
  104. 'year' => $year,
  105. "age_group_datas"=>$age_group_datas,
  106. ]);
  107. return new Response(
  108. $pdf->getOutputFromHtml($html),
  109. 200,
  110. array(
  111. 'Content-Type' => 'application/pdf',
  112. 'Content-Disposition' => 'inline; filename="stat_gender_room' .( count($rooms)==1 ? $rooms[0]->getName():"") . '.pdf"'
  113. )
  114. );
  115. }
  116. /**
  117. * Displays a pdf of students grouping by gender.
  118. *
  119. * @Route("printagrgen/{id}", name="admin_stat_print_age_room_gender", defaults={"id"=0} )
  120. * @Method("GET")
  121. * @Template()
  122. */
  123. public function ageGroupGenderRoomPdf( Pdf $pdf, int $id=0): Response
  124. {
  125. $year = $this->schoolYearService->sessionYearById();
  126. $rooms = $this->repo->findAll();
  127. $connection = $this->em->getConnection();
  128. if($id > 0){
  129. $rooms = $this->repo->findBy(array("id" => $id));
  130. $this->viewGenderAgeGroup($id);
  131. $age_group_gender_datas = $connection->executeQuery("SELECT * FROM V_AGE_GROUP_GENDER_ROOM ")->fetchAll();
  132. } else {
  133. $rooms = $this->repo->findAll();
  134. foreach($rooms as $key=>$room){
  135. $this->viewGenderAgeGroup($room->getId());
  136. $age_group_gender_datas[$room->getId()] = $connection->executeQuery("SELECT * FROM V_AGE_GROUP_GENDER_ROOM ")->fetchAll();
  137. }
  138. foreach ($rooms as $room) {
  139. $roomNames[$room->getId()] = $room->getName();
  140. }
  141. // dd($age_group_gender_datas);
  142. }
  143. $html = $this->render($id > 0 ? 'statistics/pdf/age_group_gender_room.html.twig' : 'statistics/pdf/age_group_gender_room_wide.html.twig' , [
  144. "rooms"=>$rooms,
  145. "roomNames"=>$roomNames,
  146. 'year' => $year,
  147. "minAge" => $this->findMinMaxAge()[0]["minAge"],
  148. "maxAge" => $this->findMinMaxAge()[0]["maxAge"],
  149. "age_group_gender_datas"=>$age_group_gender_datas,
  150. ]);
  151. $options = [
  152. 'orientation' => 'landscape',
  153. 'margin-top' => 0,
  154. 'margin-right' => 0,
  155. 'margin-bottom' => 0,
  156. 'margin-left' => 0,
  157. ];
  158. return new Response(
  159. $pdf->getOutputFromHtml($html, $options),
  160. 200,
  161. array(
  162. 'Content-Type' => 'application/pdf',
  163. 'Content-Disposition' => 'inline; filename="stat_gender_agegrp_room' .( count($rooms)==1 ? $rooms[0]->getName():"") . '.pdf"'
  164. )
  165. );
  166. }
  167. /**
  168. * Lists all Sequenceme entities.
  169. *
  170. * @Route("/{id}", name="admin_statistics", defaults={"id"=0})
  171. * @Method("GET")
  172. * @Template()
  173. */
  174. public function indexAction(Request $request, int $id = 0)
  175. {
  176. $rooms = $this->repo->findAll();
  177. $connection = $this->em->getConnection();
  178. // Générer les vues SQL
  179. if ($id === 0) {
  180. $this->viewGender();
  181. $this->viewAgeGroup();
  182. $this->viewGenderAgeGroup();
  183. } else {
  184. $this->viewGender($id);
  185. $this->viewAgeGroup($id);
  186. $this->viewGenderAgeGroup($id);
  187. }
  188. $gender_datas = $connection->executeQuery("SELECT * FROM V_GENDER_ROOM")->fetchAll();
  189. $age_group_datas = $connection->executeQuery("SELECT * FROM V_AGE_GROUP_ROOM")->fetchAll();
  190. $age_group_gender_datas = $connection->executeQuery("SELECT * FROM V_AGE_GROUP_GENDER_ROOM")->fetchAll();
  191. // ── roomNames ────────────────────────────────────────────────
  192. $roomNamesArr = [];
  193. foreach ($rooms as $room) {
  194. $roomNamesArr[] = $room->getName();
  195. }
  196. // ── Masculin / Féminin par classe ────────────────────────────
  197. $masculin = [];
  198. $feminin = [];
  199. foreach ($roomNamesArr as $name) {
  200. foreach ($gender_datas as $data) {
  201. if (strcmp($data['room'], $name) === 0 && strcmp($data['gender'], '0') === 0) {
  202. $masculin[] = (int) $data['workforce'];
  203. }
  204. if (strcmp($data['room'], $name) === 0 && strcmp($data['gender'], '1') === 0) {
  205. $feminin[] = (int) $data['workforce'];
  206. }
  207. }
  208. }
  209. // ── Effectif total par classe (nouveau) ──────────────────────
  210. // Calculé depuis masculin+féminin pour éviter une requête supplémentaire
  211. $totalPerClass = [];
  212. foreach ($roomNamesArr as $i => $name) {
  213. $totalPerClass[] = ($masculin[$i] ?? 0) + ($feminin[$i] ?? 0);
  214. }
  215. // ── Ratio F/(F+M) par classe en % (nouveau) ──────────────────
  216. $ratioFeminin = [];
  217. foreach ($roomNamesArr as $i => $name) {
  218. $total = $totalPerClass[$i];
  219. $ratioFeminin[] = $total > 0
  220. ? round(($feminin[$i] ?? 0) / $total * 100, 1)
  221. : 0;
  222. }
  223. // ── Tranches d'âge ───────────────────────────────────────────
  224. $age_groups_weight = [];
  225. $age_groups_label = [];
  226. $previousKey = null;
  227. foreach ($age_group_datas as $key => $group) {
  228. $age_groups_weight[] = (int) $group['effectif'];
  229. $age_groups_label[] = $previousKey === null
  230. ? '0–' . $group['tranche_age']
  231. : $age_group_datas[$previousKey]['tranche_age'] . '–' . $group['tranche_age'];
  232. $previousKey = $key;
  233. }
  234. // ── Min/Max âge (nouveau) ────────────────────────────────────
  235. $minMaxAge = $this->findMinMaxAge();
  236. $minAge = $minMaxAge[0]['minAge'] ?? 0;
  237. $maxAge = $minMaxAge[0]['maxAge'] ?? 0;
  238. // ── roomNames JSON (filtre classe unique) ────────────────────
  239. $roomNamesJson = $id > 0
  240. ? json_encode([$this->repo->findOneById($id)->getName()])
  241. : json_encode($roomNamesArr);
  242. return $this->render('statistics/dashboard.html.twig', [
  243. 'rooms' => $rooms,
  244. 'feminin' => json_encode($feminin),
  245. 'masculin' => json_encode($masculin),
  246. 'totalPerClass' => json_encode($totalPerClass), // NOUVEAU
  247. 'ratioFeminin' => json_encode($ratioFeminin), // NOUVEAU
  248. 'roomNames' => $roomNamesJson,
  249. 'ageGroupsLabel' => json_encode($age_groups_label),
  250. 'ageGroupsWeight' => json_encode($age_groups_weight),
  251. 'scatterData' => json_encode($age_group_gender_datas),
  252. 'minAge' => $minAge, // NOUVEAU
  253. 'maxAge' => $maxAge, // NOUVEAU
  254. ]);
  255. }
  256. // Cette fonction genere les vue d'effectif par tranche d'age par classe
  257. public function viewAgeGroup(int $room=0){
  258. $year = $this->schoolYearService->sessionYearById();
  259. $connection = $this->em->getConnection();
  260. if($room>0){
  261. $statement = $connection->prepare(
  262. " CREATE OR REPLACE VIEW V_AGE_GROUP_ROOM AS
  263. SELECT
  264. FLOOR(DATEDIFF(NOW(), birthday) / 365) AS tranche_age,
  265. COUNT(*) AS effectif
  266. FROM student std
  267. JOIN subscription sub ON sub.student_id = std.id
  268. JOIN class_room room ON sub.class_room_id = room.id
  269. WHERE sub.school_year_id =? AND room.id = ?
  270. GROUP BY
  271. tranche_age
  272. ORDER BY
  273. tranche_age;
  274. "
  275. );
  276. $statement->bindValue(2, $room);
  277. } else {
  278. $statement = $connection->prepare(
  279. " CREATE OR REPLACE VIEW V_AGE_GROUP_ROOM AS
  280. SELECT
  281. FLOOR(DATEDIFF(NOW(), birthday) / 365 ) AS tranche_age,
  282. COUNT(*) AS effectif
  283. FROM student std
  284. JOIN subscription sub ON sub.student_id = std.id
  285. JOIN class_room room ON sub.class_room_id = room.id
  286. WHERE sub.school_year_id =?
  287. GROUP BY
  288. tranche_age
  289. ORDER BY
  290. tranche_age;
  291. "
  292. );
  293. }
  294. $statement->bindValue(1, $year->getId());
  295. $statement->execute();
  296. }
  297. // Cette fonction genere les vues d'effectif par sexe par classe
  298. public function viewGender(int $room=0){
  299. $year = $this->schoolYearService->sessionYearById();
  300. $connection = $this->em->getConnection();
  301. if($room>0){
  302. $statement = $connection->prepare(
  303. " CREATE OR REPLACE VIEW V_GENDER_ROOM AS
  304. SELECT room.name as room , COUNT(std.id) as workforce, std.gender as gender
  305. FROM student std
  306. JOIN subscription sub ON sub.student_id = std.id
  307. JOIN class_room room ON sub.class_room_id = room.id
  308. WHERE sub.school_year_id =? AND room.id = ?
  309. GROUP BY gender; "
  310. );
  311. $statement->bindValue(2, $room);
  312. } else {
  313. $statement = $connection->prepare(
  314. " CREATE OR REPLACE VIEW V_GENDER_ROOM AS
  315. SELECT room.name as room , COUNT(std.id) as workforce, std.gender as gender
  316. FROM student std
  317. JOIN subscription sub ON sub.student_id = std.id
  318. JOIN class_room room ON sub.class_room_id = room.id
  319. WHERE sub.school_year_id =?
  320. GROUP BY room, gender; "
  321. );
  322. }
  323. $statement->bindValue(1, $year->getId());
  324. $statement->execute();
  325. }
  326. // Cette fonction genere les vues d'effectif par sexe par classe
  327. public function viewGenderAgeGroup(int $room=0){
  328. $year = $this->schoolYearService->sessionYearById();
  329. $connection = $this->em->getConnection();
  330. if($room>0){
  331. $statement = $connection->prepare(
  332. " CREATE OR REPLACE VIEW V_AGE_GROUP_GENDER_ROOM AS
  333. SELECT
  334. FLOOR(DATEDIFF(NOW(), birthday) / 365 ) AS age, std.gender as sexe,
  335. COUNT(std.id) AS poids
  336. FROM student std
  337. JOIN subscription sub ON sub.student_id = std.id
  338. JOIN class_room room ON sub.class_room_id = room.id
  339. WHERE sub.school_year_id =? AND room.id = ?
  340. GROUP BY
  341. std.gender, age
  342. ORDER BY
  343. age;
  344. "
  345. );
  346. $statement->bindValue(2, $room);
  347. } else {
  348. $statement = $connection->prepare(
  349. " CREATE OR REPLACE VIEW V_AGE_GROUP_GENDER_ROOM AS
  350. SELECT
  351. FLOOR(DATEDIFF(NOW(), birthday) / 365 ) AS age, std.gender as sexe,
  352. COUNT(std.id) AS poids
  353. FROM student std
  354. JOIN subscription sub ON sub.student_id = std.id
  355. JOIN class_room room ON sub.class_room_id = room.id
  356. WHERE sub.school_year_id =?
  357. GROUP BY
  358. std.gender, age
  359. ORDER BY
  360. age;
  361. "
  362. );
  363. }
  364. $statement->bindValue(1, $year->getId());
  365. $statement->execute();
  366. }
  367. public function findMinMaxAge()
  368. {
  369. $connection = $this->em->getConnection();
  370. $year = $this->schoolYearService->sessionYearById();
  371. $query = " SELECT MIN(YEAR(NOW()) - YEAR(std.birthday)-1) AS minAge,
  372. MAX(YEAR(NOW()) - YEAR(std.birthday)) AS maxAge
  373. FROM student std
  374. JOIN subscription sub ON sub.student_id = std.id
  375. WHERE sub.school_year_id = :year
  376. ";
  377. $parameters = ['year' => $year->getId()];
  378. $statement = $connection->prepare($query);
  379. $result = $statement->executeQuery($parameters)->fetchAll();
  380. return $result;
  381. }
  382. public function numberPerClass(){
  383. $connection = $this->em->getConnection();
  384. $year = $this->schoolYearService->sessionYearById();
  385. $query = " SELECT COUNT(std.id) AS poids , sub.class_room_id AS room
  386. FROM student std
  387. JOIN subscription sub ON sub.student_id = std.id
  388. WHERE sub.school_year_id = :year
  389. GROUP BY ( sub.class_room_id) ";
  390. $parameters = ['year' => $year->getId()];
  391. $statement = $connection->prepare($query);
  392. $result = $statement->executeQuery($parameters)->fetchAll();
  393. return $result;
  394. }
  395. }