src/Controller/Panel/ChartsController.php line 445

Open in your IDE?
  1. <?php
  2. /**
  3.  * @license Silk Software House Sp. Z O.O.
  4.  */
  5. declare(strict_types=1);
  6. namespace App\Controller\Panel;
  7. use App\Annotation\ApplySettings;
  8. use App\Assembler\DataSampleAssembler;
  9. use App\Controller\Panel\Traits\FormatableTwigDataTrait;
  10. use App\DataBridge\DataAbstraction;
  11. use App\DataBridge\DataTransformers\AgeOfMaturityTransformer;
  12. use App\DataBridge\DataTransformers\TemperatureTransformer;
  13. use App\DataBridge\DataTransformers\StrengthTransformer;
  14. use App\DataBridge\Implementations\CanvasJsTransformer;
  15. use App\DataBridge\Implementations\DataTransformerInterface;
  16. use App\DataBridge\StrengthPointsAddHelper;
  17. use App\Entity\ChannelSetting;
  18. use App\Entity\Measurement;
  19. use App\Entity\Notification;
  20. use App\Entity\TemperatureNotification;
  21. use App\Entity\User;
  22. use App\Entity\UserSetting;
  23. use App\Factory\MeasurementViewFactory;
  24. use App\Helpers\DatabaseAwareTrait;
  25. use App\Helpers\MessagesTrait;
  26. use App\Helpers\SecurityAwareTrait;
  27. use App\Model\View\MeasurementView;
  28. use App\Helpers\RecipeResolver;
  29. use App\Repository\UserSettingRepository;
  30. use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
  31. use Exception;
  32. use Doctrine\ORM\EntityManagerInterface;
  33. use FOS\RestBundle\Controller\Annotations as Rest;
  34. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  35. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  36. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  37. use Symfony\Component\HttpFoundation\RedirectResponse;
  38. use Symfony\Component\HttpFoundation\Request;
  39. use Symfony\Component\HttpFoundation\Response;
  40. use Symfony\Contracts\Translation\TranslatorInterface;
  41. /**
  42.  *  Device list controller.
  43.  */
  44. class ChartsController extends AbstractController
  45. {
  46.     use DatabaseAwareTrait;
  47.     use FormatableTwigDataTrait;
  48.     use SecurityAwareTrait;
  49.     use MessagesTrait;
  50.     /** @var DataAbstraction */
  51.     private DataAbstraction $chartDataAbstraction;
  52.     /** @var DataSampleAssembler */
  53.     private DataSampleAssembler $dataSampleAssembler;
  54.     /** @var RecipeResolver */
  55.     private RecipeResolver $recipeResolver;
  56.     /** @var array */
  57.     private array $strengthPoints = [];
  58.     /** @var TranslatorInterface */
  59.     private TranslatorInterface $translator;
  60.     /** @var MeasurementViewFactory */
  61.     private MeasurementViewFactory $factory;
  62.     /**
  63.      * todo: refactor. base controller or trait injection
  64.      * TableController constructor.
  65.      *
  66.      * @param DataAbstraction $chartDataAbstraction
  67.      * @param DataSampleAssembler $dataSampleAssembler
  68.      * @param RecipeResolver $recipeResolver
  69.      * @param TranslatorInterface $translator
  70.      * @param EntityManagerInterface $entityManager
  71.      * @param MeasurementViewFactory $factory
  72.      */
  73.     public function __construct(
  74.         DataAbstraction $chartDataAbstraction,
  75.         DataSampleAssembler $dataSampleAssembler,
  76.         RecipeResolver $recipeResolver,
  77.         TranslatorInterface $translator,
  78.         EntityManagerInterface $entityManager,
  79.         MeasurementViewFactory $factory
  80.     ) {
  81.         $this->chartDataAbstraction $chartDataAbstraction;
  82.         $this->dataSampleAssembler $dataSampleAssembler;
  83.         $this->recipeResolver $recipeResolver;
  84.         $this->translator $translator;
  85.         $this->entityManager $entityManager;
  86.         $this->factory $factory;
  87.     }
  88.     /**
  89.      * @IsGranted("show", subject="measurement")
  90.      *
  91.      * @Rest\Get(
  92.      *     "/panel/device/measurement/{id}/chart/strength/{isRelative}",
  93.      *      name="device_measurement_chart_strength",
  94.      *      requirements={"isRelative"="(true|false)"},
  95.      *      defaults={"isRelative"=false}
  96.      *     )
  97.      * @Rest\Post(
  98.      *     "/panel/device/measurement/{id}/chart/strength/{isRelative}",
  99.      *      name="device_measurement_chart_strength_post",
  100.      *      requirements={"isRelative"="(true|false)"},
  101.      *      defaults={"isRelative"=false}
  102.      *  )
  103.      *
  104.      * @Template("panel/device-list/canvasjs_chart.html.twig")
  105.      *
  106.      * @param Measurement $measurement
  107.      * @param string|null $isRelative
  108.      *
  109.      * @return array
  110.      *
  111.      * @throws Exception
  112.      */
  113.     public function strengthChart(Measurement $measurement, ?string $isRelative null): array
  114.     {
  115.         $form $this->createAndHandleNameForm($measurement);
  116.         $relative = ('true' === $isRelative);
  117.         return [
  118.                 'formName' => $form->createView(),
  119.                 'channelNameForm' => $this->createAndHandleChannelNameForm()->createView(),
  120.                 'channelRecipeForm' => $this->createAndHandleChannelRecipeForm()->createView(),
  121.                 'device' => $this->formatDeviceData($measurement),
  122.                 'measurement_meta' => $this->formatMeasurementData($measurement),
  123.                 'measurement' => $measurement,
  124.                 'channels_meta' => $this->formatChannelsData($measurement),
  125.                 'type' => 'strength',
  126.                 'title' => $this->chartDataAbstraction->getTitle(),
  127.                 'unit' => $this->chartDataAbstraction->getUnit(),
  128.                 'legend' => $relative 'Relative time [h]' 'Real time',
  129.                 'yLabel' => 'Strength [MPa]',
  130.                 'isRelativeTimescale' => $relative 0,
  131.                 'startDate' => $measurement->getStartedAt(),
  132.             ] + $this->getFontSize();
  133.     }
  134.     /**
  135.      * @IsGranted("show", subject="measurement")
  136.      *
  137.      * @Rest\Post("/panel/device/measurement/{id}/strength-notification", name="device_measurement_strength_notification_create")
  138.      *
  139.      * @param Measurement             $measurement
  140.      * @param Request                 $request
  141.      * @param EntityManagerInterface  $entityManager
  142.      * @param TranslatorInterface     $translator
  143.      *
  144.      * @return RedirectResponse
  145.      */
  146.     public function createStrengthNotification(Measurement $measurementRequest $requestEntityManagerInterface $entityManagerTranslatorInterface $translator): RedirectResponse
  147.     {
  148.         $redirect = new RedirectResponse($request->headers->get('referer') ?: $this->generateUrl('device_measurement_chart_strength', ['id' => $measurement->getId()]));
  149.         if (!$this->isCsrfTokenValid('strength_notification'$request->request->get('token'))) {
  150.             $this->addFlash('danger'$translator->trans('Wrong CSRF token provided. Please try to perform this action again.'));
  151.             return $redirect;
  152.         }
  153.         $email trim((string) $request->request->get('email'));
  154.         if (!filter_var($emailFILTER_VALIDATE_EMAIL)) {
  155.             $this->addFlash('danger'$translator->trans('strength-notification.error.email'));
  156.             return $redirect;
  157.         }
  158.         $strengthTrigger str_replace(',''.'trim((string) $request->request->get('strengthTrigger')));
  159.         if (!is_numeric($strengthTrigger) || (float) $strengthTrigger <= 0) {
  160.             $this->addFlash('danger'$translator->trans('strength-notification.error.target'));
  161.             return $redirect;
  162.         }
  163.         $selectedChannelIds array_map('intval', (array) $request->request->get('channels', []));
  164.         if (!$measurement->getSetting() || === count($selectedChannelIds)) {
  165.             $this->addFlash('danger'$translator->trans('strength-notification.error.channels'));
  166.             return $redirect;
  167.         }
  168.         $created 0;
  169.         $duplicates 0;
  170.         $repository $entityManager->getRepository(Notification::class);
  171.         foreach ($measurement->getSetting()->getChannelSettings() as $channelSetting) {
  172.             if (!in_array($channelSetting->getId(), $selectedChannelIdstrue) || $channelSetting->isDeleted()) {
  173.                 continue;
  174.             }
  175.             $existing $repository->findOneBy([
  176.                 'user' => $this->getUser(),
  177.                 'channelSetting' => $channelSetting,
  178.                 'triggeredAt' => null,
  179.                 'strengthTrigger' => (float) $strengthTrigger,
  180.                 'email' => $email,
  181.             ]);
  182.             if ($existing) {
  183.                 ++$duplicates;
  184.                 continue;
  185.             }
  186.             $notification = (new Notification())
  187.                 ->setUser($this->getUser())
  188.                 ->setChannelSetting($channelSetting)
  189.                 ->setStrengthTrigger((float) $strengthTrigger)
  190.                 ->setEmail($email);
  191.             $entityManager->persist($notification);
  192.             ++$created;
  193.         }
  194.         if (=== $created && === $duplicates) {
  195.             $this->addFlash('danger'$translator->trans('strength-notification.error.channels'));
  196.             return $redirect;
  197.         }
  198.         if ($created 0) {
  199.             $entityManager->flush();
  200.         }
  201.         if ($created 0) {
  202.             $this->addFlash('success'$translator->trans('strength-notification.success', ['%count%' => $created]));
  203.         }
  204.         if ($duplicates 0) {
  205.             $this->addFlash('info'$translator->trans('strength-notification.duplicates', ['%count%' => $duplicates]));
  206.         }
  207.         return $redirect;
  208.     }
  209.     /**
  210.      * @IsGranted("show", subject="measurement")
  211.      *
  212.      * @Rest\Post("/panel/device/measurement/{id}/temperature-notification", name="device_measurement_temperature_notification_create")
  213.      */
  214.     public function createTemperatureNotification(
  215.         Measurement $measurement,
  216.         Request $request,
  217.         EntityManagerInterface $entityManager,
  218.         TranslatorInterface $translator
  219.     ): RedirectResponse {
  220.         $redirect $this->redirectToRoute('device_measurement_chart_temperature', [
  221.             'id' => $measurement->getId(),
  222.         ]);
  223.         $token $request->request->get('token');
  224.         if (!is_string($token) || !$this->isCsrfTokenValid('temperature_notification'$token)) {
  225.             $this->addFlash('danger'$translator->trans('Wrong CSRF token provided. Please try to perform this action again.'));
  226.             return $redirect;
  227.         }
  228.         $emailInput $request->request->get('email');
  229.         $email is_string($emailInput) ? trim($emailInput) : '';
  230.         if (!filter_var($emailFILTER_VALIDATE_EMAIL)) {
  231.             $this->addFlash('danger'$translator->trans('temperature-notification.error.email'));
  232.             return $redirect;
  233.         }
  234.         $submittedTemperature $request->request->get('temperatureTrigger');
  235.         $temperatureInput is_string($submittedTemperature)
  236.             ? str_replace(',''.'trim($submittedTemperature))
  237.             : '';
  238.         $temperatureTrigger is_numeric($temperatureInput) ? (float) $temperatureInput null;
  239.         if (
  240.             null === $temperatureTrigger
  241.             || !is_finite($temperatureTrigger)
  242.             || $temperatureTrigger TemperatureNotification::MINIMUM_TEMPERATURE
  243.             || $temperatureTrigger TemperatureNotification::MAXIMUM_TEMPERATURE
  244.         ) {
  245.             $this->addFlash('danger'$translator->trans('temperature-notification.error.target'));
  246.             return $redirect;
  247.         }
  248.         $submittedDirection $request->request->get('direction');
  249.         $direction is_string($submittedDirection) ? $submittedDirection '';
  250.         if (!in_array($directionTemperatureNotification::DIRECTIONStrue)) {
  251.             $this->addFlash('danger'$translator->trans('temperature-notification.error.direction'));
  252.             return $redirect;
  253.         }
  254.         $submittedChannels $request->request->get('channels', []);
  255.         $selectedChannelIds = [];
  256.         if (is_array($submittedChannels)) {
  257.             foreach ($submittedChannels as $channelId) {
  258.                 if (is_int($channelId) || (is_string($channelId) && ctype_digit($channelId))) {
  259.                     $selectedChannelIds[] = (int) $channelId;
  260.                 }
  261.             }
  262.         }
  263.         $selectedChannelIds array_values(array_unique($selectedChannelIds));
  264.         if (!$measurement->getSetting() || === count($selectedChannelIds)) {
  265.             $this->addFlash('danger'$translator->trans('temperature-notification.error.channels'));
  266.             return $redirect;
  267.         }
  268.         $user $this->getUser();
  269.         if (!$user instanceof User) {
  270.             throw $this->createAccessDeniedException();
  271.         }
  272.         $created 0;
  273.         $duplicates 0;
  274.         $repository $entityManager->getRepository(TemperatureNotification::class);
  275.         foreach ($measurement->getSetting()->getChannelSettings() as $channelSetting) {
  276.             if (
  277.                 !in_array($channelSetting->getId(), $selectedChannelIdstrue)
  278.                 || $channelSetting->isDeleted()
  279.                 || $channelSetting->getIsStopped()
  280.             ) {
  281.                 continue;
  282.             }
  283.             $existing $repository->findOneBy([
  284.                 'user' => $user,
  285.                 'channelSetting' => $channelSetting,
  286.                 'triggeredAt' => null,
  287.                 'temperatureTrigger' => $temperatureTrigger,
  288.                 'direction' => $direction,
  289.                 'email' => $email,
  290.             ]);
  291.             if ($existing) {
  292.                 ++$duplicates;
  293.                 continue;
  294.             }
  295.             $notification = (new TemperatureNotification())
  296.                 ->setUser($user)
  297.                 ->setChannelSetting($channelSetting)
  298.                 ->setTemperatureTrigger($temperatureTrigger)
  299.                 ->setDirection($direction)
  300.                 ->setEmail($email);
  301.             $entityManager->persist($notification);
  302.             ++$created;
  303.         }
  304.         if (=== $created && === $duplicates) {
  305.             $this->addFlash('danger'$translator->trans('temperature-notification.error.channels'));
  306.             return $redirect;
  307.         }
  308.         if ($created 0) {
  309.             try {
  310.                 $entityManager->flush();
  311.             } catch (UniqueConstraintViolationException $exception) {
  312.                 $this->addFlash('info'$translator->trans('temperature-notification.concurrent-duplicate'));
  313.                 return $redirect;
  314.             }
  315.             $this->addFlash('success'$translator->trans('temperature-notification.success', ['%count%' => $created]));
  316.         }
  317.         if ($duplicates 0) {
  318.             $this->addFlash('info'$translator->trans('temperature-notification.duplicates', ['%count%' => $duplicates]));
  319.         }
  320.         return $redirect;
  321.     }
  322.     /**
  323.      * @IsGranted("show", subject="measurement")
  324.      *
  325.      * @Rest\Get(
  326.      *     "/panel/device/measurement/{id}/chart/maturity/{isRelative}",
  327.      *      name="device_measurement_chart_maturity",
  328.      *      requirements={"isRelative"="(true|false)"},
  329.      *      defaults={"isRelative"=false}
  330.      *     )
  331.      * @Rest\Post(
  332.      *     "/panel/device/measurement/{id}/chart/maturity/{isRelative}",
  333.      *      name="device_measurement_chart_maturity_post",
  334.      *      requirements={"isRelative"="(true|false)"},
  335.      *      defaults={"isRelative"=false}
  336.      *     )
  337.      *
  338.      * @Template("panel/device-list/canvasjs_chart.html.twig")
  339.      *
  340.      * @param Measurement $measurement
  341.      * @param string|null $isRelative
  342.      *
  343.      * @return array
  344.      *
  345.      * @throws Exception
  346.      */
  347.     public function maturityChart(Measurement $measurement, ?string $isRelative null): array
  348.     {
  349.         $form $this->createAndHandleNameForm($measurement);
  350.         $relative = ('true' === $isRelative);
  351.         return [
  352.                 'formName' => $form->createView(),
  353.                 'channelNameForm' => $this->createAndHandleChannelNameForm()->createView(),
  354.                 'channelRecipeForm' => $this->createAndHandleChannelRecipeForm()->createView(),
  355.                 'device' => $this->formatDeviceData($measurement),
  356.                 'measurement_meta' => $this->formatMeasurementData($measurement),
  357.                 'measurement' => $measurement,
  358.                 'channels_meta' => $this->formatChannelsData($measurement),
  359.                 'type' => 'maturity',
  360.                 'title' => $this->chartDataAbstraction->getTitle(),
  361.                 'unit' => $this->chartDataAbstraction->getUnit(),
  362.                 'legend' => $relative 'Relative time [h]' 'Real time',
  363.                 'yLabel' => 'Age of maturity [h]',
  364.                 'isRelativeTimescale' => $relative 0,
  365.                 'startDate' => $measurement->getStartedAt(),
  366.             ] + $this->getFontSize();
  367.     }
  368.     /**
  369.      * #AppCache()
  370.      *
  371.      * @IsGranted("show", subject="measurement")
  372.      *
  373.      * @ApplySettings()
  374.      *
  375.      * @Rest\Get(
  376.      *     "/panel/device/measurement/{id}/chart/{type}/{isRelative}/data",
  377.      *      name="device_measurement_chart_api",
  378.      *      requirements={"isRelative"="(true|false)"},
  379.      *      defaults={"isRelative"=false}
  380.      * )
  381.      *
  382.      * @param Measurement $measurement
  383.      * @param string      $type
  384.      * @param string|null $isRelative
  385.      *
  386.      * @return Response
  387.      *
  388.      * @throws Exception
  389.      */
  390.     public function chartData(Measurement $measurementstring $type, ?string $isRelative null): Response
  391.     {
  392.         $implementation $this->getValueTransformer(('true' === $isRelative), $type);
  393.         $chartDataAbstraction $this->getDataAbstraction($this->factory->create($measurement), $implementation);
  394.         try {
  395.             if (in_array($type, ['temperature''strength'], true)) {
  396.                 $dataArray $chartDataAbstraction->getDataArray();
  397.                 if ($measurement->getSetting() && $measurement->getSetting()->getIsAutomaticStrengthPointsEnabled()) {
  398.                     $usedRecipes $chartDataAbstraction->getUsedRecipes();
  399.                     $strengthPoints $this->buildAutomaticStrengthPoints($dataArray$usedRecipes);
  400.                     if ($strengthPoints) {
  401.                         $helper = (new StrengthPointsAddHelper($dataArray));
  402.                         if ('temperature' === $type) {
  403.                             $startingPointForRelativeTimePoints = ('true' === $isRelative) ? $implementation->getFirstDatePoint() : null;
  404.                             $helper->setUsedRecipe($usedRecipes);
  405.                             $helper->setFirstDateForRelativeTimesets($startingPointForRelativeTimePoints);
  406.                             $data $helper->addStrengthPoints($strengthPoints)->getJson();
  407.                         } else {
  408.                             $data $helper->addStrengthValuePoints($strengthPoints)->getJson();
  409.                         }
  410.                     } else {
  411.                         $data json_encode($dataArrayJSON_PRESERVE_ZERO_FRACTION);
  412.                     }
  413.                 } else {
  414.                     $data json_encode($dataArrayJSON_PRESERVE_ZERO_FRACTION);
  415.                 }
  416.             } else {
  417.                 $data $chartDataAbstraction->getJson();
  418.             }
  419.         } catch (Exception  $e) {
  420.             $this->addMessage($e->getMessage(), 'danger');
  421.             throw $e;
  422.         }
  423.         $data str_replace(
  424.             ['{{Channel}}''{{default recipe}}''{{CalculatedChannel}}''{{AddedChannel}}'],
  425.             [$this->translator->trans('Channel'), $this->translator->trans('Default recipe'), $this->translator->trans('calculated-channel'), $this->translator->trans('added-channel-legend')],
  426.             $data
  427.         );
  428.         if ($implementation->isError()) {
  429.             $this->addMessageArray($implementation->getMessages(), 'info');
  430.         }
  431.         $data $this->mergeInMessagesJson($data);
  432.         $response = new Response($data);
  433.         $response->headers->set('Content-Type''application/json');
  434.         return $response;
  435.     }
  436.     /**
  437.      * @IsGranted("edit", subject="measurement")
  438.      *
  439.      * @ApplySettings()
  440.      *
  441.      * @Rest\Post("/panel/device/measurement/{id}/chart/automatic-strength-points-toggle", name="device_measurement_automatic_strength_points_toggle")
  442.      * @Rest\Post("/panel/device/measurement/{id}/chart/temperature/automatic-strength-points-toggle", name="device_measurement_temperature_automatic_strength_points_toggle")
  443.      */
  444.     public function toggleAutomaticStrengthPoints(Measurement $measurementRequest $request): Response
  445.     {
  446.         if (!$this->isCsrfTokenValid('automatic_strength_points'$request->get('token'))) {
  447.             return $this->json([
  448.                 'success' => false,
  449.                 'message' => $this->translator->trans('Wrong CSRF token provided. Please try to perform this action again.'),
  450.             ], Response::HTTP_NOT_FOUND);
  451.         }
  452.         if (!$measurement->getSetting()) {
  453.             return $this->json([
  454.                 'success' => false,
  455.                 'message' => $this->translator->trans('Entity was not found.'),
  456.             ], Response::HTTP_NOT_FOUND);
  457.         }
  458.         $enabled filter_var($request->get('enabled'), FILTER_VALIDATE_BOOLEAN);
  459.         $setting $measurement->getSetting();
  460.         $setting->setIsAutomaticStrengthPointsEnabled($enabled);
  461.         $this->entityManager->persist($setting);
  462.         $this->entityManager->flush();
  463.         return $this->json([
  464.             'success' => true,
  465.             'enabled' => $setting->getIsAutomaticStrengthPointsEnabled(),
  466.             'message' => $this->translator->trans(
  467.                 $setting->getIsAutomaticStrengthPointsEnabled()
  468.                     ? 'temperature-chart.strength-points.enabled'
  469.                     'temperature-chart.strength-points.disabled'
  470.             ),
  471.         ], Response::HTTP_OK);
  472.     }
  473.     /**
  474.      * @IsGranted("edit", subject="measurement")
  475.      *
  476.      * @ApplySettings()
  477.      *
  478.      * @Rest\Post("/panel/device/measurement/{id}/chart/temperature/split-toggle", name="device_measurement_temperature_split_toggle")
  479.      */
  480.     public function toggleTemperatureSplit(Measurement $measurementRequest $request): Response
  481.     {
  482.         if (!$this->isCsrfTokenValid('temperature_split_view'$request->get('token'))) {
  483.             return $this->json([
  484.                 'success' => false,
  485.                 'message' => $this->translator->trans('Wrong CSRF token provided. Please try to perform this action again.'),
  486.             ], Response::HTTP_NOT_FOUND);
  487.         }
  488.         if (!$measurement->getSetting()) {
  489.             return $this->json([
  490.                 'success' => false,
  491.                 'message' => $this->translator->trans('Entity was not found.'),
  492.             ], Response::HTTP_NOT_FOUND);
  493.         }
  494.         $setting $measurement->getSetting();
  495.         $setting->setIsTemperatureSplitView(!$setting->getIsTemperatureSplitView());
  496.         $this->entityManager->persist($setting);
  497.         $this->entityManager->flush();
  498.         return $this->json([
  499.             'success' => true,
  500.             'enabled' => $setting->getIsTemperatureSplitView(),
  501.             'message' => $this->translator->trans(
  502.                 $setting->getIsTemperatureSplitView()
  503.                     ? 'temperature-chart.split.enabled'
  504.                     'temperature-chart.split.disabled'
  505.             ),
  506.         ], Response::HTTP_OK);
  507.     }
  508.     /**
  509.      * @Rest\Post(
  510.      *     "/panel/device/channel/{id}/diagram/{group}",
  511.      *     name="device_measurement_temperature_channel_move",
  512.      *     requirements={"group"="(1|2)"}
  513.      * )
  514.      */
  515.     public function moveTemperatureChannel(ChannelSetting $channelint $groupRequest $request): Response
  516.     {
  517.         if (!$this->isCsrfTokenValid('temperature_split_move'$request->get('token'))) {
  518.             return $this->json([
  519.                 'success' => false,
  520.                 'message' => $this->translator->trans('Wrong CSRF token provided. Please try to perform this action again.'),
  521.             ], Response::HTTP_NOT_FOUND);
  522.         }
  523.         $measurementSetting $channel->getMeasurementSetting();
  524.         $measurement $measurementSetting $measurementSetting->getMeasurement() : null;
  525.         if (!$measurement) {
  526.             return $this->json([
  527.                 'success' => false,
  528.                 'message' => $this->translator->trans('Entity was not found.'),
  529.             ], Response::HTTP_NOT_FOUND);
  530.         }
  531.         $this->denyAccessUnlessGranted('edit'$measurement);
  532.         $channel->setDiagramGroup($group);
  533.         $this->entityManager->persist($channel);
  534.         $hasSecondaryChannels false;
  535.         foreach ($measurementSetting->getChannelSettings() as $setting) {
  536.             if (!$setting->isDeleted() && === $setting->getDiagramGroup()) {
  537.                 $hasSecondaryChannels true;
  538.                 break;
  539.             }
  540.         }
  541.         $measurementSetting->setIsTemperatureSplitView($hasSecondaryChannels);
  542.         $this->entityManager->persist($measurementSetting);
  543.         $this->entityManager->flush();
  544.         return $this->json([
  545.             'success' => true,
  546.             'splitEnabled' => $measurementSetting->getIsTemperatureSplitView(),
  547.             'message' => $this->translator->trans('temperature-chart.split.moved'),
  548.         ], Response::HTTP_OK);
  549.     }
  550.     private function buildAutomaticStrengthPoints(array $dataArray, array $usedRecipes): array
  551.     {
  552.         $strengthPoints = [];
  553.         foreach ($dataArray['chartData'] ?? [] as $index => $channel) {
  554.             if (!empty($channel['isDeleted']) || empty($usedRecipes[$index])) {
  555.                 continue;
  556.             }
  557.             $fcc28 $usedRecipes[$index]->getFcc();
  558.             if (!$fcc28 || $fcc28 <= 0) {
  559.                 continue;
  560.             }
  561.             $strengthPoints[$index] = [
  562.                 round($fcc28 0.252),
  563.                 round($fcc28 0.502),
  564.                 round($fcc28 0.752),
  565.             ];
  566.         }
  567.         return $strengthPoints;
  568.     }
  569.     /**
  570.      * @IsGranted("show", subject="measurement")
  571.      *
  572.      * @ApplySettings()
  573.      *
  574.      * @Rest\Get(
  575.      *     "/panel/device/measurement/{id}/chart/temperature/{isRelative}",
  576.      *      name="device_measurement_chart_temperature",
  577.      *      requirements={"isRelative"="(true|false)"},
  578.      *      defaults={"isRelative"=false}
  579.      *     )
  580.      * @Rest\Post(
  581.      *     "/panel/device/measurement/{id}/chart/temperature/{isRelative}",
  582.      *      name="device_measurement_chart_temperature_post",
  583.      *      requirements={"isRelative"="(true|false)"},
  584.      *      defaults={"isRelative"=false}
  585.      *     )
  586.      *
  587.      * @Template("panel/device-list/canvasjs_chart.html.twig")
  588.      *
  589.      * @param Measurement $measurement
  590.      * @param string|null $isRelative
  591.      *
  592.      * @return array
  593.      *
  594.      * @throws Exception
  595.      */
  596.     public function temperatureChart(Measurement $measurement, ?string $isRelative null): array
  597.     {
  598.         $form $this->createAndHandleNameForm($measurement);
  599.         $relative = ('true' === $isRelative);
  600.         return [
  601.                 'formName' => $form->createView(),
  602.                 'channelNameForm' => $this->createAndHandleChannelNameForm()->createView(),
  603.                 'channelRecipeForm' => $this->createAndHandleChannelRecipeForm()->createView(),
  604.                 'device' => $this->formatDeviceData($measurement),
  605.                 'measurement_meta' => $this->formatMeasurementData($measurement),
  606.                 'measurement' => $measurement,
  607.                 'measurement_extended_name' => $measurement->getNameWithRelativeNumberPerDevice(),
  608.                 'channels_meta' => $this->formatChannelsData($measurement),
  609.                 'type' => 'temperature',
  610.                 'title' => $this->chartDataAbstraction->getTitle(),
  611.                 'unit' => $this->chartDataAbstraction->getUnit(),
  612.                 'legend' => $relative 'Relative time [h]' 'Real time',
  613.                 'yLabel' => 'Temperature [°C]',
  614.                 'isRelativeTimescale' => $relative 0,
  615.                 'startDate' => $measurement->getStartedAt(),
  616.             ] + $this->getFontSize();
  617.     }
  618.     /**
  619.      *
  620.      * @Rest\Put("/panel/channel/edit/{id}/{type}", name="panel_channel_edit_put")
  621.      *
  622.      * @param ChannelSetting $channel
  623.      * @param string $type
  624.      *
  625.      * @return Response
  626.      *
  627.      * @throws Exception
  628.      */
  629.     public function channelEdit(ChannelSetting $channelstring $type): Response
  630.     {
  631.         if ('name' === $type) {
  632.             $response $this->createAndHandleChannelNameForm($channel);
  633.         } elseif ('recipe' === $type) {
  634.             $response $this->createAndHandleChannelRecipeForm($channel);
  635.         } else {
  636.             throw new Exception('Invalid request type');
  637.         }
  638.         return new Response($response);
  639.     }
  640.     /**
  641.      * @param MeasurementView          $view
  642.      * @param DataTransformerInterface $implementation
  643.      *
  644.      * @return DataAbstraction
  645.      */
  646.     private function getDataAbstraction(MeasurementView $viewDataTransformerInterface $implementation): DataAbstraction
  647.     {
  648.         return $this->chartDataAbstraction
  649.             ->setImplementation($implementation)
  650.             ->setMeasurementView($view);
  651.     }
  652.     /**
  653.      * @param bool        $isRelative
  654.      * @param string|null $type
  655.      * todo : handle other transformers
  656.      *
  657.      * @return DataTransformerInterface
  658.      */
  659.     private function getValueTransformer(bool $isRelative, ?string $type null): DataTransformerInterface
  660.     {
  661.         $typeToValueTransformer = [
  662.             'default' => new TemperatureTransformer(),
  663.             'temperature' => new TemperatureTransformer(),
  664.             'maturity' => new AgeOfMaturityTransformer(),
  665.             'strength' => new StrengthTransformer(),
  666.         ];
  667.         $valueTransformer $typeToValueTransformer[$type] ?? $typeToValueTransformer['default'];
  668.         $implementation = (new CanvasJsTransformer($valueTransformer$this->recipeResolver$this->translator))
  669.             ->setRelativeTimeAxis($isRelative);
  670.         $implementation->setRelativeTimeAxis($isRelative);
  671.         return $implementation;
  672.     }
  673.     /**
  674.      * @return array
  675.      */
  676.     private function getFontSize(): array
  677.     {
  678.         /** @var User $user */
  679.         $user $this->getUser();
  680.         /** @var UserSettingRepository $userSettingRepository */
  681.         $userSettingRepository $this->getRepository(UserSetting::class);
  682.         $userSetting $userSettingRepository->getOrCreateForUser($user);
  683.         return ['fontSizeCoefficient' => ($userSetting->getFontSizeCoefficient())];
  684.     }
  685. }