<?php
namespace App\Service;
use App\Entity\Game;
use App\Model\AccomodationRecipe;
use App\Model\AirportRecipe;
use App\Model\Basket as BasketModel;
use App\Model\Pyton\Accommodation\AccommodationWrapper;
use App\Model\Pyton\Accommodation\Results;
use App\Model\Pyton\AccommodationReceipt;
use App\Model\Pyton\Basket as PytonBasket;
use App\Model\Pyton\Flight\Results as FlightResults;
use App\Model\Pyton\FlightReceipt;
use DateInterval;
use DateTime;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use InvalidArgumentException;
use JMS\Serializer\Serializer;
use Money\Currency;
use Money\Money;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Intl\Countries;
class Pyton
{
public const FLIGHTS_RECEIPT = '/api/flights/receipt/create';
public const FLIGHTS_SEARCH_FLIGHTS_ENDPOINT = '/api/flights/search';
public const BASICDATA_AIRPORTS_ENDPOINT = '/api/basicdata/airports';
public const FLIGHTS_SEARCH_DESTINATIONS_ENDPOINT = '/api/flights/search/destinations';
public const FLIGHTS_SEARCH_DESTINATION_ENDPOINT = '/api/flights/search/destination';
public const ACCOMMODATIONS_SEARCH_DESTINATIONS_ENDPOINT = '/api/accommodations/search/destinations';
public const ACCOMMODATIONS_SEARCH_ENDPOINT = '/api/accommodations/search';
public const BASKET_CREATE_ENDPOINT = '/api/basket/create';
public const BASKET_BOOK_ENDPOINT = '/api/basket/book/%s';
public const BASKET_CREATE_BASKET_RECEIPT_ENDPOINT = '/api/basket/receipt/%s';
public const BASKET_PASSENGERS_ENDPOINT = '/api/basket/passengers/%s';
public const ACCOMMODATIONS_RECEIPT_ENDPOINT = '/api/accommodations/receipt/create';
public const ACCOMMODATIONS_DETAIL_ENDPOINT = '/api/accommodations/details';
public const FLIGHTS_DETAIL_ENDPOINT = '/api/flights/details';
public const ACTIONCODE_AVAILABLE_ENDPOINT = '/api/actioncodes/available/%s';
public const ACTIONCODE_CHECK_ENDPOINT = '/api/actioncodes/check/%s/%s';
public const MATCH_ARRIVAL_MIN_INTERVAL = '-6 hour';
/**
* The response trigger from Pyton if a basket has already been booked.
*/
public const RESPONSE_MESSAGE_ALREADY_BOOKED = 'This basket is already booked!';
protected $ignoreFinalCountries = [
'duitsland',
];
/**
* @var Client
*/
protected $client;
/**
* @var Serializer
*/
protected $serializer;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var \App\Service\PytonScanjobLogger
*/
private $scanjobLogger;
/**
* @var SessionInterface
*/
private $session;
/**
* @var array
*/
private $flightPlaces = [];
/**
* Pyton constructor.
*/
public function __construct(
Client $client,
Serializer $serializer,
LoggerInterface $logger,
PytonScanjobLogger $scanjobLogger,
SessionInterface $session,
) {
$this->client = $client;
$this->serializer = $serializer;
$this->logger = $logger;
$this->scanjobLogger = $scanjobLogger;
$this->session = $session;
}
/**
* @throws Exception
*/
public function book(BasketModel $basket): bool
{
if (!$basket->getPytonBasket() instanceof PytonBasket) {
return false;
}
$basketId = $basket->getPytonBasket()->getBasketId();
//remove all known elementIDs?
$this->createBasketReceipt($basket, $basket->getPytonBasket()->getAccommodationReceipt());
if (\is_string($basket->getPytonBasket()->getFlightReceipt())) {
$this->createBasketReceipt($basket, $basket->getPytonBasket()->getFlightReceipt());
}
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '.sprintf(self::BASKET_BOOK_ENDPOINT, $basketId));
$response = $this->client->post(sprintf(self::BASKET_BOOK_ENDPOINT, $basketId), [
'headers' => [
'Session' => $this->session->getId(),
],
]);
// $this->log('post', self::BASKET_BOOK_ENDPOINT, $basketId, $response);
} catch (BadResponseException $exception) {
// $this->log('post', self::BASKET_BOOK_ENDPOINT, $basketId, $exception);
if ($this->isAlreadyBooked($exception->getResponse())) {
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Basket is already booked. Got response `%s`',
sprintf(self::BASKET_BOOK_ENDPOINT, $basketId),
$exception->getResponse()->getBody()->getContents()
)
);
return true;
}
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
sprintf(self::BASKET_BOOK_ENDPOINT, $basketId),
$exception->getResponse()->getBody()->getContents()
)
);
return false;
}
return true;
}
private function isAlreadyBooked(ResponseInterface $response): bool
{
$result = json_decode($response->getBody()->getContents(), true);
if (
\is_array($result)
&& !\array_key_exists('MessageDetail', $result)
&& self::RESPONSE_MESSAGE_ALREADY_BOOKED === $result['MessageDetail']
) {
return true;
}
return false;
}
private function createBasketReceipt(BasketModel $basket, string $receipt): bool
{
if (!$basket->getPytonBasket() instanceof PytonBasket) {
throw new \UnexpectedValueException('$pytonBasket of Basket is not defined');
}
$basketId = $basket->getPytonBasket()->getBasketId();
if(count($basket->getPytonBasket()->getElementIds()) > 0){
$receipt = json_decode($receipt, TRUE);
foreach ($basket->getPytonBasket()->getElementIds() AS $elementId){
$receipt['ElementId'] = $elementId;
}
$receipt = json_encode($receipt);
}
try {
$this->logger->log(LogLevel::CRITICAL, 'PYTON: '.sprintf(self::BASKET_CREATE_BASKET_RECEIPT_ENDPOINT, $basketId));
$response = $this->client->put(sprintf(self::BASKET_CREATE_BASKET_RECEIPT_ENDPOINT, $basketId), [
'headers' => [
'Session' => $this->session->getId(),
'CorrelationId' => Uuid::uuid4()->toString(),
],
'body' => $receipt,
]);
} catch (BadResponseException $exception) {
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
sprintf(self::BASKET_CREATE_BASKET_RECEIPT_ENDPOINT, $basketId),
$exception->getResponse()->getBody()->getContents()
)
);
return false;
}
$result = json_decode($response->getBody()->getContents(), true);
if (!\is_array($result) || !$this->isValidBasketReceiptResponse($result)) {
return false;
}
$basket->getPytonBasket()->addElementId($result['BasketActionResult']['ElementId']);
// dump($result);
return true;
}
private function isValidBasketReceiptResponse(array $result): bool
{
return !empty($result['BasketActionResult']['ElementId']) &&
!empty($result['BasketActionResult']['StatusId']) &&
'OK' === $result['BasketActionResult']['StatusId'];
}
public function createBasket(): string
{
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. self::BASKET_CREATE_ENDPOINT.' session:'.$this->session->getId());
$response = $this->client->post(self::BASKET_CREATE_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
]);
return $response->getBody()->getContents();
} catch (RequestException $exception) {
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::BASKET_CREATE_ENDPOINT,
$exception->getResponse()?->getBody()->getContents()
)
);
throw $exception;
}
}
private function getGenderNameFromText(string $text): string
{
$gender = 'Male';
if ('male' !== $text) {
$gender = 'Female';
}
return $gender;
}
public function updatePassengers(BasketModel $basket): string
{
if (!$basket->getPytonBasket() instanceof PytonBasket) {
throw new \UnexpectedValueException();
}
$basketId = $basket->getPytonBasket()->getBasketId();
$travelersData = $basket->getTravelersData();
if (!\is_array($travelersData) || !\array_key_exists('mainBooker', $travelersData)) {
throw new \InvalidArgumentException('Travelersdata of basket does not have mainBooker index');
}
$countryCode = '0031';
switch ($travelersData['mainBooker']['country']){
case 'NL': $countryCode = '0031';
case 'BE': $countryCode = '0032';
}
$body = [
'Contact' => [
'Gender' => $this->getGenderNameFromText($travelersData['mainBooker']['gender']),
'Initials' => $travelersData['mainBooker']['firstNames'],
'FirstName' => $travelersData['mainBooker']['firstNames'],
'Infix' => '',
'LastName' => $travelersData['mainBooker']['lastName'],
'Street' => $travelersData['mainBooker']['address'],
'HouseNumber' => $travelersData['mainBooker']['houseNumber'],
'PostalCode' => $travelersData['mainBooker']['zipcode'],
'City' => $travelersData['mainBooker']['city'],
'CountryISOCode' => $travelersData['mainBooker']['country'],
'Home' => [
'CountryCode' => $countryCode,
'AreaNumber' => mb_substr($travelersData['mainBooker']['mobile'], 0, 2),
'ClientNumber' => mb_substr($travelersData['mainBooker']['mobile'], 2),
],
'Mobile' => [
'CountryCode' => $countryCode,
'AreaNumber' => mb_substr($travelersData['mainBooker']['mobile'], 0, 2),
'ClientNumber' => mb_substr($travelersData['mainBooker']['mobile'], 2),
],
'Email' => $travelersData['mainBooker']['email'],
'ExtraItems' => [],
],
'EmergencyContact' => [
'FullName' => sprintf(
'%s %s',
$travelersData['mainBooker']['firstNames'],
$travelersData['mainBooker']['lastName']
),
'Home' => [
'CountryCode' => mb_substr($travelersData['mainBooker']['mobile'], 0, 1),
'AreaNumber' => mb_substr($travelersData['mainBooker']['mobile'], 1, 1),
'ClientNumber' => mb_substr($travelersData['mainBooker']['mobile'], 2),
],
'Mobile' => [
'CountryCode' => mb_substr($travelersData['mainBooker']['mobile'], 0, 1),
'AreaNumber' => mb_substr($travelersData['mainBooker']['mobile'], 1, 1),
'ClientNumber' => mb_substr($travelersData['mainBooker']['mobile'], 2),
],
'ExtraItems' => [],
],
'Travellers' => [],
];
$count = 1;
foreach ($travelersData['persons'] ?? [] as $person) {
/** @var DateTime $dob */
$dob = $person['dob'];
$body['Travellers'][] = [
'Id' => $count++,
'Gender' => $this->getGenderNameFromText($person['gender']),
'Initials' => $person['firstNames'],
'FirstName' => $person['firstNames'],
'Infix' => '',
'LastName' => $person['lastName'],
'DateOfBirth' => $dob->format('Y-m-d'),
'Nationality' => [
'Id' => '0031',
'Code' => $person['country'],
'Name' => Countries::getName($person['country']),
],
'ExtraItems' => [],
];
}
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. sprintf(self::BASKET_PASSENGERS_ENDPOINT, $basketId));
$response = $this->client->put(sprintf(self::BASKET_PASSENGERS_ENDPOINT, $basketId), [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => json_encode($body),
]);
// $this->log('put', self::BASKET_PASSENGERS_ENDPOINT, $basketId, $response);
} catch (BadResponseException $exception) {
// $this->log('put', self::BASKET_PASSENGERS_ENDPOINT, $basketId, $exception);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
sprintf(self::BASKET_PASSENGERS_ENDPOINT, $basketId),
$exception->getResponse()->getBody()->getContents()
)
);
}
return $response->getBody()->getContents();
}
/**
* @param $search
*
* @throws Exception
*
* @return string
*/
public function getPlaces($search)
{
$body = [
'Destination' => $search,
];
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. self::ACCOMMODATIONS_SEARCH_DESTINATIONS_ENDPOINT.' session: '.$this->session->getId());
$response = $this->client->post(self::ACCOMMODATIONS_SEARCH_DESTINATIONS_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
'CorrelationId' => Uuid::uuid4()->toString(),
],
'body' => json_encode($body),
]);
return $response->getBody()->getContents();
} catch (BadResponseException $exception) {
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_SEARCH_DESTINATIONS_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
throw $exception;
}
}
public function getLowestPriceForMatch(
Game $match,
AccomodationRecipe $recipe,
OutputInterface $output = null,
) {
$pageNumber = 1;
$availableAccommodations = $this->getAccommodations($match, $recipe, $pageNumber);
$maxNumberOfPages = 0;
if ($availableAccommodations->getPaging()) {
$maxNumberOfPages = $availableAccommodations->getPaging()
->getTotalNumberOfPages();
}
$availableAccommodations = array_filter(
$availableAccommodations->getAccommodations() ?? [],
function (AccommodationWrapper $accommodationWrapper) {
return $accommodationWrapper->getAccommodation()->getNumberOfStars() > 3;
}
);
if (\count($availableAccommodations) > 0) {
return reset($availableAccommodations);
}
if ($output) {
$output->writeln('No 3 star accommodations on this page');
}
while (0 === \count($availableAccommodations) && $pageNumber < $maxNumberOfPages) {
++$pageNumber;
if ($output) {
$output->writeln('Searching through page '.$pageNumber);
}
$availableAccommodations = $this->getAccommodations($match, $recipe, $pageNumber);
$availableAccommodations = array_filter(
$availableAccommodations->getAccommodations() ?? [],
function (AccommodationWrapper $accommodationWrapper) {
return $accommodationWrapper->getAccommodation()->getNumberOfStars() >= 3;
}
);
}
return reset($availableAccommodations);
}
/**
* @throws Exception
*/
public function getFlights(
Game $match,
AirportRecipe $recipe,
array $departures,
int $arrivalAirportId = 0,
): FlightResults {
$arrivals = [$arrivalAirportId];
if (0 === $arrivalAirportId) {
$result = json_decode((string) $this->getFlightPlaces($match->getStadium()->getPlace()), true);
if (!\is_array($result) || 0 === \count($result['Locations'])) {
throw new Exception('No place found');
}
$arrivalAirports = $result['Locations'] ?? [];
if (empty($arrivalAirports)) {
return new FlightResults();
}
$arrivals = array_map(function ($location) {
return $location['AirportLocation']['Id'];
}, $arrivalAirports);
}
$flightOptions = [];
foreach ($departures as $departure) {
foreach ($arrivals as $arrival) {
$flightOptions[] = [$departure, $arrival];
}
}
$flightResults = null;
foreach ($flightOptions as $flightOption) {
$flights = $this->getFlightsForDepartsAndArrivals(
$flightOption[0],
$flightOption[1],
$recipe->getDepartDate(),
$recipe->getReturnDate()
);
if (empty($flights)) {
continue;
}
if (!$flightResults) {
$flightResults = $this->transformFlights($match, $flights);
continue;
}
$flightResults = $flightResults->merge($this->transformFlights($match, $flights));
}
return null !== $flightResults ? $flightResults : new FlightResults();
}
/**
* @param int $pageNumber
* @param int $pageSize
*/
public function getAccommodations(
Game $match,
AccomodationRecipe $recipe,
$pageNumber = 1,
$pageSize = 10,
?BasketModel $basket = null,
): Results {
if ($pageSize > 10) {
throw new InvalidArgumentException('PageSize has a limit of 10');
}
$travellersInRooms = [];
if ($basket) {
$rooms = $basket->getRooms();
$persons = range(1, (int) $basket->getChildren() + (int) $basket->getAdults());
foreach ($persons as $index => $person) {
$travellersInRooms[$index % $rooms]['PaxIds'][] = $person;
}
}
$body = [
'Travellers' => [],
'Filters' => [
'Destination' => [
'Type' => 'Place',
'Id' => (string) $match->getStadium()->getPlaceId(),
],
'ArriveDate' => $recipe->getStartDate()->format('Y-m-d'),
'DepartDate' => $recipe->getEndDate()->format('Y-m-d'),
],
'Paging' => [
'PageNumber' => $pageNumber,
'PageSize' => $pageSize,
],
'TravellersInRooms' => $travellersInRooms,
];
/* @var DateTime $dateOfBirth */
foreach (range(1, $basket->getAdults()) as $person) {
$body['Travellers'][] = ['DateOfBirth' => '1986-08-03'];
}
$childrenDob = new DateTime();
$childrenDob->modify('- 16 year');
if ($basket->getChildren()) {
foreach (range(1, $basket->getChildren()) as $person) {
$body['Travellers'][] = ['DateOfBirth' => $childrenDob->format('Y-m-d')];
}
}
try {
$request = json_encode($body);
$this->logger->log(LogLevel::INFO, 'PYTON: '.self::ACCOMMODATIONS_SEARCH_ENDPOINT.' session:'.$this->session->getId() );
$response = $this->client->post(self::ACCOMMODATIONS_SEARCH_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => $request,
]);
} catch (BadResponseException $exception) {
$this->log('post', self::ACCOMMODATIONS_SEARCH_ENDPOINT, null, $exception, $request);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_SEARCH_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
}
$results = $this->serializer->deserialize($response->getBody()->getContents(), Results::class, 'json');
return $results;
}
/**
* @param bool $bubbleExceptions
*/
public function getVirtualAccommodationsByMatch(
Game $match,
int $travellers = 1,
int $durationInDays = 1,
int $pageNumber = 1,
$bubbleExceptions = false,
): Results {
$matchDate = clone $match->getDate();
$arriveDate = $matchDate->format('Y-m-d');
$departDate = $matchDate->add(new DateInterval('P'.$durationInDays.'D'))->format('Y-m-d');
$body = [
'Travellers' => [],
'Filters' => [
'Destination' => [
'Type' => 'Place',
'Id' => (string) $match->getStadium()->getPlaceId(),
],
'ArriveDate' => $arriveDate,
'DepartDate' => $departDate,
],
'Paging' => [
'PageNumber' => $pageNumber,
'PageSize' => 1000,
],
'TravellersInRooms' => [
['PaxIds' => range(1, $travellers)],
],
];
foreach (range(1, $travellers) as $traveler) {
$body['Travellers'][] = ['DateOfBirth' => '1970-01-01'];
}
$request = json_encode($body);
$this->scanjobLogger->insert(
self::ACCOMMODATIONS_SEARCH_ENDPOINT,
$this->session->getId(),
new \DateTime(),
$request,
''
);
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. self::ACCOMMODATIONS_SEARCH_ENDPOINT.' session:'.$this->session->getId());
$response = $this->client->post(self::ACCOMMODATIONS_SEARCH_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => $request,
]);
} catch (BadResponseException $exception) {
$this->scanjobLogger->insert(
$exception->getRequest()->getUri()->getPath(),
$this->session->getId(),
new \DateTime(),
$request,
$exception->getResponse()->getBody()->getContents()
);
if (true === $bubbleExceptions) {
throw $exception;
}
$this->log('post', self::ACCOMMODATIONS_SEARCH_ENDPOINT, null, $exception, $request);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_SEARCH_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
}
$results = $this->serializer->deserialize($response->getBody()->getContents(), Results::class, 'json');
return $results;
}
public function getVirtualAccommodationDetailbyMatch(
Game $match,
int $travellers,
int $durationInDays,
AccommodationWrapper $accommodation,
): string {
$matchDate = clone $match->getDate();
$arriveDate = $matchDate->format('Y-m-d');
$departDate = $matchDate->add(new DateInterval('P'.$durationInDays.'D'))->format('Y-m-d');
$body = [
'Travellers' => [],
'TravellersInRooms' => [
['PaxIds' => range(1, $travellers)],
],
];
foreach (range(1, $travellers) as $traveler) {
$body['Travellers'][] = ['DateOfBirth' => '1970-01-01'];
}
$body['SelectedAccommodations'][] = $accommodation;
$request = $this->serializer->serialize($body, 'json');
$this->scanjobLogger->insert(
self::ACCOMMODATIONS_DETAIL_ENDPOINT,
$this->session->getId(),
new \DateTime(),
$request,
''
);
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. self::ACCOMMODATIONS_DETAIL_ENDPOINT.' session: '.$this->session->getId());
$response = $this->client->post(self::ACCOMMODATIONS_DETAIL_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => $request,
]);
} catch (BadResponseException $exception) {
$this->scanjobLogger->insert(
$exception->getRequest()->getUri()->getPath(),
$this->session->getId(),
new \DateTime(),
$request,
$exception->getResponse()->getBody()->getContents()
);
// NOTE: Do Not Throw Exception when this fails. Just return string nok
// throw $exception;
$this->log('post', self::ACCOMMODATIONS_DETAIL_ENDPOINT, null, $exception, $request);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_DETAIL_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
return 'nok';
}
$results = $response->getBody()->getContents();
return $results;
}
public function createAccommodationReceipt(BasketModel $basket, AccommodationReceipt $receipt, Game $match, $selectedRooms = null, $roomlayout = null): bool
{
if ($selectedAccommodationDetails = $this->getAccommodationDetails($basket, $receipt, $roomlayout)) {
$accommodation = json_decode($selectedAccommodationDetails, true);
$firstAccommodation = current($accommodation['Accommodations']);
$body = [
'Travellers' => [],
'SelectedAccommodations' => [$firstAccommodation],
'Assignments' => [],
];
foreach ($receipt->getTravellers() as $dateOfBirth) {
$body['Travellers'][] = ['DateOfBirth' => $dateOfBirth->format('Y-m-d')];
}
// dump($roomlayout);
$travellersInRooms = [];
if ($basket) {
$rooms = $basket->getRooms();
$persons = range(1, $basket->getChildren() + $basket->getAdults());
if (!$roomlayout) {
foreach ($persons as $index => $person) {
$travellersInRooms[$index % $rooms]['PaxIds'][] = $person;
}
} else {
$j = 0;
foreach ($roomlayout as $room => $numberpersons) {
$travellersInRooms[$room]['PaxIds'] = [];
for ($i = 0; $i < $numberpersons; ++$i) {
$travellersInRooms[$room]['PaxIds'][] = $persons[$j];
++$j;
}
}
}
}
// dump($travellersInRooms);
if (!$selectedRooms) {
// dump('er zijn geen selected kamers');
$travellersInRooms = [];
if ($basket) {
$rooms = $basket->getRooms();
$persons = range(1, $basket->getChildren() + $basket->getAdults());
if (!$roomlayout) {
foreach ($persons as $index => $person) {
$travellersInRooms[$index % $rooms]['PaxIds'][] = $person;
}
} else {
$j = 0;
foreach ($roomlayout as $room => $numberpersons) {
$travellersInRooms[$room]['PaxIds'] = [];
for ($i = 0; $i < $numberpersons; ++$i) {
$travellersInRooms[$room]['PaxIds'][] = $persons[$j];
++$j;
}
}
}
}
foreach ($travellersInRooms as $travellersInRoom) {
$paxIds = $travellersInRoom['PaxIds'];
foreach ($body['SelectedAccommodations'][0]['Units'] as &$unit) {
if (\count($paxIds) < $unit['Occupancy']['Minimal']) {
continue;
}
if (\count($paxIds) > $unit['Occupancy']['Maximal']) {
continue;
}
if ($unit['Allotment']['Chosen'] === $unit['Allotment']['Available']) {
continue;
}
$unit['Allotment']['Chosen'] = $unit['Allotment']['Chosen'] + 1;
$firstBoardOfUnit = current($unit['Boards']);
$body['Assignments'][] = [
'Board' => $firstBoardOfUnit['Code'],
'UnitId' => $unit['Id'],
'PaxIds' => $paxIds,
];
break;
}
}
} else {
// dump('er zijn wel geselecteerde kamers door gebruiker');
$count = 0;
// $travellersInRooms = array_reverse($travellersInRooms);
// dump($travellersInRooms);
$body['Assignments'] = null;
foreach ($selectedRooms as $room) {
// dump("op zoek naar iemand in kamer: ".$room);
$paxIds = $travellersInRooms[$count]['PaxIds'];
// dd($paxIds);
// $accommodation = json_decode($basket->getPytonBasket()->getAccomodation(), true);
// foreach($accommodation['Units'] AS $unit){
foreach ($body['SelectedAccommodations'][0]['Units'] as &$unit) {
$board = current($unit['Boards']);
$roomIdentifier = $unit['Description'].$unit['Type'].$board['Name'];
if ($roomIdentifier !== $room) {
continue;
}
// dump('roomIdentifier '.$roomIdentifier.' is gelijk aan '.$room);
$body['Assignments'][] = [
'Board' => $board['Code'],
'UnitId' => $unit['Id'],
'PaxIds' => $paxIds,
];
// dump("Aantql gekozen is nu ".$unit['Allotment']['Chosen']);
$unit['Allotment']['Chosen'] = $unit['Allotment']['Chosen'] + 1;
break;
}
++$count;
}
}
// dd("hier altijd stoppen");
// dd($body);
try {
$request = $this->serializer->serialize($body, 'json');
// dump($this->session->getId());
// dd($request);
$this->logger->log(LogLevel::INFO, 'PYTON: '. self::ACCOMMODATIONS_RECEIPT_ENDPOINT.' session: '.$this->session->getId());
$response = $this->client->post(self::ACCOMMODATIONS_RECEIPT_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => $request,
]);
// dump(self::ACCOMMODATIONS_RECEIPT_ENDPOINT);
// dd($this->session->getId());
$responseContent = $response->getBody()->getContents();
$result = json_decode($responseContent, true);
$roomsTotalValue = $result['Receipt']['Prices']['Total']['Value'];
$this->syncBasketAccommodationListPrice(
$basket,
$roomsTotalValue,
\count($receipt->getTravellers()),
$match
);
$this->log('post', self::ACCOMMODATIONS_RECEIPT_ENDPOINT, null, $responseContent, $request);
$this->logger->log(
LogLevel::DEBUG,
sprintf(
'DEBUG: info when sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_RECEIPT_ENDPOINT,
$responseContent
)
);
$basket->getPytonBasket()->setAccommodationReceipt($responseContent);
$basket->getPytonBasket()->setAccomodation(json_encode($firstAccommodation));
// dd("Er ging iets goed");
return true;
} catch (BadResponseException $exception) {
$this->log('post', self::ACCOMMODATIONS_RECEIPT_ENDPOINT, null, $exception, $request);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_RECEIPT_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
// dump($exception);
// dd("Er ging iets fout");
return false;
}
}
return false;
}
/**
* @return string
*/
public function getAccommodationDetails(BasketModel $basket, AccommodationReceipt $receipt, $roomlayout = null)
{
$travellersInRooms = [];
if ($basket) {
$rooms = $basket->getRooms();
if($rooms === null) { $rooms = 1;}
$persons = range(1, $basket->getChildren() + $basket->getAdults());
if (!$roomlayout) {
foreach ($persons as $index => $person) {
$travellersInRooms[$index % $rooms]['PaxIds'][] = $person;
}
} else {
$j = 0;
foreach ($roomlayout as $room => $numberpersons) {
$travellersInRooms[$room]['PaxIds'] = [];
for ($i = 0; $i < $numberpersons; ++$i) {
$travellersInRooms[$room]['PaxIds'][] = $persons[$j];
++$j;
}
}
}
}
$body = [
'Travellers' => [],
'SelectedAccommodations' => [],
'TravellersInRooms' => $travellersInRooms,
];
dump($receipt->getTravellers());
foreach ($receipt->getTravellers() as $dateOfBirth) {
$body['Travellers'][] = ['DateOfBirth' => $dateOfBirth->format('Y-m-d')];
}
foreach ($receipt->getAccommodations() as $accommodation) {
$body['SelectedAccommodations'][] = $accommodation;
}
dd($body);
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '.self::ACCOMMODATIONS_DETAIL_ENDPOINT.' session: '.$this->session->getId());
$response = $this->client->post(self::ACCOMMODATIONS_DETAIL_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => $this->serializer->serialize($body, 'json'),
]);
// echo json_encode([
// 'headers' => [
// 'Session' => $this->session->getId(),
// ],
// 'body' => $this->serializer->serialize($body, 'json'),
// ]);
$this->log('post', self::ACCOMMODATIONS_DETAIL_ENDPOINT, null, $response);
// dd($response->getBody()->getContents());
return $response->getBody()->getContents();
} catch (ClientException $exception) {
$exceptionContent = $exception->getResponse()->getBody()->getContents();
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'ClientException on sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_DETAIL_ENDPOINT,
$exceptionContent
)
);
$this->scanjobLogger->insert(
$exception->getRequest()->getUri()->getPath(),
$this->session->getId(),
new \DateTime(),
$this->serializer->serialize($body, 'json'),
$exceptionContent
);
return false;
} catch (BadResponseException $exception) {
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::ACCOMMODATIONS_DETAIL_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
return false;
}
}
public function getAirports(): string
{
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. self::BASICDATA_AIRPORTS_ENDPOINT.' session: '.$this->session->getId());
$response = $this->client->get(self::BASICDATA_AIRPORTS_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
]);
// $this->log('get', self::BASICDATA_AIRPORTS_ENDPOINT, null, $response);
} catch (BadResponseException $exception) {
// $this->log('get', self::BASICDATA_AIRPORTS_ENDPOINT, null, $exception);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::BASICDATA_AIRPORTS_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
}
return $response->getBody()->getContents();
}
/**
* @param $search
*
* @throws Exception
*
* @return string
*/
public function getFlightPlaces($search): ?string
{
if (isset($this->flightPlaces[$search])) {
return $this->flightPlaces[$search];
}
$body = [
'Destination' => $search,
'DestinationType' => 'Depart',
];
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. self::FLIGHTS_SEARCH_DESTINATIONS_ENDPOINT.' session: '.$this->session->getId());
$response = $this->client->post(self::FLIGHTS_SEARCH_DESTINATIONS_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
'CorrelationId' => Uuid::uuid4()->toString(),
],
'body' => json_encode($body),
]);
$this->flightPlaces[$search] = $response->getBody()->getContents();
} catch (BadResponseException $exception) {
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::FLIGHTS_SEARCH_DESTINATIONS_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
}
return $this->flightPlaces[$search];
}
public function createFlightReceipt(BasketModel $basket, FlightReceipt $receipt): bool
{
$body = [
'Travellers' => [],
'Assignment' => [
'TransportId' => $receipt->getTrips()->first()->getId(),
'ClassId' => '1',
'PaxIds' => range(1, \count($receipt->getTravellers())),
],
'SelectedFlight' => $this->getFlightDetails($receipt),
];
foreach ($receipt->getTravellers()->toArray() as $dateOfBirth) {
/* @var DateTime $dateOfBirth */
$body['Travellers'][] = ['DateOfBirth' => $dateOfBirth->format('Y-m-d')];
}
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '.self::FLIGHTS_RECEIPT.' session: '.$this->session->getId());
$response = $this->client->post(self::FLIGHTS_RECEIPT, [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => $this->serializer->serialize($body, 'json'),
]);
// $this->log('post', self::FLIGHTS_RECEIPT, null, $response);
} catch (BadResponseException $exception) {
// $this->log('post', self::FLIGHTS_RECEIPT, null, $exception);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::FLIGHTS_RECEIPT,
$exception->getResponse()->getBody()->getContents()
)
);
return false;
}
$basket->getPytonBasket()->setFlightReceipt($response->getBody()->getContents());
return true;
}
public function getBasket($basketId)
{
$this->logger->log(LogLevel::INFO, 'PYTON: '. '/api/basket/'.$basketId.' session: '.$this->session->getId());
$response = $this->client->get('/api/basket/'.$basketId, [
'headers' => [
'Session' => $this->session->getId(),
],
]);
// $this->log('get', '/api/basket/' . $basketId, null, $response);
return $response->getBody()->getContents();
}
public function getArriveAirports(Game $match): array
{
$airports = [];
try {
$locations = json_decode(
$this->getFlightPlaces(
$match->getStadium()
->getPlace()
),
true
);
if (!isset($locations['Locations']) || !$locations['Locations']) {
return $airports;
}
foreach ($locations['Locations'] as $location) {
$airports[$location['AirportLocation']['Id']] = $location['AirportLocation']['Name'].' ('.
$location['CountryLocation']['Name'].')';
}
return $airports;
} catch (Exception $exception) {
return $airports;
}
}
/**
* @return string
*/
public function getFlightDetails(FlightReceipt $receipt): array
{
$body = [
'Travellers' => [],
'SelectedFlight' => [
'Legs' => [],
'Trips' => [],
],
'FirstDeparture' => $receipt->getFirstDeparture(),
'FinalDestination' => $receipt->getFinalDestination(),
'HasTrips' => true,
];
foreach ($receipt->getTravellers()->toArray() as $dateOfBirth) {
/* @var DateTime $dateOfBirth */
$body['Travellers'][] = ['DateOfBirth' => $dateOfBirth->format('Y-m-d')];
}
foreach ($receipt->getLegs() as $leg) {
$body['SelectedFlight']['Legs'][] = $leg;
}
foreach ($receipt->getTrips()->toArray() as $trip) {
$body['SelectedFlight']['Trips'][] = $trip;
}
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '.self::FLIGHTS_DETAIL_ENDPOINT.' session: '.$this->session->getId());
$response = $this->client->post(self::FLIGHTS_DETAIL_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
],
'body' => $this->serializer->serialize($body, 'json'),
]);
// $this->log('post', self::FLIGHTS_DETAIL_ENDPOINT, null, $response);
return json_decode($response->getBody()->getContents(), true)['Flights'];
} catch (BadResponseException $exception) {
// $this->log('post', self::FLIGHTS_DETAIL_ENDPOINT, null, $exception);
$this->logger->log(
LogLevel::CRITICAL,
sprintf(
'Error trigger when sending request to endpoint `%s`. Got response `%s`',
self::FLIGHTS_DETAIL_ENDPOINT,
$exception->getResponse()->getBody()->getContents()
)
);
return [];
}
}
public function log($type, $endpoint, $basket = '', $response = null, $request = null)
{
// echo '<pre>';
//
// echo "Endpoint:\t{$endpoint} <br/>";
// echo "SessionId:\t" . $this->session->getId(). "<br/>";
// echo "DateTime:\t" . (new DateTime())->format('d-m-Y h:i:s');
// echo '<hr>';
// echo "Request: <br/><br/>";
// echo $request;
// echo '<hr>';
// echo "Response: <br/><br/>";
// echo $response->getBody()->getContents();
// exit;
$handle = fopen('test.log', 'a');
fwrite(
$handle,
sprintf(
'%s : %s %s',
(new DateTime())->format('d-m-Y H:i:s'),
$type,
$endpoint
)."\r\n"
);
fclose($handle);
}
/**
* @param $accommodations
* @param $match
*
* @return mixed
*/
public function processPriceDifference($results, $match)
{
foreach ($results->getAccommodations() as &$accommodation) {
if (!$match->getCheapestAccommodation()) {
continue;
}
$priceDetails = $accommodation->getPriceDetails();
$price = $priceDetails->getPrice();
$cheapestAccommodationPrice = $match->getCheapestAccommodation()->getAmount();
if(!in_array(
$match->getStadium()->getCountry()->getSlug(),
$this->ignoreFinalCountries,
true
)){
if (!$match->isFinal() && !in_array($match->getStadium()->getCountry()->getSlug(),['duitsland', 'frankrijk', 'denemarken'])) {
$cheapestAccommodationPrice = $match->getCheapestAccommodation()->getAmount() * 2;
$price->setValue(
$price->getValue() * 2
);
}
}
$difference = $price->getValue() - $cheapestAccommodationPrice;
$difference = ceil($difference / 100) * 100;
$price->setDifference($difference);
$priceDetails->setPrice($price);
}
return $results;
}
/**
* @param int $priceValue
*/
private function syncBasketAccommodationListPrice(BasketModel $basket, int $totalPriceValue, $travelers, Game $match)
{
$selectedAccommodationWrapper = null;
$matchCheapestAccommodation = $match->getCheapestAccommodation();
if(!$match->isFinal() && !in_array($match->getStadium()->getCountry()->getSlug(),['duitsland', 'frankrijk', 'denemarken'])){
$priceForExtraDay = ceil($match->getCheapestAccommodation()->multiply(0.7,Money::ROUND_UP)->getAmount()/100)*100;
$matchCheapestAccommodation = $matchCheapestAccommodation->add( Money::EUR($priceForExtraDay));
}
foreach ($basket->getAccomodationList() as &$accommodationWrapper) {
if ($basket->getAccommodation() === $accommodationWrapper->getAccommodation()->getId()) {
$totalPricePerPerson = ($totalPriceValue / $travelers);
$roundedPriceValue = ceil($totalPricePerPerson / 100) * 100;
$accommodationWrapper->getPriceDetails()->getPrice()
->setValue($roundedPriceValue)
->setDifference($roundedPriceValue - $matchCheapestAccommodation->getAmount());
}
}
}
private function supportsDiscounts(string $basketId): bool
{
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '. sprintf(self::ACTIONCODE_AVAILABLE_ENDPOINT, $basketId));
$response = $this->client->get(sprintf(self::ACTIONCODE_AVAILABLE_ENDPOINT, $basketId), [
'headers' => [
'Session' => $this->session->getId(),
'CorrelationId' => Uuid::uuid4()->toString(),
],
]);
$content = $response->getBody()->getContents();
if ('false' !== $content) {
return true;
}
} catch (Exception $exception) {
}
return false;
}
private function fetchDiscountReceipt(string $basketId, string $actionCode): ?string
{
try {
$this->logger->log(LogLevel::CRITICAL, 'PYTON: '. sprintf(self::ACTIONCODE_CHECK_ENDPOINT, $basketId, $actionCode));
$this->logger->log(LogLevel::CRITICAL, 'PYTON: SESSION ID'. $this->session->getId());
$response = $this->client->get(
sprintf(self::ACTIONCODE_CHECK_ENDPOINT, $basketId, $actionCode),
[
'headers' => [
'Session' => $this->session->getId(),
'CorrelationId' => Uuid::uuid4()->toString(),
],
]
);
$content = $response->getBody()->getContents();
$this->logger->log(LogLevel::CRITICAL, 'PYTON: SESSION ID'. $this->session->getId());
$this->logger->log(LogLevel::CRITICAL, 'PYTON: RESPONSE'. $content);
if ('false' !== $content) {
return $content;
}
} catch (Exception $exception) {
$exception->getMessage();
}
return null;
}
/**
* @throws Exception
*/
public function applyDiscount(BasketModel $basket, string $actionCode, Game $match): bool
{
$basketId = $basket->getPytonBasket()->getBasketId();
$this->createBasketReceipt($basket, $basket->getPytonBasket()->getAccommodationReceipt());
$discountReceipt = $this->fetchDiscountReceipt($basketId, $actionCode);
// dd($discountReceipt);
if (
$this->supportsDiscounts($basketId) &&
$discountReceipt &&
$totalDiscount = $this->extractTotalDiscountPrice($discountReceipt)
) {
$basket->setDiscountReceipt($discountReceipt);
$basket->setDiscountTotal($totalDiscount);
return true;
}
return false;
}
private function extractTotalDiscountPrice(string $discountReceipt): ?Money
{
$discountReceipt = json_decode($discountReceipt, true);
if (
$discountReceipt['Receipt'] &&
$discountReceipt['Receipt']['Prices'] &&
$total = $discountReceipt['Receipt']['Prices']['Total']
) {
return new Money(
$total['Value'],
new Currency($total['Currency'])
);
}
return null;
}
private function getFlightsForDepartsAndArrivals(
$departureId,
$arrivalId,
$departureDate,
$returnDate,
$pageNumber = 1,
) {
$body = [
'Travellers' => [
['DateOfBirth' => '1983-06-24'],
],
'Filters' => [
'DepartLocation' => [
'Type' => 'Airport',
'Id' => $departureId,
],
'ArriveLocation' => [
'Type' => 'Airport',
'Id' => $arrivalId,
],
'DepartDate' => $departureDate->format('Y-m-d'),
'ReturnDate' => $returnDate->format('Y-m-d'),
'OnlyDirectFlights' => false,
'IncludeCheapestLuggageInPrice' => false,
'FlightType' => 'OnlyRoundTrips',
'FlightClasses' => 'All',
'NumberOfStopovers' => 0,
],
'Paging' => [
'PageNumber' => $pageNumber,
'PageSize' => 10,
],
];
try {
$this->logger->log(LogLevel::INFO, 'PYTON: '.self::FLIGHTS_SEARCH_FLIGHTS_ENDPOINT.' session: '.$this->session->getId());
$rawResponse = $this->client->post(self::FLIGHTS_SEARCH_FLIGHTS_ENDPOINT, [
'headers' => [
'Session' => $this->session->getId(),
'CorrelationId' => Uuid::uuid4()->toString(),
],
'body' => json_encode($body),
])->getBody()->getContents();
$result = json_decode($rawResponse, true);
if (empty($result['Paging'])) {
return $result;
}
if ($result['Paging']['HasMoreResults'] && 1 === $pageNumber) {
foreach (range(2, $result['Paging']['TotalNumberOfPages']) as $page) {
$pagedResult = $this->getFlightsForDepartsAndArrivals(
$departureId,
$arrivalId,
$departureDate,
$returnDate,
$page
);
$result['Flights']['Trips'] = array_merge_recursive(
$result['Flights']['Trips'],
$pagedResult['Flights']['Trips']
);
$result['Flights']['Legs'] = array_merge_recursive(
$result['Flights']['Legs'],
$pagedResult['Flights']['Legs']
);
}
}
} catch (RequestException $exception) {
return [];
}
return $result;
}
private function transformFlights(Game $match, array $response): FlightResults
{
$minimumArriveDatetime = clone $match->getDate();
$minimumArriveDatetime = $minimumArriveDatetime->modify(self::MATCH_ARRIVAL_MIN_INTERVAL);
$whiteListedTrips = [];
$flightResultsPrototype = [];
$flightResultsPrototype['Paging'] = !empty($response['Paging']) ? $response['Paging'] : [];
$flightResultsPrototype['Flights'] = [
'Legs' => [],
'Trips' => [],
'FirstDeparture' => [],
'FinalDestination' => [],
'HasTrips' => $response['Flights']['HasTrips'],
];
$flightResultsPrototype['Filters'] = $response['Filters'];
foreach ($response['Flights']['Legs'] as $i => $leg) {
if ('TO' === mb_substr($leg['Id'], 0, 2)) {
$arriveDatetime = new \DateTime(
$leg['Arrive']['Date'].' '.
$leg['Arrive']['Time']
);
if ($arriveDatetime <= $minimumArriveDatetime) {
$flightResultsPrototype['Flights']['Legs'][] = $leg;
$whiteListedTrips[] = mb_substr($leg['Id'], 3);
$flightResultsPrototype['Flights']['Legs'][] = $response['Flights']['Legs'][$i + 1];
}
}
}
foreach ($response['Flights']['Trips'] as $i => $trip) {
if (\in_array($trip['Id'], $whiteListedTrips, true)) {
$flightResultsPrototype['Flights']['Trips'][] = $trip;
}
}
$flightResultsPrototype = json_encode($flightResultsPrototype);
return $this->serializer->deserialize($flightResultsPrototype, FlightResults::class, 'json');
}
}