src/Controller/Back/AccueilController.php line 531

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Back;
  3. use App\Form\User\UserFirstConnexionType;
  4. use App\Repository\AbonnementRepository;
  5. use App\Repository\FactureRepository;
  6. use App\Repository\GarageRepository;
  7. use App\Repository\LogsGarageRepository;
  8. use App\Repository\OccupationGaragesRepository;
  9. use App\Repository\OffreRepository;
  10. use App\Repository\ParameterRepository;
  11. use App\Repository\ZChartRevenuesRepository;
  12. use App\Repository\ZChartSubscribersRepository;
  13. use App\Repository\ZChartUsageRepository;
  14. use App\Repository\ZChartUsersRepository;
  15. use App\Repository\ZShelterMonthlyOccupationRepository;
  16. use App\Security\JwtAuthenticator;
  17. use App\Service\ShelterService;
  18. use App\Services\ParameterService;
  19. use App\Utils\DateCalculator;
  20. use Doctrine\ORM\EntityManagerInterface;
  21. use Symfony\Bundle\FrameworkBundle\Console\Application;
  22. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  23. use Symfony\Component\Console\Input\ArrayInput;
  24. use Symfony\Component\HttpFoundation\JsonResponse;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\HttpFoundation\RequestStack;
  27. use Symfony\Component\HttpKernel\Exception\GoneHttpException;
  28. use Symfony\Component\HttpKernel\KernelInterface;
  29. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  30. use Symfony\Component\Routing\Annotation\Route;
  31. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  32. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  33. use Symfony\Component\Security\Core\Security;
  34. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  35. class AccueilController extends AbstractController
  36. {
  37.     public function __construct(
  38.         private readonly ParameterRepository $parameterRepository,
  39.         private readonly GarageRepository $shelterRepository,
  40.         private readonly FactureRepository $invoiceRepository,
  41.         private readonly AbonnementRepository $subscriptionRepository,
  42.         private readonly OffreRepository $offerRepository,
  43.         private readonly LogsGarageRepository $logsGarageRepository,
  44.         private readonly OccupationGaragesRepository $occupationGaragesRepository,
  45.         private readonly ZShelterMonthlyOccupationRepository $shelterMonthlyOccupationRepository,
  46.         private readonly ParameterService $parameterService,
  47.         private readonly EntityManagerInterface $em,
  48.         private readonly DateCalculator $dateCalculator,
  49.         private readonly ZChartRevenuesRepository $chartRevenuesRepository,
  50.         private readonly ZChartUsageRepository $chartUsageRepository,
  51.         private readonly ZChartUsersRepository $chartUsersRepository,
  52.         private readonly ZChartSubscribersRepository $chartSubscribersRepository,
  53.         private readonly KernelInterface $kernelInterface,
  54.     ) {}
  55.     /**
  56.      * @Route("/connexion", name="connexion")
  57.      */
  58.     public function connexion(AuthenticationUtils $authenticationUtilsRequest $request)
  59.     {
  60.         if ($this->getUser() && $this->isGranted('IS_REMEMBERED')) {
  61.             return $this->redirectToRoute('home');
  62.         }
  63.         // Parameters management
  64.         $parameters $this->parameterRepository->findAll();
  65.         $request->getSession()->set('params'$parameters);
  66.         // get the login error if there is one
  67.         $error $authenticationUtils->getLastAuthenticationError();
  68.         if ($request->query->has('error')) {
  69.             $error = new BadCredentialsException($request->query->get('error'));
  70.         }
  71.         // last username entered by the user
  72.         $lastUsername $authenticationUtils->getLastUsername();
  73.         if ($request->query->has('lastUsername')) {
  74.             $lastUsername $request->query->get('lastUsername');
  75.         }
  76.         return $this->render('login/index.html.twig', [
  77.             'last_username' => $lastUsername,
  78.             'error' => $error,
  79.         ]);
  80.     }
  81.     /**
  82.      * @Route("/first-connexion/{jwt}", name="first_connexion")
  83.      */
  84.     public function firstConnexion(
  85.         string $jwt,
  86.         JwtAuthenticator $jwtAuthenticator,
  87.         UserPasswordHasherInterface $passwordHasher,
  88.         Request $request
  89.     ) {
  90.         try {
  91.             $user $jwtAuthenticator->checkJwt($jwt);
  92.         } catch (\Exception $e) {
  93.             throw new GoneHttpException('token invalid :: ' $e->getMessage());
  94.         }
  95.         $form $this->createForm(UserFirstConnexionType::class, $user);
  96.         $form->handleRequest($request);
  97.         if ($form->isSubmitted() && $form->isValid()) {
  98.             $hashedPassword $passwordHasher->hashPassword(
  99.                 $user,
  100.                 $user->getPassword()
  101.             );
  102.             $user->setPassword($hashedPassword);
  103.             $user->setIsActive(true);
  104.             $user->deleteEmailAuthCode();
  105.             $this->addFlash('success''success.firstConnexionSuccess');
  106.             $this->em->persist($user);
  107.             $this->em->flush();
  108.             return $this->redirectToRoute('connexion');
  109.         }
  110.         return $this->render('login/first-connexion.html.twig', [
  111.             'form' => $form->createView(),
  112.             'user' => $user,
  113.         ]);
  114.     }
  115.     /**
  116.      * @Route("/accueil", name="home")
  117.      */
  118.     public function home()
  119.     {
  120.         if ('1' === $this->parameterService->get('sensorModeOnly')) {
  121.             return $this->redirectToRoute('homeSensorModeOnly');
  122.         }
  123.         try {
  124.             if (=== count($this->chartRevenuesRepository->findAll())) {
  125.                 $this->preloadChartsCommand(
  126.                     $this->kernelInterface,
  127.                     'app:preloadDashboardChartsCommand'
  128.                 );
  129.             }
  130.         } catch (\Exception $e) {
  131.             $this->redirectToRoute('liste_abris');
  132.         }
  133.         $occupiedPlaces 0;
  134.         $totalAbundance 0;
  135.         $nbSubMax 0;
  136.         $nbSheltersLimited 0;
  137.         $subscriptionLimited false;
  138.         $abundance 0;
  139.         $garages $this->shelterRepository->getAllGarages();
  140.         foreach ($garages as $garage) {
  141.             if (-!== $garage->getNbTheoriqueAbo()) {
  142.                 $subscriptionLimited true;
  143.                 $nbSubMax += $garage->getNbTheoriqueAbo();
  144.                 $abundance = ($garage->getNbTheoriqueAbo() / $garage->getCapacite()) * 100;
  145.                 $totalAbundance += $abundance;
  146.                 ++$nbSheltersLimited;
  147.             }
  148.         }
  149.         if ($nbSheltersLimited) {
  150.             $abundance round($totalAbundance $nbSheltersLimited1);
  151.         }
  152.         // capacity graph
  153.         $totalCapacity $this->shelterRepository->getTotalCapacity();
  154.         // Occupation graph
  155.         $occupations $this->occupationGaragesRepository->findRecentOccupation();
  156.         foreach ($occupations as $occupation) {
  157.             $occupiedPlaces += $occupation->getOccupation();
  158.         }
  159.         $freePlaces $totalCapacity $occupiedPlaces;
  160.         return $this->render('home/home.html.twig', [
  161.             'user' => $this->getUser(),
  162.             'activeShelters' => count($this->shelterRepository->getConnectedShelters()),
  163.             'totalShelters' => count($this->shelterRepository->getAllGarages()),
  164.             'totalCapacity' => $totalCapacity,
  165.             'nbAbo' => count($this->subscriptionRepository->getCurrentSubscriptions()),
  166.             'nbSubMax' => $nbSubMax,
  167.             'abundance' => $abundance,
  168.             'occupiedPlaces' => $occupiedPlaces,
  169.             'freePlaces' => $freePlaces,
  170.             'sensor' => (bool) $this->parameterService->get('capteur'),
  171.             'subscriptionLimited' => $subscriptionLimited,
  172.         ]);
  173.     }
  174.     /**
  175.      * @Route("/accueil-captage", name="homeSensorModeOnly")
  176.      */
  177.     public function homeSensorModeOnly(ShelterService $shelterService)
  178.     {
  179.         try {
  180.             if (=== count($this->shelterMonthlyOccupationRepository->findAll())) {
  181.                 $this->preloadChartsCommand(
  182.                     $this->kernelInterface,
  183.                     'app:preloadDataChartsCommand'
  184.                 );
  185.             }
  186.         } catch (\Exception $e) {
  187.             $this->redirectToRoute('liste_abris');
  188.         }
  189.         $shelters $this->shelterRepository->findAll();
  190.         $occupiedPlaces 0;
  191.         $totalCapacity = (int) $this->shelterRepository->getTotalCapacity();
  192.         $occupations $this->occupationGaragesRepository->findRecentOccupation();
  193.         foreach ($occupations as $occupation) {
  194.             $occupiedPlaces += $occupation->getOccupation();
  195.         }
  196.         $freePlaces $totalCapacity $occupiedPlaces;
  197.         $sheltersOccupation $shelterService->getSheltersOccupationData($shelters);
  198.         return $this->render('home/sensor_mode_only.html.twig', [
  199.             'occupiedPlaces' => $occupiedPlaces,
  200.             'freePlaces' => $freePlaces,
  201.             'totalCapacity' => $totalCapacity,
  202.             'shelters' => $sheltersOccupation,
  203.         ]);
  204.     }
  205.     /**
  206.      * @Route("/accueil-captage/getSheltersOccupation",
  207.      *  name="homeGetSheltersOccupation",
  208.      *  options={"expose"=true},
  209.      *  methods={"GET"}
  210.      * )
  211.      */
  212.     public function getSheltersOccupation(ShelterService $shelterService)
  213.     {
  214.         $shelters $this->shelterRepository->findAll();
  215.         $sheltersOccupation $shelterService->getSheltersOccupationData($shelters);
  216.         return new JsonResponse($sheltersOccupation);
  217.     }
  218.     /**
  219.      * @Route("/accueil-captage/last24hOccupation",
  220.      *  name="homeLast24hOccupation",
  221.      *  options={"expose"=true},
  222.      *  methods={"GET"}
  223.      * )
  224.      */
  225.     public function getLast24hOccupation()
  226.     {
  227.         $data $this->occupationGaragesRepository->getOccupationData(
  228.             '%Y-%m-%d %H:00:00',
  229.             '-24 hours',
  230.             'hour'
  231.         );
  232.         $maxCapacity = (int) $this->shelterRepository->getTotalCapacity();
  233.         $mappedData $this->mapOccupationData($data'H''hour'$maxCapacity'h00');
  234.         return new JsonResponse($mappedData);
  235.     }
  236.     /**
  237.      * @Route("/accueil-captage/last8DaysOccupation",
  238.      *  name="homeLast8DaysOccupation",
  239.      *  options={"expose"=true},
  240.      *  methods={"GET"}
  241.      * )
  242.      */
  243.     public function getLast8DaysOccupation()
  244.     {
  245.         $data $this->occupationGaragesRepository->getOccupationData(
  246.             '%Y-%m-%d',
  247.             '-8 days',
  248.             'day'
  249.         );
  250.         $maxCapacity = (int) $this->shelterRepository->getTotalCapacity();
  251.         $mappedData $this->mapOccupationData($data'EEE''day'$maxCapacity);
  252.         return new JsonResponse($mappedData);
  253.     }
  254.     /**
  255.      * @Route("/accueil-captage/last12MonthsOccupation",
  256.      *  name="homeLast12MonthsOccupation",
  257.      *  options={"expose"=true},
  258.      *  methods={"GET"}
  259.      * )
  260.      */
  261.     public function getLast12MonthsOccupation()
  262.     {
  263.         $data $this->mergeCurrentAndAnnualOccupationData();
  264.         return new JsonResponse($data);
  265.     }
  266.     /**
  267.      * @Route(
  268.      *      "/accueil/getRevenues",
  269.      *      name="homeRevenues",
  270.      *      options={"expose"=true},
  271.      *      methods={"GET"})
  272.      *
  273.      * @return JsonResponse
  274.      */
  275.     public function getRevenues()
  276.     {
  277.         $now = (new \DateTime());
  278.         $firstDayOfThisMonth $this->dateCalculator->fromNow('first day of this month');
  279.         $monthRevenues $this->invoiceRepository->getRevenuesByDateRange($firstDayOfThisMonth$now);
  280.         $lastMonth $this->chartRevenuesRepository->getLastMonth();
  281.         $year $this->chartRevenuesRepository->getRevenuesFromStartOfYear() + $monthRevenues;
  282.         return new JsonResponse([
  283.             'month' => $monthRevenues,
  284.             'lastMonth' => $lastMonth $lastMonth 0,
  285.             'year' => $year $year 0,
  286.         ]);
  287.     }
  288.     /**
  289.      * @Route(
  290.      *      "/accueil/getOffersDistribution",
  291.      *      name="homeOffersDistribution",
  292.      *      options={"expose"=true},
  293.      *      methods={"GET"}
  294.      * )
  295.      */
  296.     public function getOffersDistribution()
  297.     {
  298.         $offerArray = [];
  299.         $offers $this->offerRepository->getOffresVisible();
  300.         foreach ($offers as $offer) {
  301.             $abos $this->subscriptionRepository->findByOffer($offer);
  302.             if (array_key_exists($offer->getDescription(), $offerArray)) {
  303.                 $offerArray[$offer->getDescription()] += count($abos);
  304.                 continue;
  305.             }
  306.             $offerArray[$offer->getDescription()] = count($abos);
  307.         }
  308.         arsort($offerArray);
  309.         return new JsonResponse($offerArray);
  310.     }
  311.     /**
  312.      * @Route(
  313.      *      "/accueil/getConnectedShelters",
  314.      *      name="homeConnectedShelters",
  315.      *      options={"expose"=true},
  316.      *      methods={"GET"}
  317.      * )
  318.      */
  319.     public function getConnectedShelters()
  320.     {
  321.         $connectedShelters count($this->shelterRepository->getConnectedShelters());
  322.         $totalShelters count($this->shelterRepository->getAllGarages());
  323.         $res = ($connectedShelters $totalShelters) * 100;
  324.         return new JsonResponse($res);
  325.     }
  326.     /**
  327.      * @Route(
  328.      *      "/accueil/getUsage",
  329.      *      name="homeGetUsage",
  330.      *      options={"expose"=true},
  331.      *      methods={"GET"}
  332.      * )
  333.      */
  334.     public function getUsage()
  335.     {
  336.         $days = [];
  337.         for ($i 0$i <= 10; ++$i) {
  338.             $days[] = $this->dateCalculator->getDatePart($i'days''E');
  339.         }
  340.         $logs $this->chartUsageRepository->getUsage();
  341.         $arrayLogs = [];
  342.         $arrayUsers = [];
  343.         foreach ($logs as $log) {
  344.             $arrayLogs[] = $log->getNbAccess();
  345.             $arrayUsers[] = $log->getNbUsers();
  346.         }
  347.         // get data for the current day
  348.         $today $this->dateCalculator->fromNow('today')->format('Y-m-d');
  349.         $logsLastDay $this->logsGarageRepository->getAccessHistoryByDay($today);
  350.         $usersLastDay $this->logsGarageRepository->getUsersByPeriod($today'day');
  351.         $arrayLogs[count($arrayLogs) - 1] = count($logsLastDay);
  352.         $arrayUsers[count($arrayUsers) - 1] = count($usersLastDay);
  353.         $dataUsage = [
  354.             $arrayLogs,
  355.             $arrayUsers,
  356.             array_reverse($days),
  357.         ];
  358.         return new JsonResponse($dataUsage);
  359.     }
  360.     /**
  361.      * @Route(
  362.      *      "/accueil/getSubscription",
  363.      *      name="homeGetSubscription",
  364.      *      options={"expose"=true},
  365.      *      methods={"GET"}
  366.      * )
  367.      */
  368.     public function getSubscription()
  369.     {
  370.         $currentSubscriptions $this->subscriptionRepository->getCurrentSubscriptions();
  371.         return new JsonResponse(count($currentSubscriptions));
  372.     }
  373.     /**
  374.      * @Route(
  375.      *      "/accueil/getOccupation",
  376.      *      name="homeGetOccupation",
  377.      *      options={"expose"=true},
  378.      *      methods={"GET"}
  379.      * )
  380.      */
  381.     public function getOccupation()
  382.     {
  383.         $occupiedPlaces 0;
  384.         $occupations $this->occupationGaragesRepository->findRecentOccupation();
  385.         foreach ($occupations as $occupation) {
  386.             $occupiedPlaces += $occupation->getOccupation();
  387.         }
  388.         return new JsonResponse($occupiedPlaces);
  389.     }
  390.     /**
  391.      * @Route(
  392.      *      "/accueil/getActiveSubscriptions",
  393.      *      name="homeGetActiveSubscriptions",
  394.      *      options={"expose"=true},
  395.      *      methods={"GET"}
  396.      * )
  397.      */
  398.     public function getActiveSubscriptions()
  399.     {
  400.         $subscriptionsArray $offerNames $months $dataActiveSubscription = [];
  401.         $users $this->chartUsersRepository->getLast12Months();
  402.         $offers $this->offerRepository->getOffresVisible();
  403.         $usersArray array_map(function ($user) {
  404.             return $user->getValue();
  405.         }, $users);
  406.         foreach ($offers as $offer) {
  407.             $offerNames[] = $offer->getDescription();
  408.             $logs $this->chartSubscribersRepository->getLast12Months($offer);
  409.             $subscriptionsArray[] = array_map(function ($log) {
  410.                 return $log->getValue();
  411.             }, $logs);
  412.         }
  413.         for ($i 0$i <= 11; ++$i) {
  414.             array_push($months$this->dateCalculator->getDatePart($i'month''MMM'));
  415.         }
  416.         $months array_reverse($months);
  417.         // Get last month's data
  418.         $today $this->dateCalculator->fromNow('today')->format('Y-m-d');
  419.         $monthlySubsArray array_map(function ($offer) use ($today) {
  420.             return count($this->subscriptionRepository->findByMonthAndOffer($offer$today));
  421.         }, $offers);
  422.         foreach ($subscriptionsArray as $index => &$sous_tableau) {
  423.             $sous_tableau[count($sous_tableau) - 1] = $monthlySubsArray[$index];
  424.         }
  425.         $monthlyUsers $this->logsGarageRepository->getUsersByPeriod($today'month');
  426.         $usersArray[count($usersArray) - 1] = count($monthlyUsers);
  427.         array_push($dataActiveSubscription$offerNames$subscriptionsArray$usersArray$months);
  428.         return new JsonResponse($dataActiveSubscription);
  429.     }
  430.     public function two_fa(RequestStack $requestStack)
  431.     {
  432.         $authenticationException $this->getLastAuthenticationException($requestStack);
  433.         $attempts2Fa $requestStack->getSession()->get('attempts2Fa');
  434.         if (=== $attempts2Fa) {
  435.             return $this->redirectToRoute('logout');
  436.         }
  437.         return $this->render('security/2fa.html.twig', [
  438.             'authenticationError' => $authenticationException $authenticationException->getMessageKey() : null,
  439.             'authenticationErrorData' => $authenticationException $authenticationException->getMessageData() : null,
  440.             'checkPathUrl' => '/2fa_check',
  441.             'authCodeParameterName' => '_auth_code',
  442.             'displayTrustedOption' => false,
  443.             'isCsrfProtectionEnabled' => false,
  444.         ]);
  445.     }
  446.     /**
  447.      * @Route("/mentionslegales", name="legalNotices",  methods={"GET"})
  448.      */
  449.     public function legalNotices()
  450.     {
  451.         return $this->render('notice/index.html.twig');
  452.     }
  453.     protected function getLastAuthenticationException(RequestStack $requestStack): ?AuthenticationException
  454.     {
  455.         $authException $requestStack->getSession()->get(Security::AUTHENTICATION_ERROR);
  456.         if ($authException instanceof AuthenticationException) {
  457.             $requestStack->getSession()->remove(Security::AUTHENTICATION_ERROR);
  458.             return $authException;
  459.         }
  460.         return null// The value does not come from the security component.
  461.     }
  462.     private function mapOccupationData(
  463.         array $occupations,
  464.         string $dateFormat,
  465.         string $dateKey,
  466.         int $maxCapacity,
  467.         string $datetimeTermination ''
  468.     ): array {
  469.         $timezone $this->dateCalculator->getDateTimeZone();
  470.         $formatter $this->dateCalculator->newIntlDateFormatter($dateFormat);
  471.         return [
  472.             'labels' => array_map(function ($occupation) use ($formatter$dateKey$datetimeTermination$timezone) {
  473.                 $date = new \DateTime($occupation[$dateKey], $timezone);
  474.                 return $formatter->format($date) . $datetimeTermination;
  475.             }, $occupations),
  476.             'avgOccupation' => array_map(function ($occupation) {
  477.                 return floor((float) $occupation['avgOccupation']);
  478.             }, $occupations),
  479.             'minOccupation' => array_map(function ($occupation) {
  480.                 return floor((int) $occupation['minOccupation']);
  481.             }, $occupations),
  482.             'maxOccupation' => array_map(function ($occupation) {
  483.                 return floor((int) $occupation['maxOccupation']);
  484.             }, $occupations),
  485.             'maxCapacity' => $maxCapacity,
  486.         ];
  487.     }
  488.     private function mapShelterMonthlyOccupationData(
  489.         array $occupations,
  490.         string $dateFormat,
  491.         int $maxCapacity
  492.     ): array {
  493.         $formatter $this->dateCalculator->newIntlDateFormatter($dateFormat);
  494.         $monthlyData = [];
  495.         foreach ($occupations as $occupation) {
  496.             $formattedMonth $formatter->format($occupation->getMonth());
  497.             if (!isset($monthlyData[$formattedMonth])) {
  498.                 $monthlyData[$formattedMonth] = [
  499.                     'avgOccupation' => 0,
  500.                     'minOccupation' => 0,
  501.                     'maxOccupation' => 0,
  502.                 ];
  503.             }
  504.             $monthlyData[$formattedMonth]['avgOccupation'] += $occupation->getAvgOccupation();
  505.             $monthlyData[$formattedMonth]['minOccupation'] += $occupation->getMinOccupation();
  506.             $monthlyData[$formattedMonth]['maxOccupation'] += $occupation->getMaxOccupation();
  507.         }
  508.         $result = [
  509.             'labels' => [],
  510.             'avgOccupation' => [],
  511.             'minOccupation' => [],
  512.             'maxOccupation' => [],
  513.             'maxCapacity' => $maxCapacity,
  514.         ];
  515.         foreach ($monthlyData as $month => $data) {
  516.             $result['labels'][] = $month;
  517.             $result['avgOccupation'][] = $data['avgOccupation'];
  518.             $result['minOccupation'][] = $data['minOccupation'];
  519.             $result['maxOccupation'][] = $data['maxOccupation'];
  520.         }
  521.         return $result;
  522.     }
  523.     private function mergeCurrentAndAnnualOccupationData(): array
  524.     {
  525.         $last12MonthsData $this->shelterMonthlyOccupationRepository->getLast12FullMonthsData();
  526.         $last12MonthsOccupationArray $this->mapShelterMonthlyOccupationData(
  527.             $last12MonthsData,
  528.             'MMM',
  529.             (int) $this->shelterRepository->getTotalCapacity()
  530.         );
  531.         $currentMonthData $this->occupationGaragesRepository->getOccupationData(
  532.             '%Y-%m',
  533.             'first day of this month',
  534.             'month'
  535.         );
  536.         $currentMonthOccupationArray = [
  537.             'labels' => [],
  538.             'avgOccupation' => [],
  539.             'minOccupation' => [],
  540.             'maxOccupation' => [],
  541.         ];
  542.         if (!empty($currentMonthData)) {
  543.             $data $currentMonthData[0];
  544.             $formattedMonth $this->dateCalculator->newIntlDateFormatter('MMM')
  545.                 ->format(new \DateTime($data['month'] . '-01'))
  546.             ;
  547.             $currentMonthOccupationArray = [
  548.                 'labels' => [$formattedMonth],
  549.                 'avgOccupation' => [floor((float) $data['avgOccupation'])],
  550.                 'minOccupation' => [(int) $data['minOccupation']],
  551.                 'maxOccupation' => [(int) $data['maxOccupation']],
  552.             ];
  553.         }
  554.         return array_merge_recursive($last12MonthsOccupationArray$currentMonthOccupationArray);
  555.     }
  556.     private function preloadChartsCommand(
  557.         KernelInterface $kernel,
  558.         string $command
  559.     ): void {
  560.         $application = new Application($kernel);
  561.         $application->setAutoExit(false);
  562.         $input = new ArrayInput([
  563.             'command' => $command,
  564.         ]);
  565.         $application->run($input);
  566.     }
  567. }