src/Controller/StudentController.php line 226

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Program;
  4. use App\Entity\Student;
  5. use App\Entity\Subscription;
  6. use App\Entity\ClassRoom;
  7. use App\Form\StudentType;
  8. use App\Repository\StudentRepository;
  9. use App\Repository\EvaluationRepository;
  10. use App\Repository\SequenceRepository;
  11. use App\Repository\MarkRepository;
  12. use Knp\Snappy\Pdf;
  13. use App\Repository\SchoolYearRepository;
  14. use App\Repository\SubscriptionRepository;
  15. use App\Repository\PaymentRepository;
  16. use App\Repository\QuaterRepository;
  17. use App\Repository\InstallmentRepository;
  18. use App\Repository\PaymentPlanRepository;
  19. use App\Repository\MainTeacherRepository;
  20. use App\Repository\ProgramRepository;
  21. use Doctrine\ORM\EntityManagerInterface;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\Routing\Annotation\Route;
  25. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
  26. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  27. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  28. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  29. use App\Service\SchoolYearService;
  30. use App\Entity\User;
  31. /**
  32. * Studentme controller.
  33. *
  34. * @Route("/prof/students")
  35. */
  36. class StudentController extends AbstractController
  37. {
  38. private EntityManagerInterface $em;
  39. private $repo;
  40. private $scRepo;
  41. private $seqRepo;
  42. private SubscriptionRepository $subRepo;
  43. private $markRepo;
  44. private $evalRepo;
  45. private $qtRepo;
  46. private $snappy;
  47. private SchoolYearService $schoolYearService;
  48. private PaymentPlanRepository $ppRepo;
  49. private InstallmentRepository $instRepo;
  50. private PaymentRepository $pRepo;
  51. private MainTeacherRepository $mainTeacherRepo;
  52. private ProgramRepository $programRepo;
  53. public function __construct(
  54. PaymentRepository $pRepo,
  55. InstallmentRepository $instRepo,
  56. PaymentPlanRepository $ppRepo,
  57. SchoolYearService $schoolYearService,
  58. EntityManagerInterface $em,
  59. SubscriptionRepository $subRepo,
  60. MarkRepository $markRepo,
  61. EvaluationRepository $evalRepo,
  62. StudentRepository $repo,
  63. SequenceRepository $seqRepo,
  64. SchoolYearRepository $scRepo,
  65. QuaterRepository $qtRepo,
  66. MainTeacherRepository $mainTeacherRepo,
  67. ProgramRepository $programRepo,
  68. Pdf $snappy
  69. ) {
  70. $this->em = $em;
  71. $this->repo = $repo;
  72. $this->scRepo = $scRepo;
  73. $this->markRepo = $markRepo;
  74. $this->seqRepo = $seqRepo;
  75. $this->evalRepo = $evalRepo;
  76. $this->subRepo = $subRepo;
  77. $this->qtRepo = $qtRepo;
  78. $this->snappy = $snappy;
  79. $this->ppRepo = $ppRepo;
  80. $this->pRepo = $pRepo;
  81. $this->instRepo = $instRepo;
  82. $this->mainTeacherRepo = $mainTeacherRepo;
  83. $this->programRepo = $programRepo;
  84. $this->schoolYearService = $schoolYearService;
  85. }
  86. // =========================================================
  87. // Méthode privée mutualisée pour l'auth
  88. // Remplace les blocs répétés dans chaque action
  89. // =========================================================
  90. private function checkAuth(): ?Response
  91. {
  92. if (!$this->getUser()) {
  93. $this->addFlash('warning', 'You need login first!');
  94. return $this->redirectToRoute('app_login');
  95. }
  96. /** @var User $user */
  97. $user = $this->getUser();
  98. if (!$user->isVerified()) {
  99. $this->addFlash('warning', 'You need to have a verified account!');
  100. return $this->redirectToRoute('app_login');
  101. }
  102. return null;
  103. }
  104. /**
  105. * @Route("/create", name="admin_students_new", methods={"GET","POST"})
  106. */
  107. public function create(Request $request): Response
  108. {
  109. if ($redirect = $this->checkAuth()) return $redirect;
  110. $student = new Student();
  111. // Générer le matricule AVANT la construction du formulaire
  112. $numero = $this->repo->getNumeroDispo();
  113. $student->setMatricule($numero);
  114. $form = $this->createForm(StudentType::class, $student);
  115. $form->handleRequest($request);
  116. if ($form->isSubmitted() && $form->isValid()) {
  117. if ($student->getEntryClass() !== null) {
  118. $sub = new Subscription();
  119. $sub->setStudent($student);
  120. $sub->setClassRoom($student->getEntryClass());
  121. $sub->setSchoolYear($this->schoolYearService->sessionYearById());
  122. $this->em->persist($sub);
  123. }
  124. $this->em->persist($student);
  125. $this->em->flush();
  126. $this->addFlash('success', 'Student successfully created');
  127. return $this->redirectToRoute('admin_students', [
  128. 'type' => "new_students_not_yet_registered_checkbox",
  129. ]);
  130. }
  131. return $this->render('student/new.html.twig', [
  132. 'form' => $form->createView(),
  133. ]);
  134. }
  135. /**
  136. * Lists all Studentme entities.
  137. *
  138. * @Route("/{type}", name="admin_students")
  139. * @Method("GET")
  140. * @Template()
  141. */
  142. public function indexAction($type)
  143. {
  144. if ($redirect = $this->checkAuth()) return $redirect;
  145. $year = $this->schoolYearService->sessionYearById();
  146. switch ($type) {
  147. case "new_students_not_yet_registered_checkbox":
  148. $students = $this->repo->findNewStudents($year);
  149. break;
  150. case "new_registered_students_checkbox":
  151. $students = $this->repo->findNewRegisteredStudents($year);
  152. break;
  153. case "registered_former_students_checkbox":
  154. $students = $this->repo->findFormerRegisteredStudents($year);
  155. break;
  156. case "complete_registered_students_checkbox":
  157. $students = $this->repo->findEnrolledStudentsThisYear2($year);
  158. break;
  159. default:
  160. $students = $this->repo->findEnrolledStudentsThisYear2($year);
  161. break;
  162. }
  163. // Regrouper les élèves par classe
  164. $studentsByClass = [];
  165. foreach ($students as $student) {
  166. $class = $student->getEntryClass();
  167. if ($class) {
  168. $studentsByClass[$class->getId()]['class'] = $class;
  169. $studentsByClass[$class->getId()]['students'][] = $student;
  170. } else {
  171. $studentsByClass['no_class']['class'] = null;
  172. $studentsByClass['no_class']['students'][] = $student;
  173. }
  174. }
  175. // Récupération des programmes pour le filtre
  176. $programs = $this->programRepo->findAll();
  177. return $this->render('student/list.html.twig', [
  178. 'programs' => $programs,
  179. 'studentsByClass' => $studentsByClass,
  180. 'type' => $type,
  181. 'year' => $year,
  182. ]);
  183. }
  184. /**
  185. * @Route("/{id}/unregister/{room_id}", name="admin_students_unregister", requirements={"id"="\d+", "room_id"="\d+"})
  186. * @ParamConverter("std", options={"mapping": {"id": "id"}})
  187. * @ParamConverter("room", options={"mapping": {"room_id": "id"}})
  188. */
  189. public function unregisterAction(Student $std, ClassRoom $room)
  190. {
  191. if ($redirect = $this->checkAuth()) return $redirect;
  192. $year = $this->schoolYearService->sessionYearById();
  193. $sub = $this->subRepo->findOneBy(["student" => $std, "classRoom" => $room, "schoolYear" => $year]);
  194. $this->em->remove($sub);
  195. $this->em->flush();
  196. return $this->redirectToRoute('admin_classrooms_show', ["id" => $room->getId()]);
  197. }
  198. /**
  199. * Finds and displays a Studentme entity.
  200. *
  201. * @Route("/{id}/show", name="admin_students_show", requirements={"id"="\d+"})
  202. * @Method("GET")
  203. * @Template()
  204. */
  205. public function showAction(Student $student)
  206. {
  207. if ($redirect = $this->checkAuth()) return $redirect;
  208. $year = $this->schoolYearService->sessionYearById();
  209. $seq = $this->seqRepo->findOneBy(["activated" => true]);
  210. $sub = $this->subRepo->findOneBy(["student" => $student, "schoolYear" => $year]);
  211. $results = [
  212. 'student' => $student,
  213. 'cours' => null,
  214. 'session1' => null,
  215. 'session2' => null,
  216. 'session3' => null,
  217. 'session4' => null,
  218. 'session5' => null,
  219. 'session6' => null,
  220. ];
  221. $payments = $this->pRepo->findBy(["subscription" => $sub], ['updatedAt' => 'ASC']);
  222. $paymentPlan = $this->ppRepo->findOneBy(["schoolYear" => $year]);
  223. if ($sub !== null) {
  224. $installments = $this->instRepo->findBy(["paymentPlan" => $paymentPlan, "classRoom" => $sub->getClassRoom()]);
  225. } else {
  226. $installments = $this->instRepo->findBy(["paymentPlan" => $paymentPlan]);
  227. }
  228. $seqs = $this->seqRepo->findSequenceThisYear($year);
  229. $evalSeqs = [];
  230. if ($sub !== null) {
  231. foreach ($seqs as $s) {
  232. $evalSeqs[$s->getId()] = $this->evalRepo->findBy([
  233. "classRoom" => $sub->getClassRoom(),
  234. "sequence" => $s,
  235. ]);
  236. }
  237. $courses = [];
  238. $averageSeqs = [];
  239. if ($seq && isset($evalSeqs[$seq->getId()])) {
  240. foreach ($evalSeqs[$seq->getId()] as $eval) {
  241. $courses[] = $eval->getCourse()->getWording();
  242. }
  243. }
  244. foreach ($seqs as $s) {
  245. $average = [];
  246. foreach ($evalSeqs[$s->getId()] as $eval) {
  247. $mark = $this->markRepo->findOneBy(["student" => $student, "evaluation" => $eval]);
  248. if ($mark) {
  249. $average[] = $mark->getValue();
  250. }
  251. }
  252. $averageSeqs[$s->getId()] = $average;
  253. }
  254. $filename = "assets/images/student/" . $student->getMatricule() . ".jpg";
  255. $file_exists = file_exists($filename);
  256. $results['payments'] = $payments;
  257. $results['payment_plan'] = $paymentPlan;
  258. $results['installments'] = $installments;
  259. $results['sub'] = $sub;
  260. $results['file_exists'] = $file_exists;
  261. $results['cours'] = json_encode($courses);
  262. foreach ($seqs as $s) {
  263. $results[strtolower($s->getWording())] = json_encode($averageSeqs[$s->getId()]);
  264. }
  265. }
  266. return $this->render('student/show.html.twig', $results);
  267. }
  268. /**
  269. * Displays a form to edit an existing Studentme entity.
  270. *
  271. * @Route("/{id}/edit", name="admin_students_edit", requirements={"id"="\d+"}, methods={"GET","PUT"})
  272. * @Template()
  273. */
  274. public function edit(Request $request, Student $student): Response
  275. {
  276. if ($redirect = $this->checkAuth()) return $redirect;
  277. $form = $this->createForm(StudentType::class, $student, ['method' => 'PUT']);
  278. $form->handleRequest($request);
  279. if ($form->isSubmitted() && $form->isValid()) {
  280. $this->em->flush();
  281. $this->addFlash('success', 'Student successfully updated');
  282. $year = $this->schoolYearService->sessionYearById();
  283. $sub = $this->subRepo->findOneBy(["student" => $student, "schoolYear" => $year]);
  284. return $this->redirectToRoute('admin_classrooms_show', ["id" => $sub->getClassRoom()->getId()]);
  285. }
  286. return $this->render('student/edit.html.twig', [
  287. 'student' => $student,
  288. 'form' => $form->createView(),
  289. ]);
  290. }
  291. /**
  292. * Deletes a Studentme entity.
  293. *
  294. * @Route("/{id}/delete", name="admin_students_delete", requirements={"id"="\d+"}, methods={"DELETE"})
  295. */
  296. public function delete(Student $student, Request $request): Response
  297. {
  298. if ($redirect = $this->checkAuth()) return $redirect;
  299. if ($this->isCsrfTokenValid('students_deletion' . $student->getId(), $request->request->get('csrf_token'))) {
  300. $this->em->remove($student);
  301. $this->em->flush();
  302. $this->addFlash('info', 'Student successfully deleted');
  303. }
  304. return $this->redirectToRoute('admin_students', [
  305. 'type' => "new_students_not_yet_registered_checkbox",
  306. ]);
  307. }
  308. /**
  309. * Build student's school certificate
  310. *
  311. * @Route("/{id}/certificate", name="admin_student_certificate", requirements={"id"="\d+"})
  312. */
  313. public function schoolCertificate(Pdf $pdf, Student $std): Response
  314. {
  315. if ($redirect = $this->checkAuth()) return $redirect;
  316. $year = $this->schoolYearService->sessionYearById();
  317. $sub = $this->subRepo->findOneBy(["student" => $std, "schoolYear" => $year]);
  318. $html = $this->renderView('student/school_certificate.html.twig', [
  319. 'year' => $year,
  320. 'std' => $std,
  321. 'sub' => $sub,
  322. ]);
  323. return new Response($pdf->getOutputFromHtml($html), 200, [
  324. 'Content-Type' => 'application/pdf',
  325. 'Content-Disposition' => 'inline; filename="certif_' . $std->getMatricule() . '.pdf"',
  326. ]);
  327. }
  328. /**
  329. * @Route("/{id}/receipt", name="admin_student_receipt", requirements={"id"="\d+"})
  330. */
  331. public function tuitionReceiptAction(Pdf $pdf, Student $std): Response
  332. {
  333. if ($redirect = $this->checkAuth()) return $redirect;
  334. $year = $this->schoolYearService->sessionYearById();
  335. $sub = $this->subRepo->findOneBy(["student" => $std, "schoolYear" => $year]);
  336. $payments = $this->pRepo->findBy(["subscription" => $sub], ['updatedAt' => 'ASC']);
  337. $paymentPlan = $this->ppRepo->findOneBy(["schoolYear" => $year]);
  338. $installments = $this->instRepo->findBy(["paymentPlan" => $paymentPlan, "classRoom" => $sub->getClassRoom()]);
  339. $html = $this->renderView('student/templating/tuition_receipt.html.twig', [
  340. 'year' => $year,
  341. 'std' => $std,
  342. 'sub' => $sub,
  343. 'payments' => $payments,
  344. 'payment_plan' => $paymentPlan,
  345. 'installments' => $installments,
  346. ]);
  347. return new Response($pdf->getOutputFromHtml($html), 200, [
  348. 'Content-Type' => 'application/pdf',
  349. 'Content-Disposition' => 'inline; filename="recu_' . $std->getMatricule() . '.pdf"',
  350. ]);
  351. }
  352. /**
  353. * @Route("/{id}/badge", name="admin_student_badge", requirements={"id"="\d+"})
  354. */
  355. public function schoolBadge(Pdf $pdf, Student $std): Response
  356. {
  357. if ($redirect = $this->checkAuth()) return $redirect;
  358. $year = $this->schoolYearService->sessionYearById();
  359. $sub = $this->subRepo->findOneBy(["student" => $std, "schoolYear" => $year]);
  360. $filename = "assets/images/student/" . $std->getMatricule() . ".jpg";
  361. $fileExist = file_exists($filename);
  362. $html = $this->renderView('student/badge.html.twig', [
  363. 'sub' => $sub,
  364. 'fileExist' => $fileExist,
  365. ]);
  366. return new Response($pdf->getOutputFromHtml($html), 200, [
  367. 'Content-Type' => 'application/pdf',
  368. 'Content-Disposition' => 'inline; filename="badge_' . $std->getMatricule() . '.pdf"',
  369. ]);
  370. }
  371. /**
  372. * @Route("/{id}/reportCardTrim2024", name="admin_students_reportcards_quat_2024", requirements={"id"="\d+"})
  373. * @Method("GET")
  374. * @Template()
  375. */
  376. public function reporCardTrimAction2024(Pdf $pdf, Student $std, Request $request)
  377. {
  378. if ($redirect = $this->checkAuth()) return $redirect;
  379. // Fix: valeurs par defaut si le formulaire ne les envoie pas
  380. $headerFontSize = (float) ($request->request->get('header_font_size') ?: '1');
  381. $lineHeight = (float) ($request->request->get('line_height') ?: '1');
  382. $bodyFontSize = $request->request->get('body_font_size', '10');
  383. $titleFontSize = $request->request->get('title_font_size', '18');
  384. $headerPadding = $request->request->get('header_padding', '10');
  385. $logoSize = $request->request->get('logo_size', '80');
  386. $cellPadding = $request->request->get('cell_padding', '5');
  387. $containerPadding = $request->request->get('container_padding', '10');
  388. $photoSize = $request->request->get('photo_size', '110');
  389. $copyright = $request->request->get('copyright') === 'on';
  390. $connection = $this->em->getConnection();
  391. $year = $this->schoolYearService->sessionYearById();
  392. $sub = $this->subRepo->findOneBy(["student" => $std, "schoolYear" => $year]);
  393. $quater = $this->qtRepo->findOneBy(["activated" => true]);
  394. $students = $this->repo->findEnrolledStudentsThisYearInClass($sub->getClassRoom(), $year);
  395. $mainTeacher = $this->mainTeacherRepo->findOneBy([
  396. "classRoom" => $sub->getClassRoom(),
  397. "schoolYear" => $year,
  398. ])->getTeacher();
  399. $filename = "assets/images/student/" . $std->getMatricule() . ".jpg";
  400. $fileExist = file_exists($filename);
  401. // Fix doublons attribution : on ne garde que la derniere attribution
  402. // par cours pour l'annee, evite les lignes en double dans le bulletin
  403. $query = "SELECT sequence.id as sequence, course.id as course_id, course.wording, course.coefficient,
  404. mark.value, mark.weight, mark.rank2, evaluation.mini as mini, evaluation.maxi as maxi,
  405. evaluation.competence, user.full_name
  406. FROM sequence
  407. JOIN evaluation ON evaluation.sequence_id = sequence.id
  408. AND evaluation.class_room_id = :room_id
  409. JOIN course ON evaluation.course_id = course.id
  410. JOIN (
  411. SELECT course_id, MAX(id) AS max_att_id
  412. FROM attribution
  413. WHERE year_id = :year_id
  414. GROUP BY course_id
  415. ) latest_att ON latest_att.course_id = course.id
  416. JOIN attribution ON attribution.id = latest_att.max_att_id
  417. JOIN user ON user.id = attribution.teacher_id
  418. JOIN mark ON evaluation.id = mark.evaluation_id
  419. AND mark.student_id = :student_id
  420. JOIN quater ON sequence.quater_id = quater.id
  421. WHERE quater.id = :quater_id
  422. ORDER BY course.id, sequence.id";
  423. $params = [
  424. 'quater_id' => $quater->getId(),
  425. 'student_id' => $std->getId(),
  426. 'room_id' => $sub->getClassRoom()->getId(),
  427. 'year_id' => $year->getId(),
  428. ];
  429. $result = $connection->executeQuery($query, $params);
  430. $data = $result->fetchAllAssociative();
  431. $html = $this->renderView('student/reportcard/quaterly_2024.html.twig', [
  432. 'year' => $year,
  433. 'quater' => $quater,
  434. 'mainTeacher' => $mainTeacher,
  435. 'data' => $data,
  436. 'std' => $std,
  437. 'students' => $students,
  438. 'room' => $sub->getClassRoom(),
  439. 'lineHeight' => $lineHeight,
  440. 'headerFontSize' => $headerFontSize,
  441. 'bodyFontSize' => $bodyFontSize,
  442. 'titleFontSize' => $titleFontSize,
  443. 'headerPadding' => $headerPadding,
  444. 'logoSize' => $logoSize,
  445. 'cellPadding' => $cellPadding,
  446. 'containerPadding' => $containerPadding,
  447. 'photoSize' => $photoSize,
  448. 'copyright' => $copyright,
  449. 'fileExist' => $fileExist,
  450. ]);
  451. return new Response($pdf->getOutputFromHtml($html), 200, [
  452. 'Content-Type' => 'application/pdf',
  453. 'Content-Disposition' => 'inline; filename="bull_' . $quater->getId() . '_' . $std->getMatricule() . '.pdf"',
  454. ]);
  455. }
  456. /**
  457. * @Route("/{id}/reportCardTrim", name="admin_students_reportcards_quat", requirements={"id"="\d+"})
  458. * @Method("GET")
  459. * @Template()
  460. */
  461. public function reporCardTrimAction(Pdf $pdf, Student $std)
  462. {
  463. if ($redirect = $this->checkAuth()) return $redirect;
  464. $connection = $this->em->getConnection();
  465. $year = $this->schoolYearService->sessionYearById();
  466. $sub = $this->subRepo->findOneBy(["student" => $std, "schoolYear" => $year]);
  467. $quater = $this->qtRepo->findOneBy(["activated" => true]);
  468. $sequences = $this->seqRepo->findBy(["quater" => $quater]);
  469. $filename = "assets/images/student/" . $std->getMatricule() . ".jpg";
  470. $fileExist = file_exists($filename);
  471. $i = 1;
  472. foreach ($sequences as $seq) {
  473. $sql = "CREATE OR REPLACE VIEW V_STUDENT_MARK_SEQ" . $i . " AS
  474. SELECT DISTINCT eval.id as eval, crs.id as crs, room.id as room,
  475. teach.full_name as teacher, modu.id as module, m.value as value, m.weight as weight
  476. FROM mark m
  477. JOIN student std ON m.student_id = std.id
  478. JOIN evaluation eval ON m.evaluation_id = eval.id
  479. JOIN class_room room ON eval.class_room_id = room.id
  480. JOIN course crs ON eval.course_id = crs.id
  481. JOIN attribution att ON att.course_id = crs.id
  482. JOIN user teach ON att.teacher_id = teach.id
  483. JOIN module modu ON modu.id = crs.module_id
  484. JOIN sequence seq ON seq.id = eval.sequence_id
  485. WHERE std.id = ? AND eval.sequence_id = ?
  486. ORDER BY crs.id";
  487. $connection->executeStatement($sql, [$std->getId(), $seq->getId()]);
  488. $i++;
  489. }
  490. $connection->executeStatement(
  491. "CREATE OR REPLACE VIEW V_STUDENT_MARK_QUATER AS
  492. SELECT DISTINCT seq1.crs as crs,
  493. (seq1.value * seq1.weight + seq2.value * seq2.weight) / (seq1.weight + seq2.weight) as value,
  494. greatest(seq1.weight, seq2.weight) as weight,
  495. seq1.weight as weight1, seq2.weight as weight2,
  496. seq1.value as value1, seq2.value as value2,
  497. seq1.teacher as teacher, seq1.module as module, seq1.room as room
  498. FROM V_STUDENT_MARK_SEQ1 seq1
  499. JOIN V_STUDENT_MARK_SEQ2 seq2 ON seq1.crs = seq2.crs
  500. ORDER BY seq1.crs"
  501. );
  502. $dataQuater = $connection->executeQuery("SELECT * FROM V_STUDENT_MARK_QUATER")->fetchAllAssociative();
  503. $html = $this->renderView('student/reportcardTrimApc.html.twig', [
  504. 'year' => $year,
  505. 'quater' => $quater,
  506. 'data' => $dataQuater,
  507. 'sequences' => $sequences,
  508. 'std' => $std,
  509. 'room' => $sub->getClassRoom(),
  510. 'fileExist' => $fileExist,
  511. ]);
  512. return new Response($pdf->getOutputFromHtml($html), 200, [
  513. 'Content-Type' => 'application/pdf',
  514. 'Content-Disposition' => 'inline; filename="bull_' . $quater->getId() . '_' . $std->getMatricule() . '.pdf"',
  515. ]);
  516. }
  517. /**
  518. * @Route("/{id}/reportCardYear", name="admin_students_reportcards_year", requirements={"id"="\d+"})
  519. * @Method("GET")
  520. * @Template()
  521. */
  522. public function reporCardYear(Student $std, Request $request)
  523. {
  524. if ($redirect = $this->checkAuth()) return $redirect;
  525. $headerFontSize = $request->request->get('header_font_size');
  526. $lineHeight = $request->request->get('line_height');
  527. $copyright = $request->request->get('copyright') == "on";
  528. $connection = $this->em->getConnection();
  529. $year = $this->schoolYearService->sessionYearById();
  530. $sequences = $this->seqRepo->findSequenceThisYear($year);
  531. $sub = $this->subRepo->findOneBy(["student" => $std, "schoolYear" => $year]);
  532. $filename = "assets/images/student/" . $std->getMatricule() . ".jpg";
  533. $fileExist = file_exists($filename);
  534. $i = 1;
  535. foreach ($sequences as $seq) {
  536. $sql = "CREATE OR REPLACE VIEW V_STUDENT_MARK_SEQ" . $i . " AS
  537. SELECT DISTINCT eval.id as eval, crs.id as crs, room.id as room, year.id as year,
  538. teach.id as teacher, modu.id as module, m.value as value, m.weight as weight
  539. FROM mark m
  540. JOIN student std ON m.student_id = std.id
  541. JOIN evaluation eval ON m.evaluation_id = eval.id
  542. JOIN class_room room ON eval.class_room_id = room.id
  543. JOIN course crs ON eval.course_id = crs.id
  544. JOIN attribution att ON att.course_id = crs.id
  545. JOIN user teach ON att.teacher_id = teach.id
  546. JOIN module modu ON modu.id = crs.module_id
  547. JOIN sequence seq ON seq.id = eval.sequence_id
  548. JOIN quater quat ON seq.quater_id = quat.id
  549. JOIN school_year year ON quat.school_year_id = year.id
  550. WHERE std.id = ? AND room.id = ? AND eval.sequence_id = ?
  551. ORDER BY crs.id";
  552. $connection->executeStatement($sql, [$std->getId(), $sub->getClassRoom()->getId(), $seq->getId()]);
  553. $i++;
  554. }
  555. $connection->executeStatement(
  556. "CREATE OR REPLACE VIEW V_STUDENT_MARK_QUATER1 AS
  557. SELECT DISTINCT seq1.crs as crs,
  558. (seq1.value * seq1.weight + seq2.value * seq2.weight) / (seq1.weight + seq2.weight) as value,
  559. greatest(seq1.weight, seq2.weight) as weight,
  560. seq1.teacher as teacher, seq1.module as modu, seq1.room as room
  561. FROM V_STUDENT_MARK_SEQ1 seq1
  562. JOIN V_STUDENT_MARK_SEQ2 seq2 ON seq1.crs = seq2.crs
  563. ORDER BY seq1.crs"
  564. );
  565. $connection->executeStatement(
  566. "CREATE OR REPLACE VIEW V_STUDENT_MARK_QUATER2 AS
  567. SELECT DISTINCT seq1.crs as crs,
  568. (seq1.value * seq1.weight + seq2.value * seq2.weight) / (seq1.weight + seq2.weight) as value,
  569. greatest(seq1.weight, seq2.weight) as weight,
  570. seq1.teacher as teacher, seq1.module as modu, seq1.room as room
  571. FROM V_STUDENT_MARK_SEQ3 seq1
  572. JOIN V_STUDENT_MARK_SEQ4 seq2 ON seq1.crs = seq2.crs
  573. ORDER BY seq1.crs"
  574. );
  575. $connection->executeStatement(
  576. "CREATE OR REPLACE VIEW V_STUDENT_MARK_QUATER3 AS
  577. SELECT DISTINCT seq1.crs as crs,
  578. (seq1.value * seq1.weight + seq2.value * seq2.weight) / (seq1.weight + seq2.weight) as value,
  579. greatest(seq1.weight, seq2.weight) as weight,
  580. seq1.teacher as teacher, seq1.module as modu, seq1.room as room
  581. FROM V_STUDENT_MARK_SEQ5 seq1
  582. JOIN V_STUDENT_MARK_SEQ6 seq2 ON seq1.crs = seq2.crs
  583. ORDER BY seq1.crs"
  584. );
  585. $connection->executeStatement(
  586. "CREATE OR REPLACE VIEW ANNUAL_DATA AS
  587. SELECT DISTINCT
  588. course.wording as course, course.coefficient as coef,
  589. module.name as module,
  590. user.full_name as teacher,
  591. quat1.value as value1, quat1.weight as weight1,
  592. quat2.value as value2, quat2.weight as weight2,
  593. quat3.value as value3, quat3.weight as weight3,
  594. (quat1.value * quat1.weight + quat2.value * quat2.weight + quat3.value * quat3.weight)
  595. / (quat1.weight + quat2.weight + quat3.weight) as value
  596. FROM V_STUDENT_MARK_QUATER1 quat1
  597. JOIN class_room ON class_room.id = quat1.room
  598. JOIN course ON course.id = quat1.crs
  599. JOIN module ON course.module_id = quat1.modu
  600. JOIN user ON user.id = quat1.teacher
  601. JOIN V_STUDENT_MARK_QUATER2 quat2 ON quat1.crs = quat2.crs
  602. JOIN V_STUDENT_MARK_QUATER3 quat3 ON quat2.crs = quat3.crs
  603. ORDER BY module"
  604. );
  605. $dataYear = $connection->executeQuery("SELECT * FROM ANNUAL_DATA")->fetchAllAssociative();
  606. $html = $this->renderView('student/reportcardYearApc.html.twig', [
  607. 'year' => $year,
  608. 'data' => $dataYear,
  609. 'std' => $std,
  610. 'room' => $sub->getClassRoom(),
  611. 'headerFontSize' => 0.75 * $headerFontSize,
  612. 'lineHeight' => 1.5 * $lineHeight,
  613. 'copyright' => $copyright,
  614. 'fileExist' => $fileExist,
  615. ]);
  616. return new Response($this->snappy->getOutputFromHtml($html), 200, [
  617. 'Content-Type' => 'application/pdf',
  618. 'Content-Disposition' => 'attachment; filename="BUL_ANN_' . $std->getMatricule() . '.pdf"',
  619. ]);
  620. }
  621. /**
  622. * Return classes for a selected program (AJAX)
  623. *
  624. * @Route("/admin/students/classes", name="admin_students_classes", methods={"GET"})
  625. */
  626. public function classesByProgram(Request $request)
  627. {
  628. $programId = $request->query->get('programId');
  629. if (!$programId) {
  630. return new Response('No program selected', 400);
  631. }
  632. $program = $this->em->getRepository(Program::class)->find($programId);
  633. if (!$program) {
  634. return new Response('Program not found', 404);
  635. }
  636. $classes = [];
  637. foreach ($program->getSections() as $section) {
  638. foreach ($section->getCycles() as $cycle) {
  639. foreach ($cycle->getLevels() as $level) {
  640. foreach ($level->getRooms() as $class) {
  641. $classes[] = $class;
  642. }
  643. }
  644. }
  645. }
  646. return $this->render('student/_classes_tabs.html.twig', [
  647. 'classes' => $classes,
  648. ]);
  649. }
  650. /**
  651. * Return students for a selected class and type (AJAX)
  652. *
  653. * @Route("/admin/students/list", name="admin_students_list", methods={"GET"})
  654. */
  655. public function studentList(Request $request)
  656. {
  657. $classId = $request->query->get('classId');
  658. $type = $request->query->get('type');
  659. if (!$classId) {
  660. return new Response('No class selected', 400);
  661. }
  662. $class = $this->em->getRepository(ClassRoom::class)->find($classId);
  663. if (!$class) {
  664. return new Response('Class not found', 404);
  665. }
  666. switch ($type) {
  667. case "new_students_not_yet_registered_checkbox":
  668. $students = $this->repo->findNewStudentsByClass($class);
  669. break;
  670. case "new_registered_students_checkbox":
  671. $students = $this->repo->findNewRegisteredStudentsByClass($class);
  672. break;
  673. case "registered_former_students_checkbox":
  674. $students = $this->repo->findFormerRegisteredStudentsByClass($class);
  675. break;
  676. case "complete_registered_students_checkbox":
  677. $students = $this->repo->findEnrolledStudentsByClass($class);
  678. break;
  679. default:
  680. $students = [];
  681. }
  682. return $this->render('student/_student_table.html.twig', [
  683. 'students' => $students,
  684. 'class' => $class,
  685. ]);
  686. }
  687. }