src/Controller/Api/Internal/ApiController.php line 263

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Api\Internal;
  3. use App\Controller\Admin\AdminDashbordController;
  4. use App\Controller\Admin\RequestLogCrudController;
  5. use App\Entity\ControllerUser;
  6. use App\Entity\Eg\Admin\UserGroup;
  7. use App\Entity\Eg\Osm\Parking;
  8. use App\Entity\ElectricList;
  9. use App\Entity\Fine;
  10. use App\Entity\Gps;
  11. use App\Entity\LapPair;
  12. use App\Entity\LogExport;
  13. use App\Entity\Logs\RequestLogHistory;
  14. use App\Entity\Operations\FineOperation;
  15. use App\Entity\Operations\OperationType;
  16. use App\Entity\Operations\RequestLogOperation;
  17. use App\Entity\ParkingStatus;
  18. use App\Entity\Photo;
  19. use App\Entity\RequestLog;
  20. use App\Entity\RouteLap;
  21. use App\Entity\VerificationStatus;
  22. use App\ValueObject\LicensePlate;
  23. use Doctrine\ORM\EntityManagerInterface;
  24. use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
  25. use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
  26. use Symfony\Component\HttpFoundation\JsonResponse;
  27. use Symfony\Component\HttpFoundation\Request;
  28. use Symfony\Component\HttpFoundation\Response;
  29. use Symfony\Component\Routing\Annotation\Route;
  30. final class ApiController extends AdminDashbordController
  31. {
  32.     #[Route(path: '/fine-logs/{fineId}', name: 'api_fine_logs', methods: ['GET'])]
  33.     public function getFineLogs(EntityManagerInterface $manager, $fineId): JsonResponse
  34.     {
  35.         $response = [];
  36.         /** @var FineOperation[] $data */
  37.         $data = $manager->getRepository(FineOperation::class)->findByFine($fineId, ['id' => 'DESC']);
  38.         foreach ($data as $operation) {
  39.             $response[] = $operation->getHistoryData();
  40.         }
  41.         return new JsonResponse($response);
  42.     }
  43.     #[Route(path: '/request-log-operations/{requestLogId}', name: 'api_request_log_operations', methods: ['GET'])]
  44.     public function getRequestLogOperations(EntityManagerInterface $manager, $requestLogId): JsonResponse
  45.     {
  46.         $response = [];
  47.         // Tutaj dodajemy jeszcze weryfikacje użytkowników, bo się w trochę złym miejscu to dodawało, a teraz już za późno zmieniać strukturę
  48.         // (po to też są te while poniżej)
  49.         $verifications = $manager->getRepository(RequestLogHistory::class)->findBy([
  50.             'requestLog' => $requestLogId,
  51.             'method' => 'verify'
  52.         ], ['id' => 'DESC']);
  53.         $i = 0;
  54.         /** @var RequestLogOperation[] $data */
  55.         $data = $manager->getRepository(RequestLogOperation::class)->findByRequestLog($requestLogId, ['id' => 'DESC']);
  56.         foreach ($data as $operation) {
  57.             while ($i < count($verifications) && $verifications[$i]->getCreatedAt() > $operation->getCreatedAt()) {
  58.                 $response[] = $verifications[$i]->imitateOperationHistoryData();
  59.                 $i += 1;
  60.             }
  61.             $response[] = $operation->getHistoryData();
  62.         }
  63.         // To w teorii nie powinno być potrzebne, no bo jakim cudem ma ręczna weryfikacja byćc przed operacjami powstającymi przy utworzeniu odczytu, ale niech zostanie
  64.         while ($i < count($verifications)) {
  65.             $response[] = $verifications[$i]->imitateOperationHistoryData();
  66.             $i += 1;
  67.         }
  68.         return new JsonResponse($response);
  69.     }
  70.     #[Route(path: '/request-log-history/{requestLogId}', name: 'api_request_log_history', methods: ['GET'])]
  71.     public function getRequestLogHistory(EntityManagerInterface $manager, $requestLogId): JsonResponse
  72.     {
  73.         $response = [];
  74.         $requestLog = $manager->getRepository(RequestLog::class)->find($requestLogId);
  75.         $data = $manager->getRepository(RequestLogHistory::class)->filterOutMethod('verify', $requestLogId);
  76.         foreach ($data as $log) {
  77.             $logData = $log->getKeyData();
  78.             if ($log->getMethod() === RequestLogHistory::ELECTRIC_METHOD) {
  79.                 $requestArr = \json_decode($log->getRequestExternal(), true);
  80.                 $licensePlate = $requestArr['license_plate'] ?? null;
  81.                 $electricCar = null;
  82.                 if ($licensePlate) {
  83.                     $electricCar = $manager->getRepository(ElectricList::class)->find($licensePlate);
  84.                 }
  85.                 if ($electricCar) {
  86.                     $logData['formattedResponses'][] = [
  87.                         'created_at' => $electricCar->getCreatedAt()->format('Y-m-d H:i:s'),
  88.                         'photo' => $_ENV['PHOTO_ADDRESS'] . $electricCar->getPhotoPath()
  89.                     ];
  90.                 } else {
  91.                     $logData['formattedResponses'][] = [
  92.                         'created_at' => null,
  93.                         'photo' => null
  94.                     ];
  95.                 }
  96.             }
  97.             $logData['licensePlate'] = LicensePlate::fromHttpRequest($logData['request'], $requestLog->getAnonymized()) ?? 'Brak';
  98.             $response[] = $logData;
  99.         }
  100.         return new JsonResponse($response);
  101.     }
  102.     #[Route(path: '/is-super-admin', name: 'api_is_super_admin', methods: ['GET'])]
  103.     public function isSuperAdmin(): JsonResponse
  104.     {
  105.         return new JsonResponse($this->isGranted('ROLE_SUPER_ADMIN'));
  106.     }
  107.     #[Route(path: '/is-badanie-granted', name: 'api_is_badanie_granted', methods: ['GET'])]
  108.     public function isBadanieGranted(): JsonResponse
  109.     {
  110.         return new JsonResponse($this->isGranted('ROLE_BADANIE'));
  111.     }
  112.     /**
  113.      * Metoda do wyciągania  zdjęć danego obiektu RequestLog
  114.      */
  115.     #[Route(path: '/get-photos/{requestLogId}', name: 'api_get_photos')]
  116.     public function getPhotos(EntityManagerInterface $manager, $requestLogId): JsonResponse
  117.     {
  118.         $photos = $manager->getRepository(Photo::class)->findByLogIds([$requestLogId]);
  119.         return new JsonResponse($photos);
  120.     }
  121.     /**
  122.      * Metoda do wyciągnięcie gps dla kontrolerów z ograniczeniem do co 5 odczytu lokalizacji
  123.      *
  124.      * dateFrom - data od (format "yyyy-mm-dd hh:mm:ss", np.: "2022-06-09 00:00:00")
  125.      * dateTo - data do (format "yyyy-mm-dd hh:mm:ss", np.: "2022-06-09 23:59:59")
  126.      */
  127.     #[Route(path: '/get-controllers-routes', name: 'api_get_controllers_routes', methods: ['GET'])]
  128.     public function getControllersRoutes(EntityManagerInterface $manager, Request $request)
  129.     {
  130.         $data = $request->query;
  131.         $result = [];
  132.         $gpsRepository = $manager->getRepository(Gps::class);
  133.         $controllersDevices = $gpsRepository->getControllersAndDevicePairForTimeRange($data->get('dateFrom'), $data->get('dateTo'));
  134.         $gpsInterval = 5;
  135.         $dateFrom = new \DateTime($data->get('dateFrom'));
  136.         $dateTo = new \DateTime($data->get('dateTo'));
  137.         $diff = $dateTo->diff($dateFrom);
  138.         $hoursNumber = $diff->h;
  139.         if ($hoursNumber > 1 && $hoursNumber < 2) {
  140.             $gpsInterval = 10;
  141.         } elseif ($hoursNumber >= 2 && $hoursNumber < 4) {
  142.             $gpsInterval = 15;
  143.         } elseif ($hoursNumber >= 4) {
  144.             $gpsInterval = 20;
  145.         }
  146.         foreach ($controllersDevices as $key => $controllerData) {
  147.             $gpsList = $gpsRepository->getGpsForIntervalByController($data->get('dateFrom'), $data->get('dateTo'), $gpsInterval, $controllerData['createdBy'], $controllerData['urzadzeniaId']);
  148.             $keyControllerDevice = $controllerData['createdBy'] . '_' . $controllerData['urzadzeniaId'];
  149.             if (empty($controllerData['controllerLogin'])) {
  150.                 $controllerId = $manager->getRepository(ControllerUser::class)->findOneBy(['controllerId' => $controllerData['createdBy']]);
  151.                 if ($controllerId) {
  152.                     $controllerData['controllerLogin'] = $controllerData['createdBy'];
  153.                 }
  154.             }
  155.             if (!array_key_exists($keyControllerDevice, $result)) {
  156.                 $result[$keyControllerDevice] = [
  157.                     'points' => [],
  158.                     'createdBy' => $controllerData['createdBy'],
  159.                     'deviceId' => $controllerData['urzadzeniaId'],
  160.                     'controllerLogin' => $controllerData['controllerLogin']
  161.                 ];
  162.             }
  163.             foreach ($gpsList as $key => $gps) {
  164.                 if ($gpsList[$key]['createdAt']) {
  165.                     $gpsList[$key]['createdAt'] = $gps['createdAt']->format('Y-m-d H:i:s.u');
  166.                 }
  167.                 if ($gpsList[$key]['gps']) {
  168.                     $gpsList[$key]['gps'] = json_decode($gps['gps'], true);
  169.                 }
  170.                 if ($gpsList[$key]['accuracy']) {
  171.                     $gpsList[$key]['accuracy'] = $gps['accuracy'];
  172.                 }
  173.                 if ($gpsList[$key]['speed']) {
  174.                     $gpsList[$key]['speed'] = $gps['speed'];
  175.                 }
  176.                 if ($gpsList[$key]['direction']) {
  177.                     $gpsList[$key]['direction'] = $gps['direction'];
  178.                 }
  179.                 array_push($result[$keyControllerDevice]['points'], $gpsList[$key]);
  180.             }
  181.         }
  182.         return new JsonResponse($result);
  183.     }
  184.     /**
  185.      * Metoda  do wyciągania wszystkich grup requestów między datami
  186.      *
  187.      * time - data i czas (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  188.      */
  189.     #[Route(path: '/controler-car-position', name: 'get_controller_car_position', methods: ['GET'])]
  190.     public function getControllerCarPosition(EntityManagerInterface $manager, Request $request): JsonResponse
  191.     {
  192.         $timeEnd = new \DateTime($request->query->get('time'));
  193.         $timeStart = (new \DateTime($request->query->get('time')))->modify('-5 minutes');
  194.         $points = $manager->getRepository(Gps::class)->getControllerCarPositions($timeStart, $timeEnd);
  195.         $result = [];
  196.         foreach ($points as $point) {
  197.             $key = $point['createdBy'] . '_' . $point['urzadzeniaId'];
  198.             if (!array_key_exists($key, $result)) {
  199.                 $result[$key] = [];
  200.                 $result[$key]['points'] = [];
  201.                 $result[$key]['controlerId'] = $point['createdBy'];
  202.                 $result[$key]['deviceId'] = $point['urzadzeniaId'];
  203.                 $result[$key]['controllerLogin'] = $point['controllerLogin'];
  204.             }
  205.             $result[$key]['lastPoint'] = [
  206.                 'lon' => $point['longitude'],
  207.                 'lat' => $point['latitude']
  208.             ];
  209.             array_push($result[$key]['points'], [floatval($point['longitude']), floatval($point['latitude'])]);
  210.         }
  211.         return new JsonResponse($result);
  212.     }
  213.     /**
  214.      * Metoda do wyciągania danych przejazdów między konkretnymi datami (z jednego dnia)
  215.      *
  216.      * start - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  217.      * end - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  218.      */
  219.     #[Route(path: '/getstates', name: 'get_states', methods: ['GET'])]
  220.     public function getStates(EntityManagerInterface $manager, Request $request): JsonResponse
  221.     {
  222.         $check = $this->checkDateIntervalQueryParams($request, true);
  223.         if ($check) {
  224.             return $check;
  225.         }
  226.         $result = [];
  227.         $routeLaps = $manager->getRepository(RouteLap::class)->getStates(new \DateTime($request->query->get('start')), new \DateTime($request->query->get('end')));
  228.         $grouppedByParking = \array_reduce($routeLaps, function ($res, $item) {
  229.             if (!isset($res[$item['parkingId']])) {
  230.                 $res[$item['parkingId']] = [];
  231.             }
  232.             $res[$item['parkingId']][] = $item;
  233.             return $res;
  234.         }, []);
  235.         foreach ($grouppedByParking as $parkingId => $parkingRoutes) {
  236.             $parking = $manager->getRepository(Parking::class)->find($parkingId);
  237.             $parkingTypes = $parking->getParkingType();
  238.             $totalPlaces = $parking->getTotalPlaces();
  239.             $avgOccupiedPlaces = $this->floorAvgFromArrayByKey($parkingRoutes, 'occupiedPlaces');
  240.             $avgUnpaidPlaces = $this->floorAvgFromArrayByKey($parkingRoutes, 'unpaidPlaces');
  241.             $occupancyState = null;
  242.             $paymentState = null;
  243.             if ($totalPlaces > 0) {
  244.                 // Occupancy
  245.                 $occPercent = ($avgOccupiedPlaces / $totalPlaces) * 100;
  246.                 if ($occPercent > $parkingTypes->getOccupiedMaxPercent()) {
  247.                     $occupancyState = 2;
  248.                 } elseif ($occPercent <= $parkingTypes->getOccupiedMaxPercent() && $occPercent > $parkingTypes->getFreeMaxPercent()) {
  249.                     $occupancyState = 1;
  250.                 } else {
  251.                     $occupancyState = 0;
  252.                 }
  253.                 // Payment
  254.                 $percentUnpaid = ($avgUnpaidPlaces / $totalPlaces) * 100;
  255.                 if ($percentUnpaid > $parkingTypes->getSomeUnpaidMaxPercent()) {
  256.                     $paymentState = 2;
  257.                 } elseif ($percentUnpaid <= $parkingTypes->getSomeUnpaidMaxPercent() && $percentUnpaid > $parkingTypes->getFewUnpaidMaxPercent()) {
  258.                     $paymentState = 1;
  259.                 } else {
  260.                     if ($occupancyState == 0) {
  261.                         $paymentState = 3;
  262.                     } else {
  263.                         $paymentState = 0;
  264.                     }
  265.                 }
  266.             }
  267.             $result[$parkingId] = [
  268.                 'parkingId' => $parkingId,
  269.                 'occupancyState' => $occupancyState,
  270.                 'paymentState' => $paymentState,
  271.                 'totalPlaces' => $totalPlaces,
  272.                 'occupiedPlaces' => $avgOccupiedPlaces,
  273.                 'unpaidPlaces' => $avgUnpaidPlaces
  274.             ];
  275.         }
  276.         return new JsonResponse($result);
  277.     }
  278.     /**
  279.      * Metoda do wyliczania średniej wartości dla wskazanego klucza w tablicy obiektów. Wartość jest zaokroąglana i sprowadzana to typu int
  280.      *
  281.      * @param array $arr tablica obiektów
  282.      * @param string $key klucz
  283.      *
  284.      * @return int liczba stanowiącą średnią z wartości pod wskazanym kluczem w obiektach w tablicy
  285.      */
  286.     private function floorAvgFromArrayByKey(array $arr, string $key): int
  287.     {
  288.         $arrOfKey = \array_map(fn ($item) => $item[$key], $arr);
  289.         return (int) \round(\array_sum($arrOfKey) / \count($arrOfKey));
  290.     }
  291.     /**
  292.      * Metoda  do wyciągania danych przejazdów między konkretnymi datami (z jednego dnia) na jednym parkingu
  293.      *
  294.      * start - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  295.      * end - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  296.      */
  297.     #[Route(path: '/getstate/{parkingId}', name: 'get_state_parking', methods: ['GET'])]
  298.     public function getState(EntityManagerInterface $manager, Request $request, $parkingId)
  299.     {
  300.         $check = $this->checkDateIntervalQueryParams($request, true);
  301.         if ($check) {
  302.             return $check;
  303.         }
  304.         $result = $manager->getRepository(RouteLap::class)->getState(new \DateTime($request->query->get('start')), new \DateTime($request->query->get('end')), $parkingId);
  305.         return new JsonResponse($result);
  306.     }
  307.     /**
  308.      * Metoda do sprawdzania czy otrzymane parametry zapytania są poprawne dla przedziału dat
  309.      *
  310.      * @param bool $sameDay Parametr decydujący o tym, czy daty muszą być tego samego dnia
  311.      *
  312.      * @return JsonResponse|void jeżeli jest bład to zwróci go w postaci JsonResponse, jeżeli nie to nic nie zwróci
  313.      */
  314.     private function checkDateIntervalQueryParams(Request $request, $sameDay = false)
  315.     {
  316.         $timeRange = [];
  317.         if (!$request->query->get('start') || !$request->query->get('end')) {
  318.             return new JsonResponse(['error' => 'Niepełne parametry zapytania'], 400);
  319.         }
  320.         $timeRange['start'] = new \DateTime($request->query->get('start'));
  321.         $timeRange['end'] = new \DateTime($request->query->get('end'));
  322.         if ($timeRange['start'] > $timeRange['end']) {
  323.             return new JsonResponse(['error' => 'Data końca jest wcześniejsza niż data początku'], 400);
  324.         }
  325.         if ($sameDay) {
  326.             $midnight = (new \DateTime($request->query->get('end')))->setTime(0, 0, 0);
  327.             if ($timeRange['start'] < $midnight) {
  328.                 return new JsonResponse(['error' => 'Daty początku i końca nie są tego samego dnia'], 400);
  329.             }
  330.         }
  331.     }
  332.     /**
  333.      * Metoda do wyciągania statystyk wystawionych zawiadomień w danym przedziale czasowym
  334.      *
  335.      * dateStart - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  336.      * dateEnd - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  337.      */
  338.     #[Route(path: '/get-fine-statistics', name: 'get-fine-statistics', methods: ['GET'])]
  339.     public function getFineStatistics(Request $request): JsonResponse
  340.     {
  341.         $dateStart = new \DateTime($request->query->get('dateStart'));
  342.         $dateEnd = new \DateTime($request->query->get('dateEnd'));
  343.         $verificationStatuses = VerificationStatus::STATUSES;
  344.         $isSuperAdmin = $this->getUser()->isSuperAdmin();
  345.         if (!$isSuperAdmin) {
  346.             unset($verificationStatuses[VerificationStatus::AUTOMATICALLY_ANONYMIZED_DUPLICATE]);
  347.         }
  348.         foreach ($verificationStatuses as $id => $name) {
  349.             $fineStats[$id] = [
  350.                 'number_of_fines' => 0,
  351.                 'name' => $name
  352.             ];
  353.         }
  354.         foreach (
  355.             $this->manager->getRepository(Fine::class)->getGroupedByVerificationStatuses($dateStart, $dateEnd, $isSuperAdmin) as $fineByStatus
  356.         ) {
  357.             $fineStats[$fineByStatus['id']] = [
  358.                 'number_of_fines' => $fineByStatus['number_of_fines'],
  359.                 'name' => $fineByStatus['name']
  360.             ];
  361.         }
  362.         return new JsonResponse($fineStats);
  363.     }
  364.     /**
  365.      * Metoda do wyciągania statystyk pojedynczych odczytów w danym przedziale czasowym
  366.      *
  367.      * start - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  368.      * end - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  369.      */
  370.     #[Route(path: '/get-request-log-statistics', name: 'get-request-log-statistics', methods: ['GET'])]
  371.     public function getRequestLogStatistics(EntityManagerInterface $manager, Request $request)
  372.     {
  373.         $dateStart = new \DateTime($request->query->get('dateStart'));
  374.         $dateEnd = new \DateTime($request->query->get('dateEnd'));
  375.         $requestLogStats = [];
  376.         $mergedStatuses = [
  377.             ParkingStatus::PARKING_ERROR_2   => ParkingStatus::PARKING_ERROR,
  378.             ParkingStatus::PARKING_EXCEEDED_2 => ParkingStatus::PARKING_EXCEEDED,
  379.         ];
  380.         foreach (ParkingStatus::ARRAY_PARKING_STATUSES as $key => $value) {
  381.             if ($key !== ParkingStatus::OUTSIDE_PARKING && !isset($mergedStatuses[$key])) {
  382.                 $requestLogStats[$key] = [
  383.                     'numer_of_request_logs' => 0,
  384.                     'name' => $value['name']
  385.                 ];
  386.             }
  387.         }
  388.         foreach ($manager->getRepository(RequestLog::class)->getGroupedByPaymentStatuses($dateStart, $dateEnd) as $requestLogByStatus) {
  389.             $id = (int) $requestLogByStatus['id'];
  390.             $targetId = $mergedStatuses[$id] ?? $id;
  391.             $requestLogStats[$targetId]['numer_of_request_logs'] = ($requestLogStats[$targetId]['numer_of_request_logs'] ?? 0) + $requestLogByStatus['numer_of_request_logs'];
  392.         }
  393.         return new JsonResponse($requestLogStats);
  394.     }
  395.     /**
  396.      * Metoda do wyciągania statystyk niepoprawnych odczytów kazdego dnia w danym przedziale czasowym
  397.      *
  398.      * start - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  399.      * end - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  400.      */
  401.     #[Route(path: '/get-request-log-mistakes', name: 'get-request-log-mistakes', methods: ['GET'])]
  402.     public function getRequestLogMistakes(EntityManagerInterface $manager, Request $request)
  403.     {
  404.         $dateStart = new \DateTime($request->query->get('dateStart'));
  405.         $dateEnd = new \DateTime($request->query->get('dateEnd'));
  406.         $mistakes = $manager->getRepository(RequestLog::class)->getIncorrectlyRead($dateStart, $dateEnd);
  407.         $requestsByDay = [];
  408.         foreach ($manager->getRepository(RequestLog::class)->getAllRead($dateStart, $dateEnd) as $dayOfReqeusts) {
  409.             $requestsByDay[$dayOfReqeusts['requestDate']] = $dayOfReqeusts['requestCount'];
  410.         }
  411.         foreach ($mistakes as $index => $key) {
  412.             if (array_key_exists($key['requestDate'], $requestsByDay)) {
  413.                 $mistakes[$index]['percentage'] = round($key['requestCount'] * 100 / $requestsByDay[$key['requestDate']], 2) . '%';
  414.             } else {
  415.                 $mistakes[$index]['percentage'] = 'NaN';
  416.             }
  417.         }
  418.         return new JsonResponse($mistakes);
  419.     }
  420.     /**
  421.      * Metoda  do sugerowania niesparowanych odczytów w parach przejazdów
  422.      */
  423.     #[Route(path: '/get-suggestions/{lapPairID}', name: 'get-lap-pair-suggestions', methods: ['GET'])]
  424.     public function getLapPairSuggestions(EntityManagerInterface $manager, $lapPairID)
  425.     {
  426.         $lapPair = $manager->getRepository(LapPair::class)->find($lapPairID);
  427.         $suggestions = [];
  428.         $suggestionPairs = [];
  429.         foreach (
  430.             $manager->getRepository(RequestLog::class)->findBy([
  431.                 'routeLap' => $lapPair->getFirstLap(),
  432.                 'paymentStatus' => [ParkingStatus::PARKING_ERROR, ParkingStatus::PARKING_UNPAID]
  433.             ]) as $firstLog
  434.         ) {
  435.             foreach (
  436.                 $manager->getRepository(RequestLog::class)->getToSuggestions(
  437.                     $lapPair->getSecondLap(),
  438.                     [ParkingStatus::PARKING_ERROR, ParkingStatus::PARKING_UNPAID],
  439.                     $firstLog->getLicensePlate()
  440.                 ) as $secondLog
  441.             ) {
  442.                 $proximity = $this->getStringProximity($firstLog->getLicensePlate(), $secondLog->getLicensePlate());
  443.                 if ($proximity <= 2) {
  444.                     $suggestionPairs[] = [
  445.                         'first_log' => $firstLog->getBasicLogData(),
  446.                         'second_log' => $secondLog->getBasicLogData()
  447.                     ];
  448.                 }
  449.             }
  450.         }
  451.         foreach ($suggestionPairs as $pair) {
  452.             $firstLink = $this->container->get(AdminUrlGenerator::class)
  453.                 ->setAction(Crud::PAGE_EDIT)
  454.                 ->setController(RequestLogCrudController::class)
  455.                 ->setEntityId($pair['first_log']['id'])
  456.                 ->generateUrl();
  457.             $secondLink = $this->container->get(AdminUrlGenerator::class)
  458.                 ->setAction(Crud::PAGE_EDIT)
  459.                 ->setController(RequestLogCrudController::class)
  460.                 ->setEntityId($pair['second_log']['id'])
  461.                 ->generateUrl();
  462.             $suggestions[] = [
  463.                 'firstLicensePlate' => $pair['first_log']['licensePlate'],
  464.                 'secondLicensePlate' => $pair['second_log']['licensePlate'],
  465.                 'firstDate' => $pair['first_log']['date'],
  466.                 'secondDate' => $pair['second_log']['date'],
  467.                 'firstLocation' => $pair['first_log']['location'],
  468.                 'secondLocation' => $pair['second_log']['location'],
  469.                 'first_photo' => array_key_exists('plate', $pair['first_log']['photos']) ? $pair['first_log']['photos']['plate'] : '',
  470.                 'second_photo' => array_key_exists('plate', $pair['second_log']['photos']) ? $pair['second_log']['photos']['plate'] : '',
  471.                 'first_edit_url' => $firstLink,
  472.                 'second_edit_url' => $secondLink,
  473.             ];
  474.         }
  475.         return new JsonResponse($suggestions);
  476.     }
  477.     /**
  478.      * Prywatna metoda do obliczania "bliskości" dwóch stringów
  479.      *
  480.      * zwraca:
  481.      *      0 - jeden string zawiera drugi
  482.      *      x - stringi róznia sie o conajmniej liter
  483.      */
  484.     private function getStringProximity($first, $second)
  485.     {
  486.         if (strlen($first) > strlen($second)) {
  487.             $longer = $first;
  488.             $shorter = $second;
  489.         } else {
  490.             $longer = $second;
  491.             $shorter = $first;
  492.         }
  493.         if (str_contains($longer, (string) $shorter)) {
  494.             return 0;
  495.         }
  496.         // Sprawdzanie odpoczątku
  497.         $commonLetters = 0;
  498.         foreach (str_split($shorter) as $index => $value) {
  499.             if ($value === $longer[$index]) {
  500.                 $commonLetters += 1;
  501.             }
  502.         }
  503.         if (strlen($shorter) - $commonLetters <= 2) {
  504.             return strlen($shorter) - $commonLetters;
  505.         }
  506.         // Sprawdzanie od końca
  507.         $commonLetters = 0;
  508.         for ($i = 0; $i < strlen($shorter); $i += 1) {
  509.             if ($shorter[strlen($shorter) - $i - 1] === $longer[strlen($longer) - $i - 1]) {
  510.                 $commonLetters += 1;
  511.             }
  512.         }
  513.         return strlen($shorter) - $commonLetters;
  514.     }
  515.     #[Route(path: '/fine-payment-requests/{fineId}', name: 'api_fine_payment_requests', methods: ['GET'])]
  516.     public function getFinePaymentRequests(EntityManagerInterface $manager, $fineId): JsonResponse
  517.     {
  518.         $response = [];
  519.         $data = $manager->getRepository(RequestLogHistory::class)->findByFineId($fineId);
  520.         foreach ($data as $log) {
  521.             $logData = $log->getKeyData();
  522.             $logData['licensePlate'] = LicensePlate::fromHttpRequest($logData['request'], $log->getRequestLog()->getAnonymized()) ?? 'Brak';
  523.             $response[] = $logData;
  524.         }
  525.         return new JsonResponse($response);
  526.     }
  527.     #[Route(path: '/fine-payment-requests-admin/{fineId}', name: 'api_fine_payment_requests_admin', methods: ['GET'])]
  528.     public function getFinePaymentRequestsAdmin(EntityManagerInterface $manager, $fineId): JsonResponse
  529.     {
  530.         $response = [];
  531.         $reqLogsHistory = $manager->getRepository(RequestLogHistory::class)->getAllFromSpecificFineRequestGroup($fineId);
  532.         foreach ($reqLogsHistory as $log) {
  533.             $logData = $log->getKeyData();
  534.             $logData['licensePlate'] = LicensePlate::fromHttpRequest($logData['request'], $log->getRequestLog()->getAnonymized()) ?? 'Brak';
  535.             $response[] = $logData;
  536.         }
  537.         return new JsonResponse($response);
  538.     }
  539.     /**
  540.      * Metoda do wyciągania statystyk operacji wykonanych przez uzytkownikow w danym przedziale czasowym
  541.      *
  542.      * dateStart - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  543.      * dateEnd - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  544.      * user - ID użytkownika (jeżeli 0 to wszyscy użytkownicy)
  545.      */
  546.     #[Route(path: '/get-user-statements', name: 'get-user-statements', methods: ['GET'])]
  547.     public function getUserStatements(EntityManagerInterface $manager, Request $request): JsonResponse
  548.     {
  549.         $dateStart = new \DateTime($request->query->get('dateStart'));
  550.         $dateEnd = new \DateTime($request->query->get('dateEnd'));
  551.         $user = $request->query->get('user');
  552.         $allVerificationOperations = $manager->getRepository(FineOperation::class)->getGroupedByUserActions($dateStart, $dateEnd, [OperationType::MANUAL_REQUEST_VERIFICATION, OperationType::DIRECTORS_CONFIRMATION], $user, UserGroup::WORKERS_GROUP_NAMES);
  553.         $positiveVerifications = $manager->getRepository(FineOperation::class)->getGroupedByUserActions($dateStart, $dateEnd, [OperationType::MANUAL_REQUEST_VERIFICATION], $user, UserGroup::WORKERS_GROUP_NAMES, VerificationStatus::AWAITING_POSITIVE_VERIFICATION);
  554.         $negativeVerifications = $manager->getRepository(FineOperation::class)->getGroupedByUserActions($dateStart, $dateEnd, [OperationType::MANUAL_REQUEST_VERIFICATION], $user, UserGroup::WORKERS_GROUP_NAMES, VerificationStatus::AWAITING_NEGATIVE_VERIFICATION);
  555.         $confirmPositiveVerifications = $manager->getRepository(FineOperation::class)->getGroupedByUserActions($dateStart, $dateEnd, [OperationType::DIRECTORS_CONFIRMATION], $user, UserGroup::WORKERS_GROUP_NAMES, VerificationStatus::POSITIVELY_VERIFIED);
  556.         $confirmNegativeVerifications = $manager->getRepository(FineOperation::class)->getGroupedByUserActions($dateStart, $dateEnd, [OperationType::DIRECTORS_CONFIRMATION], $user, UserGroup::WORKERS_GROUP_NAMES, VerificationStatus::NEGATIVELY_VERIFIED);
  557.         $groupedResponses = [];
  558.         foreach ($allVerificationOperations as $operation) {
  559.             $groupedResponses[$operation['authorUsername']][$operation['createdDate']] = [
  560.                 'date' => $operation['createdDate'],
  561.                 'allOperations' => $operation['typeCount'],
  562.                 'positiveOperations' => 0,
  563.                 'negativeOperations' => 0,
  564.                 'confirmPositiveOperations' => 0,
  565.                 'confirmNegativeOperations' => 0
  566.             ];
  567.         }
  568.         foreach ($positiveVerifications as $operation) {
  569.             $groupedResponses[$operation['authorUsername']][$operation['createdDate']]['positiveOperations'] = $operation['typeCount'];
  570.         }
  571.         foreach ($negativeVerifications as $operation) {
  572.             $groupedResponses[$operation['authorUsername']][$operation['createdDate']]['negativeOperations'] = $operation['typeCount'];
  573.         }
  574.         foreach ($confirmPositiveVerifications as $operation) {
  575.             $groupedResponses[$operation['authorUsername']][$operation['createdDate']]['confirmPositiveOperations'] = $operation['typeCount'];
  576.         }
  577.         foreach ($confirmNegativeVerifications as $operation) {
  578.             $groupedResponses[$operation['authorUsername']][$operation['createdDate']]['confirmNegativeOperations'] = $operation['typeCount'];
  579.         }
  580.         foreach ($groupedResponses as $userKey => $userResponses) {
  581.             foreach ($userResponses as $dateKey => $dateResponse) {
  582.                 $groupedResponses[$userKey][$dateKey]['otherOperations'] = $dateResponse['allOperations'] - $dateResponse['positiveOperations'] - $dateResponse['negativeOperations'] - $dateResponse['confirmPositiveOperations'] - $dateResponse['confirmNegativeOperations'];
  583.             }
  584.         }
  585.         return new JsonResponse($groupedResponses);
  586.     }
  587.     /**
  588.      * Metoda do wyciągania statystyk operacji wykonanych przez uzytkownikow w danym przedziale czasowym
  589.      *
  590.      * start - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  591.      * end - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  592.      * user - ID użytkownika (jeżeli 0 to wszyscy użytkownicy)
  593.      */
  594.     #[Route(path: '/get-request-log-statements', name: 'get-request-log-statements', methods: ['GET'])]
  595.     public function getRequestLogStatements(EntityManagerInterface $manager, Request $request)
  596.     {
  597.         $dateStart = new \DateTime($request->query->get('dateStart'));
  598.         $dateEnd = new \DateTime($request->query->get('dateEnd'));
  599.         $user = $request->query->get('user');
  600.         $unformattedStats = $manager->getRepository(RequestLogHistory::class)->getGroupedByDay($dateStart, $dateEnd, 'verify', $user, UserGroup::WORKERS_GROUP_NAMES);
  601.         $groupedResponses = [];
  602.         foreach ($unformattedStats as $operation) {
  603.             $groupedResponses[$operation['authorUsername']][$operation['createdDate']] = [
  604.                 'date' => $operation['createdDate'],
  605.                 'count' => $operation['typeCount']
  606.             ];
  607.         }
  608.         return new JsonResponse($groupedResponses);
  609.     }
  610.     /**
  611.      * Metoda do wyciągania statystyk esportów w danym przedziale czasowym
  612.      *
  613.      * start - data i czas startu (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 00:00:00")
  614.      * end - data i czas końca (format "YYYY-MM-DD HH:mm:ss", np.: "2022-06-09 23:59:59")
  615.      */
  616.     #[Route(path: '/get-log-exports-statements', name: 'get-log-exports-statements', methods: ['GET'])]
  617.     public function getLogExportsStatements(EntityManagerInterface $manager, Request $request)
  618.     {
  619.         $dateStart = new \DateTime($request->query->get('dateStart'));
  620.         $dateEnd = new \DateTime($request->query->get('dateEnd'));
  621.         $unformattedStats = $manager->getRepository(LogExport::class)->getGroupedByDay($dateStart, $dateEnd);
  622.         $groupedResponses = ['' => []];
  623.         foreach ($unformattedStats as $export) {
  624.             $groupedResponses[''][$export['createdDate']]['createdDate'] = $export['createdDate'];
  625.             $groupedResponses[''][$export['createdDate']]['s' . $export['status']] = $export['typeCount']; // Muszę tutaj dodać jakąś literkę, bo Vue.js przerabia kolejność kolumn, jeżeli zinterpretuje, że część z nich to inty :)
  626.         }
  627.         foreach ($groupedResponses[''] as $date => $response) {
  628.             if (!array_key_exists('s0', $response)) {
  629.                 $groupedResponses[''][$date]['s0'] = 0;
  630.             }
  631.             if (!array_key_exists('s200', $response)) {
  632.                 $groupedResponses[''][$date]['s200'] = 0;
  633.             }
  634.             if (!array_key_exists('s201', $response)) {
  635.                 $groupedResponses[''][$date]['s201'] = 0;
  636.             }
  637.             if (!array_key_exists('s409', $response)) {
  638.                 $groupedResponses[''][$date]['s409'] = 0;
  639.             }
  640.             $groupedResponses[''][$date]['count'] = $groupedResponses[''][$date]['s0'] + $groupedResponses[''][$date]['s200'] + $groupedResponses[''][$date]['s201'] + $groupedResponses[''][$date]['s409'];
  641.         }
  642.         return new JsonResponse($groupedResponses);
  643.     }
  644.     #[Route(path: '/save-image', name: 'save_image', methods: ['POST'])]
  645.     public function saveImage(Request $request): JsonResponse
  646.     {
  647.         $file = $request->files->get('file');
  648.         $mode = $request->request->get('mode');
  649.         $requestLogId = $request->request->get('requestLogId');
  650.         $errors = [];
  651.         if (!$file) {
  652.             $errors[] = 'file is missing';
  653.         }
  654.         if (!$mode) {
  655.             $errors[] = 'mode is missing';
  656.         }
  657.         if (!$requestLogId) {
  658.             $errors[] = 'requestLogId is missing';
  659.         }
  660.         if (!empty($errors)) {
  661.             return new JsonResponse(['errors' => $errors], Response::HTTP_BAD_REQUEST);
  662.         }
  663.         $curl = \curl_init();
  664.         \curl_setopt_array($curl, [
  665.             CURLOPT_URL => $_ENV['WS_ADDRESS_PHOTO'] . 'upload-file',
  666.             CURLOPT_POSTFIELDS => [
  667.                 'mode' => $mode,
  668.                 'file' => new \CURLFile($file->getPathname(), $file->getMimeType(), $file->getClientOriginalName()),
  669.                 'requestLogId' => $requestLogId
  670.             ],
  671.             CURLOPT_HTTPHEADER => [
  672.                 'Content-Type: multipart/form-data',
  673.                 'Access-Control-Allow-Origin: *'
  674.             ],
  675.             CURLOPT_RETURNTRANSFER => true,
  676.             CURLOPT_SSL_VERIFYPEER => false
  677.         ]);
  678.         $result = \curl_exec($curl);
  679.         $info = \curl_getinfo($curl);
  680.         \curl_close($curl);
  681.         return new JsonResponse([
  682.             'response' => $result,
  683.             'code' => $info['http_code']
  684.         ]);
  685.     }
  686. }