feat: travel data API endpoint for travelinfo pages in TYPO3

This commit is contained in:
Björn Fromme
2025-05-15 11:54:37 +02:00
parent 1e5401ea25
commit 6bf22045c4
6 changed files with 120 additions and 9 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ Accept: application/json
Authorization: Bearer {{$auth.token("oauth2_api")}}
### API travel
GET {{base_url}}/api/travels/DWWMP301125
GET {{base_url}}/api/travels/DPWBSP030229
Accept: application/json
Authorization: Bearer {{$auth.token("oauth2_api")}}
+29 -1
View File
@@ -66,7 +66,35 @@ trait TypeConversionTrait
}
try {
$dateTime = Carbon::createFromFormat('d.m.Y H:i', substr($string, 0, 16))->startOfDay();
$dateTime = Carbon::createFromFormat('d.m.Y H:i', substr($string, 0, 16));
return $dateTime->toDateTimeImmutable();
} catch (InvalidFormatException $e) {
return null;
}
}
/**
* This method is used primarily to parse pickup-times since data is
* either provided with date and time or time only. In this case a
* date has to be provided which defaults to a travel's start date.
*/
protected function stringToDateTimeFuzzy(?string $string, \DateTimeImmutable $defaultDate): ?\DateTimeImmutable
{
if (true === empty($string)) {
return null;
}
try {
if (1 === preg_match('/^\d{2}:\d{2}$/', $string)) {
// time only provided
$dateTime = Carbon::createFromFormat('H:i', $string);
// assign default date
$dateTime->setDateFrom($defaultDate);
} else {
// date and time provided
$dateTime = Carbon::createFromFormat('d.m.Y H:i', substr($string, 0, 16));
}
return $dateTime->toDateTimeImmutable();
} catch (InvalidFormatException $e) {
@@ -0,0 +1,23 @@
<?php
namespace App\BusProNet\Utility;
class TravelCodeUtility
{
public function sanitize(string $travelCode): string
{
return str_replace(['-', '/'], '', strtoupper($travelCode));
}
public function getBaseCode(string $travelCode): string
{
return strtoupper(substr($travelCode, 0, -6));
}
public function getDate(string $travelCode): \DateTimeImmutable
{
$datePart = substr($travelCode, -6);
return \DateTimeImmutable::createFromFormat('dmy', $datePart);
}
}
+16 -6
View File
@@ -11,6 +11,7 @@ use App\BusProNet\Model\Pickup;
use App\BusProNet\Model\Room;
use App\BusProNet\Model\Service;
use App\BusProNet\Model\Travel;
use App\BusProNet\Utility\TravelCodeUtility;
use League\Flysystem\FilesystemOperator;
use League\Flysystem\StorageAttributes;
use Psr\Cache\InvalidArgumentException;
@@ -130,11 +131,13 @@ class TravelLoader extends AbstractLoader
{
$hotelNode = $this->getHotelNode($node, $hotelId);
$dateFrom = $this->stringToDate($node->attr('termin'));
$travel = new Travel();
$travel->id = (int) $node->attr('idbuspro');
$travel->hotelId = (int) $hotelNode->attr('idbuspro');
$travel->label = $this->getStringOrNullValue($node->filterXPath('//text'));
$travel->dateFrom = $this->stringToDate($node->attr('termin'));
$travel->dateFrom = $dateFrom;
$travel->dateTo = $this->stringToDate($node->attr('bis'));
$travel->code = $node->attr('code');
$travel->type = $node->attr('reiseart');
@@ -145,7 +148,7 @@ class TravelLoader extends AbstractLoader
$travel->transportationServices = $this
->getTransportationServices($node->filterXPath('//lei_befoerderung/leistung'));
$travel->rooms = $this->getRooms($hotelNode);
$travel->pickupsTo = $this->getPickups($node->filterXPath('//zustiege/zustieg'));
$travel->pickupsTo = $this->getPickups($node->filterXPath('//zustiege/zustieg'), $dateFrom);
$travel->pickupsFro = $this->getPickups($node->filterXPath('//zustiege_rueck/zustieg_rueck'));
$travel->guide = $this->getGuide($node);
@@ -248,19 +251,23 @@ class TravelLoader extends AbstractLoader
return $transportationServices;
}
public function getPickups(Crawler $node): array
public function getPickups(Crawler $node, ?\DateTimeImmutable $defaultDate = null): array
{
$pickups = [];
$node->each(function (Crawler $pickupNode) use (&$pickups) {
$node->each(function (Crawler $pickupNode) use (&$pickups, $defaultDate) {
$pickupId = (int) $pickupNode->attr('idbuspro');
$pickup = new Pickup();
$pickup->id = $pickupId;
$pickup->time = $this->stringToDateTime($pickupNode->attr('zeit'));
$pickup->price = $pickupNode->attr('preis') ?
$this->stringToFloat($pickupNode->attr('preis')) : null;
// parse date and time only for direction 'to' indicated by provided default date
if (null !== $defaultDate) {
$pickup->time = $this->stringToDateTimeFuzzy($pickupNode->attr('zeit'), $defaultDate);
}
$pickups[$pickupId] = $pickup;
});
@@ -352,6 +359,8 @@ class TravelLoader extends AbstractLoader
public function patchBookings(BaseData $bookings): void
{
$travelCodeUtility = new TravelCodeUtility();
foreach ($bookings->getItems() as $booking) {
if (null === $travelId = $booking->travelId) {
continue;
@@ -368,11 +377,12 @@ class TravelLoader extends AbstractLoader
$travelDate = $travelData->dateFrom;
$travelCode = $travelData->code;
// generate url to travelinfo page five days before travel begins
if ($travelDate->modify('-5 days') < $now) {
$booking->travelInfoUrl = sprintf(
'%s/%s',
rtrim($this->travelInfoBaseUrl, '/'),
str_replace('/', '-', $travelCode)
$travelCodeUtility->sanitize($travelCode)
);
}
}
+3 -1
View File
@@ -3,6 +3,7 @@
namespace App\Controller\Api;
use App\BusProNet\Model\Travel;
use App\BusProNet\Utility\TravelCodeUtility;
use App\BusProNet\XmlLoader\HotelLoader;
use App\BusProNet\XmlLoader\PickupLoader;
use App\BusProNet\XmlLoader\TravelLoader;
@@ -55,7 +56,8 @@ class TravelController extends AbstractController
)]
public function singleByCode(string $travelCode, ?string $hotelCode = null): JsonResponse
{
$travelCode = str_replace('-', '/', $travelCode);
// sanitize travel code by removing potential dividers
$travelCode = (new TravelCodeUtility())->sanitize($travelCode);
$travelId = $this->travelXmlLoader->mapCodeToId($travelCode);
$hotelId = $hotelCode ? $this->hotelXmlLoader->mapCodeToId($travelCode) : null;
@@ -0,0 +1,48 @@
<?php
namespace BusProNet\Utility;
use App\BusProNet\Utility\TravelCodeUtility;
use PHPUnit\Framework\TestCase;
class TravelCodeUtilityTest extends TestCase
{
public function testSanitizeCode(): void
{
$utility = new TravelCodeUtility();
$travelCode = 'ABCDEF/010125';
$sanitized = $utility->sanitize($travelCode);
$this->assertEquals('ABCDEF010125', $sanitized);
$travelCode = 'ABCDEF-010125';
$sanitized = $utility->sanitize($travelCode);
$this->assertEquals('ABCDEF010125', $sanitized);
$travelCode = 'abcdef/010125';
$sanitized = $utility->sanitize($travelCode);
$this->assertEquals('ABCDEF010125', $sanitized);
}
public function testGetBaseCode(): void
{
$utility = new TravelCodeUtility();
$travelCode = 'ABCDEF010125';
$baseCode = $utility->getBaseCode($travelCode);
$this->assertEquals('ABCDEF', $baseCode);
$travelCode = 'abcdef010125';
$baseCode = $utility->getBaseCode($travelCode);
$this->assertEquals('ABCDEF', $baseCode);
}
public function testGetDate(): void
{
$utility = new TravelCodeUtility();
$travelCode = 'ABCDEF010125';
$date = $utility->getDate($travelCode);
$this->assertEquals('2025-01-01', $date->format('Y-m-d'));
}
}