src/Controller/Gauge/GaugeController.php line 48

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Gauge;
  3. use App\Controller\AppBaseController;
  4. use App\Entity\Admin\RemoteEndPoint;
  5. use App\Entity\Building\Building;
  6. use App\Entity\Client\Client;
  7. use App\Entity\Gauge\Gauge;
  8. use App\Entity\Gauge\GaugeLimitOverflow;
  9. use App\Entity\Gauge\Supplier;
  10. use App\Entity\User;
  11. use App\Form\Type\Gauge\GaugeCreateFormType;
  12. use App\Form\Type\Gauge\GaugeEditFormType;
  13. use App\Form\Type\Gauge\GaugeEditLevelFormType;
  14. use App\Repository\Gauge\GaugeRepository;
  15. use App\Service\Event\AutoEventGenerator;
  16. use App\Service\File\FileBuildingSync;
  17. use App\Service\Gauge\QrCodeGeneratorService;
  18. use App\Service\Output\HeatingPowerManager;
  19. use App\Service\Output\RemoteReadingsMiner;
  20. use App\Message\UpdateAllReadingsDailyStatusForGauge;
  21. use App\Service\TransHelper;
  22. use App\Service\User\UserSettingsManager;
  23. use App\Service\VueExportHelper;
  24. use App\Service\VueListHelper;
  25. use App\Utils\DateFormat;
  26. use App\Utils\GaugeType;
  27. use App\Utils\Period;
  28. use Symfony\Component\HttpFoundation\JsonResponse;
  29. use Symfony\Component\HttpFoundation\Request;
  30. use Symfony\Component\HttpFoundation\Response;
  31. use Symfony\Component\Messenger\MessageBusInterface;
  32. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  33. use Symfony\Component\Routing\Annotation\Route;
  34. class GaugeController extends AppBaseController
  35. {
  36. /**
  37. * @Route("/building/{buildingId}/gauge-list", name="gauge_list")
  38. * @ParamConverter("building", class="App\Entity\Building\Building", options={"id" = "buildingId"})
  39. * @param Request $request
  40. * @param Building $building
  41. * @param VueListHelper $listHelper
  42. * @return Response
  43. * @throws \Exception
  44. */
  45. public function list(Request $request, Building $building, VueListHelper $listHelper): Response
  46. {
  47. $this->denyAccessUnlessGranted('ROLE_CAN_SEE_GAUGE_LIST');
  48. $this->denyAccessUnlessGrantedBuilding('view', $building);
  49. $returnUrl = $request->get('returnUrl');
  50. if (strpos($returnUrl, $this->router->generate('building_list')) === false) {
  51. $returnUrl = $this->router->generate('building_list');
  52. }
  53. /** @var GaugeRepository $gaugeRepository */
  54. $gaugeRepository = $this->entityManager->getRepository(Gauge::class);
  55. $gaugeRepository->setBuilding($building);
  56. $listData = $listHelper->serializeCustomAppListDefiner($gaugeRepository);
  57. $this->logger->log('page', $request, null, ['name' => $building->getName()]);
  58. return $this->render('app/gauge/list.html.twig', [
  59. 'building' => $building,
  60. 'listData' => $listData,
  61. 'disabledTitle' => $this->translator->trans('list.title.disabled', ['%building%' => $building->getName()], 'gauge'),
  62. 'listDisabledData' => $listHelper->getSerializedDisabled(Gauge::class, $gaugeRepository),
  63. 'returnUrl' => $returnUrl
  64. ]);
  65. }
  66. /**
  67. * @Route("building/{buildingId}/gauge/export", name="gauge_export")
  68. * @ParamConverter("building", class="App\Entity\Building\Building", options={"id" = "buildingId"})
  69. * @param Request $request
  70. * @param Building $building
  71. * @param VueExportHelper $helper
  72. * @return Response
  73. */
  74. public function export(Request $request, Building $building, VueExportHelper $helper): Response
  75. {
  76. $this->denyAccessUnlessGranted('ROLE_CAN_EXPORT_GAUGE');
  77. $this->denyAccessUnlessGrantedBuilding('view', $building);
  78. $data = [
  79. 'term' => $request->query->getAlnum('searchTerm'),
  80. 'on' => $request->query->get('searchOn'),
  81. 'sortBy' => $request->query->get('sortBy'),
  82. 'sortDirection' => $request->query->get('sortDirection'),
  83. 'ids' => $request->get('filteredItems'),
  84. ];
  85. $this->logger->log('gauge.exported', $building, $data['term'], $data['on']);
  86. /** @var GaugeRepository $gaugeRepos */
  87. $gaugeRepos = $this->entityManager->getRepository(Gauge::class);
  88. $gaugeRepos->setBuilding($building);
  89. $gaugeRepos->setIsExport(true);
  90. $listData = $gaugeRepos->getAppListItems();
  91. $title = $this->translator->trans('page.title.list', ['%building%' => $building->getName()], 'gauge');
  92. $safeTitle = preg_replace('/[\/\\\:*?"<>|]/', '_', $title);
  93. $filename = $safeTitle.'_'. date('Y-m-d') . '.xlsx';
  94. try {
  95. return $this->file($helper->getExportFile(Gauge::class, $data, $listData), $filename);
  96. } catch (\Exception $e) {
  97. $this->addFlash(
  98. 'error',
  99. $this->translator->trans('message.cannot_generate_export', [], 'common')
  100. );
  101. return $this->redirectToRoute('gauge_list', ['buildingId' => $building->getId()]);
  102. }
  103. }
  104. /**
  105. * @Route("/building/{buildingId}/gauge/create", name="gauge_create")
  106. * @ParamConverter("building", class="App\Entity\Building\Building", options={"id" = "buildingId"})
  107. *
  108. * @param Request $request
  109. * @param Building $building
  110. * @param TransHelper $transHelper
  111. * @param HeatingPowerManager $heatingPowerManager
  112. * @return Response
  113. */
  114. public function create(
  115. Request $request,
  116. Building $building,
  117. TransHelper $transHelper,
  118. HeatingPowerManager $heatingPowerManager,
  119. QrCodeGeneratorService $qrCodeGenerator
  120. ): Response {
  121. $this->denyAccessUnlessGranted('ROLE_CAN_CREATE_GAUGE');
  122. $this->denyAccessUnlessGrantedBuilding('view', $building);
  123. $gauge = new Gauge();
  124. $gauge->setBuilding($building);
  125. $form = $this->createForm(GaugeCreateFormType::class, $gauge);
  126. if ($request->isMethod('POST')) {
  127. $form->handleRequest($request);
  128. if ($form->isSubmitted() && $form->isValid()) {
  129. $this->logger->log('gauge.created', $request->request->get('gauge_create_form'));
  130. /**
  131. * @var Gauge $gauge
  132. */
  133. $gauge = $form->getData();
  134. // check unknown heating power type
  135. $heatingPowerManager->checkAndProcessHeatingPowerCase($gauge);
  136. $result = $this->processCreateUpdate(
  137. $gauge,
  138. $this->generateUrl('gauge_list', ['buildingId' => $building->getId()]),
  139. $this->translator->trans(
  140. 'message.gauge_created',
  141. [],
  142. 'gauge'
  143. )
  144. );
  145. // Generate QR code for the newly created gauge
  146. try {
  147. $qrPath = $qrCodeGenerator->generateQrCodeForGauge($gauge);
  148. if ($qrPath !== null) {
  149. $gauge->setQrCodePath($qrPath);
  150. $this->entityManager->persist($gauge);
  151. $this->entityManager->flush();
  152. }
  153. } catch (\Exception $e) {
  154. // QR generation failure shouldn't break gauge creation
  155. // Log the error but continue with the response
  156. error_log('QR code generation failed for gauge ' . $gauge->getId() . ': ' . $e->getMessage());
  157. }
  158. $this->entityManager->getRepository(Supplier::class)->updateUsages();
  159. return $result;
  160. }
  161. return $this->errorResponse($form);
  162. }
  163. return $this->render('app/gauge/new.html.twig', [
  164. 'form' => $this->formSerializer->serialize($form, ['groups' => 'create']),
  165. 'building' => $building,
  166. 'translations' => $transHelper->getGaugeFormTranslations()
  167. ]);
  168. }
  169. /**
  170. * @Route("/gauge/edit/{id}", name="gauge_edit")
  171. * @ParamConverter("gauge", class="App\Entity\Gauge\Gauge")
  172. * @param Gauge $gauge
  173. * @param Request $request
  174. * @param TransHelper $transHelper
  175. * @param HeatingPowerManager $heatingPowerManager
  176. * @return Response
  177. */
  178. public function edit(Gauge $gauge, Request $request, TransHelper $transHelper, HeatingPowerManager $heatingPowerManager, FileBuildingSync $fileBuildingSync, MessageBusInterface $messageBus): Response
  179. {
  180. $this->denyAccessUnlessGrantedGauge('view', $gauge);
  181. $originalRemoteAsPrimary = $gauge->isRemoteAsPrimary();
  182. // EM-838: Snapshot building before form may change it
  183. $originalBuilding = $gauge->getBuilding();
  184. $form = $this->createForm(GaugeEditFormType::class, $gauge);
  185. if ($request->isMethod('POST')) {
  186. $this->denyAccessUnlessGrantedGauge('edit', $gauge);
  187. $form->handleRequest($request);
  188. if ($form->isSubmitted() && $form->isValid()) {
  189. $this->logger->log('gauge.updated', $gauge, $request->request->get('gauge_edit_form'));
  190. /**
  191. * @var Gauge $gauge
  192. */
  193. $gauge = $form->getData();
  194. // check unknown heating power type
  195. $heatingPowerManager->checkAndProcessHeatingPowerCase($gauge);
  196. // EM-838: Update file_building for all associated files when building changed
  197. if ($originalBuilding->getId() !== $gauge->getBuilding()->getId()) {
  198. $fileBuildingSync->updateFileBuildingsForGaugeMove($gauge, $originalBuilding);
  199. }
  200. $result = $this->processCreateUpdate(
  201. $gauge,
  202. $this->generateUrl('gauge_list', ['buildingId' => $gauge->getBuilding()->getId()]),
  203. $this->translator->trans('message.gauge_updated', [], 'gauge')
  204. );
  205. $this->entityManager->getRepository(Supplier::class)->updateUsages();
  206. if ($gauge->isRemoteAsPrimary() !== $originalRemoteAsPrimary) {
  207. $messageBus->dispatch(new UpdateAllReadingsDailyStatusForGauge($gauge->getId()));
  208. }
  209. return $result;
  210. }
  211. // return bag of errors
  212. return $this->errorResponse($form);
  213. }
  214. $this->logger->log('gauge.detail', $gauge);
  215. return $this->render('app/gauge/edit.html.twig', [
  216. 'gauge' => $gauge,
  217. 'form' => $this->formSerializer->serialize($form, ['groups' => 'edit']),
  218. 'translations' => $transHelper->getGaugeFormTranslations()
  219. ]);
  220. }
  221. /**
  222. * @Route("/gauge/edit-level/{id}", name="gauge_edit_level")
  223. * @ParamConverter("gauge", class="App\Entity\Gauge\Gauge")
  224. * @param Gauge $gauge
  225. * @param Request $request
  226. * @param TransHelper $transHelper
  227. * @return Response
  228. */
  229. public function editLevel(Gauge $gauge, Request $request, TransHelper $transHelper, FileBuildingSync $fileBuildingSync): Response
  230. {
  231. $this->denyAccessUnlessGrantedGauge('edit', $gauge);
  232. $this->denyAccessUnlessGranted('ROLE_ADMIN');
  233. // EM-838: Snapshot building before form may change it
  234. $originalBuilding = $gauge->getBuilding();
  235. $form = $this->createForm(GaugeEditLevelFormType::class, $gauge);
  236. if ($request->isMethod('POST')) {
  237. $this->denyAccessUnlessGrantedGauge('edit', $gauge);
  238. $form->handleRequest($request);
  239. if ($form->isSubmitted() && $form->isValid()) {
  240. $this->logger->log('gauge.updated', $gauge, $request->request->get('gauge_edit_level_form'));
  241. /**
  242. * @var Gauge $gauge
  243. */
  244. $gauge = $form->getData();
  245. // EM-838: Update file_building for all associated files when building changed
  246. if ($originalBuilding->getId() !== $gauge->getBuilding()->getId()) {
  247. $fileBuildingSync->updateFileBuildingsForGaugeMove($gauge, $originalBuilding);
  248. }
  249. return $this->processCreateUpdate(
  250. $gauge,
  251. $this->generateUrl('gauge_list', ['buildingId' => $gauge->getBuilding()->getId()]),
  252. $this->translator->trans('message.gauge_updated', [], 'gauge')
  253. );
  254. }
  255. // return bag of errors
  256. return $this->errorResponse($form);
  257. }
  258. $this->logger->log('gauge.detail', $gauge);
  259. return $this->render('app/gauge/edit_level.html.twig', [
  260. 'gauge' => $gauge,
  261. 'form' => $this->formSerializer->serialize($form, ['groups' => 'edit']),
  262. 'translations' => $transHelper->getGaugeFormTranslations()
  263. ]);
  264. }
  265. /**
  266. * @Route("/building/{buildingId}/gauge/list-settings", name="gauge_list_settings")
  267. * @ParamConverter("building", class="App\Entity\Building\Building", options={"id" = "buildingId"})
  268. *
  269. * buildingId is compulsory for return button and route after update
  270. *
  271. * @param UserSettingsManager $userSettingsManager
  272. * @param Building $building
  273. * @param TransHelper $transHelper
  274. * @param Request $request
  275. * @return Response
  276. */
  277. public function listSettings(
  278. Request $request,
  279. Building $building,
  280. UserSettingsManager $userSettingsManager,
  281. TransHelper $transHelper
  282. ): Response {
  283. $this->denyAccessUnlessGranted('ROLE_CAN_SEE_GAUGE_LIST');
  284. $this->denyAccessUnlessGrantedBuilding('view', $building);
  285. /** @var User $user */
  286. $user = $this->getUser();
  287. if ($request->isMethod('POST')) {
  288. $requestData = $request->request->all();
  289. $userSettingsManager->saveUserListFieldsByRequestData($user, $requestData, 'gauge');
  290. $this->logger->log('gauge.list_settings_updated', $requestData);
  291. return $this->processCreateUpdate(
  292. $user,
  293. $this->generateUrl('gauge_list', ['buildingId' => $building->getId()]),
  294. $this->translator->trans('message.list_settings.success', [], 'gauge')
  295. );
  296. }
  297. $client = $building->getClient();
  298. $allowedFields = $userSettingsManager->getAllowedFields('gauge', $client);
  299. $userFields = $userSettingsManager->getUserListFields($user, 'gauge', $client);
  300. $this->logger->log('gauge.list_settings_detail');
  301. return $this->render('app/gauge/settings.html.twig', [
  302. 'building' => $building,
  303. 'allowedFields' => $allowedFields,
  304. 'userFields' => $userFields,
  305. 'translations' => $transHelper->getDefaultUserSettingsTranslations()
  306. ]);
  307. }
  308. /**
  309. * @Route("/gauge/disable/{id}", name="gauge_disable")
  310. * @ParamConverter("gauge", class="App\Entity\Gauge\Gauge")
  311. *
  312. * @param Request $request
  313. * @param Gauge $gauge
  314. * @param AutoEventGenerator $autoEventGenerator
  315. * @return Response
  316. */
  317. public function disable(Request $request, Gauge $gauge, AutoEventGenerator $autoEventGenerator): Response
  318. {
  319. $this->denyAccessUnlessGranted('ROLE_CAN_DISABLE_GAUGE');
  320. /** @var User $user */
  321. $user = $this->getUser();
  322. /** @var int|null $disabledReason */
  323. $disabledReason = $request->query->get('disabledReason');
  324. /** @var bool|null $showInOutput */
  325. $showInOutput = $request->query->get('showInOutput');
  326. $gauge->disable($user, $disabledReason, $showInOutput);
  327. try {
  328. $this->entityManager->persist($gauge);
  329. $this->entityManager->flush();
  330. $autoEventGenerator->processGaugeDisabled($gauge, false);
  331. } catch (\Exception $exception) {
  332. $this->addFlash('error', $this->translator->trans('message.gauge_disabled.error', [], 'gauge'));
  333. }
  334. $this->logger->log('page', $request, null, ['name' => $gauge->getName()]);
  335. $this->addFlash('success', $this->translator->trans('message.gauge_disabled.success', [], 'gauge'));
  336. return $this->redirectToRoute('gauge_list', ['buildingId' => $gauge->getBuilding()->getId()]);
  337. }
  338. /**
  339. * @Route("/gauge/enable/{id}", name="gauge_enable")
  340. * @ParamConverter("gauge", class="App\Entity\Gauge\Gauge")
  341. *
  342. * @param Request $request
  343. * @param Gauge $gauge
  344. * @return Response
  345. */
  346. public function enable(Request $request, Gauge $gauge): Response
  347. {
  348. $this->denyAccessUnlessGranted('ROLE_CAN_ENABLE_GAUGE');
  349. $gauge->enable();
  350. try {
  351. $this->entityManager->persist($gauge);
  352. $this->entityManager->flush();
  353. } catch (\Exception $exception) {
  354. $this->addFlash('error', $this->translator->trans('message.gauge_enabled.error', [], 'gauge'));
  355. }
  356. $this->logger->log('page', $request, null, ['name' => $gauge->getName()]);
  357. $this->addFlash('success', $this->translator->trans('message.gauge_enabled.success', [], 'gauge'));
  358. return $this->redirectToRoute('gauge_list', ['buildingId' => $gauge->getBuilding()->getId()]);
  359. }
  360. /**
  361. * @Route("/gauge/delete/{id}", name="gauge_delete")
  362. * @ParamConverter("user", class="App\Entity\Gauge\Gauge")
  363. * @param Gauge $gauge
  364. * @return Response
  365. */
  366. public function delete(Gauge $gauge): Response
  367. {
  368. $this->denyAccessUnlessGranted('ROLE_CAN_DELETE_GAUGE');
  369. $buildingId = $gauge->getBuilding()->getId();
  370. try {
  371. if ($gauge->getReadings()->count() > 0 || $gauge->getInvoices()->count() > 0) {
  372. $this->addFlash('error', $this->translator->trans('message.gauge_has_data.error', [], 'gauge'));
  373. return $this->redirectToRoute('gauge_list', ['buildingId' => $buildingId]);
  374. }
  375. if ($gauge->getChildren()->count() > 0) {
  376. $this->addFlash('error', $this->translator->trans('message.gauge_has_children.error', [], 'gauge'));
  377. return $this->redirectToRoute('gauge_list', ['buildingId' => $buildingId]);
  378. }
  379. /** @var User $user */
  380. $user = $this->getUser();
  381. if ($gauge->getImport() !== null) {
  382. $gauge->getImport()->addWarning([
  383. 'gaugeId' => $gauge->getId(),
  384. 'text' => $this->translator->trans('gauge.warning.deleted', [
  385. '%user%' => $user->getName(),
  386. '%identifier%' => $gauge->getProperties()->getIdentifier(),
  387. '%date%' => (new \DateTime())->format(DateFormat::NORMALIZED),
  388. '%gaugeId%' => $gauge->getId(),
  389. ], 'import')
  390. ]);
  391. }
  392. $this->logger->log('gauge.deleted', $gauge);
  393. $this->entityManager->remove($gauge);
  394. $this->entityManager->flush();
  395. $this->addFlash('success', $this->translator->trans('message.gauge_deleted.success', [], 'gauge'));
  396. } catch (\Exception $exception) {
  397. $this->addFlash('error', $this->translator->trans('message.gauge_deleted.error', [], 'gauge'));
  398. }
  399. return $this->redirectToRoute('gauge_list', ['buildingId' => $buildingId]);
  400. }
  401. /**
  402. * @Route("/api/gauge/choices-by-client/{id}", name="api_gauge_choices_by_client")
  403. * @ParamConverter("client", class="App\Entity\Client\Client")
  404. *
  405. * @param Client $client
  406. * @return Response
  407. */
  408. public function getChoicesByClient(Client $client): Response
  409. {
  410. $this->denyAccessUnlessGranted('ROLE_CAN_EDIT_USER');
  411. $choices = [];
  412. foreach ($client->getBuildings() as $building) {
  413. foreach ($building->getGauges() as $gauge) {
  414. $choices[] = [
  415. 'html' => $gauge->getNameWithBuilding(),
  416. 'value' => (string)$gauge->getId(),
  417. ];
  418. }
  419. }
  420. return new JsonResponse($choices);
  421. }
  422. /**
  423. * @Route("/gauge/{id}/limit-overflow", name="gauge_limit_overflow_list")
  424. * @ParamConverter("gauge", class="App\Entity\Gauge\Gauge")
  425. *
  426. * @param Gauge $gauge
  427. * @param VueListHelper $listHelper
  428. * @return Response
  429. * @throws \Exception
  430. */
  431. public function distributionRateOptimize(Gauge $gauge, VueListHelper $listHelper): Response
  432. {
  433. $this->denyAccessUnlessGrantedGauge('view', $gauge);
  434. $repos = $this->entityManager->getRepository(GaugeLimitOverflow::class);
  435. $repos->setGauge($gauge);
  436. return $this->render('app/generic/list.html.twig', [
  437. 'listData' => $listHelper->serializeCustomAppListDefiner($repos),
  438. 'pageTitle' => $this->translator->trans('page.title.gauge_limit_overflow', array('%gauge%' => $gauge->getName()), 'gauge'),
  439. 'topButtons' => [[
  440. 'label' => $this->translator->trans('button.label.back_to_gauge_detail', [], 'gauge'),
  441. 'url' => $this->generateUrl('gauge_edit', ['id' => $gauge->getId()]),
  442. 'class' => 'btn-secondary'
  443. ]]
  444. ]);
  445. }
  446. /**
  447. * @Route("/api/gauge-remote-endpoint-period/{id}", name="api_remote_endpoint_period")
  448. *
  449. * @param int $id
  450. * @param RemoteReadingsMiner $remoteReadingsMiner
  451. * @return Response
  452. */
  453. public function getActionCategoryChoicesByParent(int $id, RemoteReadingsMiner $remoteReadingsMiner, GaugeType $gaugeType): Response
  454. {
  455. $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  456. /** @var User $loggedUser */
  457. $loggedUser = $this->getUser();
  458. // $remoteEndpointRepos = $this->entityManager->getRepository(RemoteEndPoint::class);
  459. // $allowedEndpoints = $remoteEndpointRepos->getEndpointsForPairingInApp($loggedUser->getClient());
  460. //
  461. // if (!array_key_exists($id, $allowedEndpoints)) {
  462. // throw $this->createNotFoundException('Not found or allowed RemoteEndPoint');
  463. // }
  464. $frequencyInMinutes = $remoteReadingsMiner->getMinutesFrequencyByLastRecords($id);
  465. return new JsonResponse([
  466. 'minutes' => $frequencyInMinutes,
  467. 'period' => $gaugeType->getFormattedPeriodByMinutes($frequencyInMinutes),
  468. ]);
  469. }
  470. }