feat: api endpoints for pickups planning

This commit is contained in:
Björn Fromme
2025-12-12 09:44:38 +01:00
parent 1d97a19982
commit b1ff6989ec
8 changed files with 117 additions and 2 deletions
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\DataProcessor;
class PickupPlanningTransformer
{
/**
* @param array<int, array<string, mixed>> $payload
*
* @return array<string, array<int, array<string, string>>>
*/
public function transform(array $payload): array
{
$result = [];
foreach ($payload as $item) {
if ('HIN' !== ($item['PlanungsArt'] ?? null)) {
continue;
}
$travelCode = $item['Reisecode'] ?? null;
if (null === $travelCode) {
continue;
}
$dateTime = \DateTimeImmutable::createFromFormat('d.m.Y H:i', $item['Zeit'] ?? '');
$result[$travelCode][] = [
'city' => $item['Zustieg/Ausstieg Ort'] ?? '',
'location' => $item['Zustieg/Ausstieg Strasse'] ?? '',
'datetime' => false !== $dateTime ? $dateTime->format('Y-m-d H:i') : '',
];
}
foreach ($result as $travelCode => $pickups) {
usort($pickups, static fn (array $a, array $b): int => $a['datetime'] <=> $b['datetime']);
$result[$travelCode] = $pickups;
}
return $result;
}
}
+51 -2
View File
@@ -2,9 +2,13 @@
namespace App\Controller\Api;
use App\BusProNet\DataProcessor\PickupPlanningTransformer;
use App\BusProNet\XmlLoader\PickupLoader;
use League\Flysystem\FilesystemException;
use League\Flysystem\FilesystemOperator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
@@ -13,8 +17,13 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_OAUTH2_API')]
class PickupController extends AbstractController
{
public function __construct(private readonly PickupLoader $xmlLoader)
{
private const PLANNING_FILE = 'pickup_planning.json';
public function __construct(
private readonly PickupLoader $xmlLoader,
private readonly PickupPlanningTransformer $planningTransformer,
private readonly FilesystemOperator $jsonExport,
) {
}
#[Route('/pickups', name: 'api_pickups_all')]
@@ -32,4 +41,44 @@ class PickupController extends AbstractController
return $this->json($pickup, Response::HTTP_OK, [], ['groups' => 'api:list']);
}
#[Route('/pickups-planning/{travelCode}', name: 'api_pickups_planning_single', methods: ['GET'])]
public function planningByTravelCode(string $travelCode): JsonResponse
{
try {
if (false === $this->jsonExport->fileExists(self::PLANNING_FILE)) {
return $this->json(['error' => 'Planning data not available'], Response::HTTP_NOT_FOUND);
}
$planning = json_decode($this->jsonExport->read(self::PLANNING_FILE), true, 512, \JSON_THROW_ON_ERROR);
} catch (FilesystemException|\JsonException) {
return $this->json(['error' => 'Failed to read planning data'], Response::HTTP_INTERNAL_SERVER_ERROR);
}
if (false === \array_key_exists($travelCode, $planning)) {
return $this->json(['error' => 'Travel code not found'], Response::HTTP_NOT_FOUND);
}
return $this->json($planning[$travelCode]);
}
#[Route('/pickups-planning', name: 'api_pickups_planning', methods: ['POST'])]
public function planning(Request $request): JsonResponse
{
try {
$payload = $request->toArray();
} catch (\JsonException) {
return $this->json(['error' => 'Invalid JSON payload'], Response::HTTP_BAD_REQUEST);
}
$transformed = $this->planningTransformer->transform($payload);
try {
$this->jsonExport->write(self::PLANNING_FILE, json_encode($transformed, \JSON_THROW_ON_ERROR));
} catch (FilesystemException|\JsonException) {
return $this->json(['error' => 'Failed to write planning data'], Response::HTTP_INTERNAL_SERVER_ERROR);
}
return $this->json(['status' => 'ok']);
}
}