Covivo/mobicoop

View on GitHub
api/src/DataProvider/Entity/ConduentPTProvider.php

Summary

Maintainability
F
4 days
Test Coverage
<?php

/**
 * Copyright (c) 2020, MOBICOOP. All rights reserved.
 * This project is dual licensed under AGPL and proprietary licence.
 ***************************
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <gnu.org/licenses>.
 ***************************
 *    Licence MOBICOOP described in the file
 *    LICENSE
 */

namespace App\DataProvider\Entity;

use App\DataProvider\Exception\DataProviderException;
use App\DataProvider\Interfaces\ProviderInterface;
use App\DataProvider\Service\DataProvider;
use App\Geography\Entity\Address;
use App\PublicTransport\Entity\PTArrival;
use App\PublicTransport\Entity\PTCompany;
use App\PublicTransport\Entity\PTDeparture;
use App\PublicTransport\Entity\PTJourney;
use App\PublicTransport\Entity\PTLeg;
use App\PublicTransport\Entity\PTLine;
use App\Travel\Entity\TravelMode;

/**
 * Conduent Public Transportation data provider.
 *
 * @author Maxime Bardot <maxime.bardot@mobicoop.org>
 */
class ConduentPTProvider implements ProviderInterface
{
    private const PT_MODE_CAR = 'Driving';
    private const PT_MODE_BUS = 'Bus';
    private const PT_MODE_TRAIN_LOCAL = 'Train';
    private const PT_MODE_WALK = 'Walking';
    private const PT_MODE_SUBWAY = 'Subway';
    private const PT_MODE_WAITING = 'Waiting';
    private const PT_MODE_BIKE = 'Biking';

    private const COUNTRY = 'France';
    private const NC = '';

    private const PROFILE_RESSOURCE = 'MCP.ID.API/profiles';
    private const COLLECTION_RESSOURCE_JOURNEYS = 'MCP.TSUP.API/travelQueries/full';

    private const DATETIME_INPUT_FORMAT = 'Y-m-d\\TH:i:s';

    private const NB_TRAVELERS = 3;
    private const ALLOW_EXTENDED_QUERIES_IN_PAST = 1;

    private const DEFAULT_PROFILE_ID = 5275;

    private $collection;
    private $uri;

    public function __construct(string $uri)
    {
        $this->collection = [];
        $this->uri = $uri;
    }

    /**
     * {@inheritdoc}
     */
    public function getCollection(string $class, string $apikey, array $params)
    {
        switch ($class) {
            case PTJourney::class:
                $this->getCollectionJourneys($class, $params, $apikey);

                return $this->collection;

               break;

            default:
                break;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getItem(string $class, string $apikey, array $params)
    {
    }

    /**
     * {@inheritdoc}
     */
    public function deserialize(string $class, array $data)
    {
        switch ($class) {
            case PTJourney::class:
                return $this->deserializeJourney($data);

                break;

            default:
                break;
        }
    }

    private function getCollectionJourneys($class, array $params, string $apikey)
    {
        // Get profile id
        $dataProvider = new DataProvider($this->uri, self::PROFILE_RESSOURCE);
        $paramsGet = [
            'criteria.name' => $params['username'],
        ];
        $headers = [
            'Cookie' => 'XAuthToken=ApiKey-'.$apikey,
        ];
        $response = $dataProvider->getCollection($paramsGet, $headers);

        $profileId = self::DEFAULT_PROFILE_ID;
        if (200 == $response->getCode()) {
            $data = json_decode((string) $response->getValue(), true);
            if (isset($data['data'], $data['data']['list'])) {
                foreach ($data['data']['list'] as $profile) {
                    if (isset($profile['data'], $profile['data']['profileId']) && $profile['data']['isDefault']) {
                        $profileId = $profile['data']['profileId'];
                    }
                }
            }
        }

        // Do the PT search
        $dataProvider = new DataProvider($this->uri, self::COLLECTION_RESSOURCE_JOURNEYS);

        $paramsPost = [
            'origin' => [
                'latitude' => $params['origin_latitude'],
                'longitude' => $params['origin_longitude'],
            ],
            'destination' => [
                'latitude' => $params['destination_latitude'],
                'longitude' => $params['destination_longitude'],
            ],
            'travelerProfileId' => $profileId,
            'nbTravelers' => self::NB_TRAVELERS,
            'allowExtendedQueriesInPast' => self::ALLOW_EXTENDED_QUERIES_IN_PAST,
            'departureDate' => $params['date']->format(self::DATETIME_INPUT_FORMAT).'Z',
        ];

        $response = $dataProvider->postCollection($paramsPost, $headers);
        if (200 == $response->getCode()) {
            $data = json_decode((string) $response->getValue(), true);

            foreach ($data['data']['list'] as $trip) {
                $journey = $this->deserialize($class, $trip);
                if (is_null($journey)) {
                    continue;
                }
                $this->collection[] = $journey;
            }
        } elseif (510 == $response->getCode()) {
            // Out of bound for conduent
            // throw new DataProviderException(DataProviderException::OUT_OF_BOUND);
            // For out of bound we do nothing. We just treat it as a no found solution
        } else {
            throw new DataProviderException(DataProviderException::ERROR_COLLECTION_RESSOURCE_JOURNEYS);
        }
    }

    private function deserializeJourney($data)
    {
        $journey = new PTJourney(count($this->collection) + 1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
        if (isset($data['data']['result']['travelDuration'])) {
            $journey->setDuration($this->convertToSeconds($data['data']['result']['travelDuration']));
        }
        if (isset($data['data']['result']['nbConnections'])) {
            $journey->setChangeNumber($data['data']['result']['nbConnections']);
        }

        $departure = new PTDeparture(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
        if ($data['data']['result']['departureDate']) {
            $departure->setDate(new \DateTime($data['data']['result']['departureDate']));
        }

        $departureAddress = new Address();
        $departureAddress->setId(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
        $departureAddress->setAddressCountry(self::COUNTRY);

        if (isset($data['data']['result']['originTown'])) {
            $departureAddress->setAddressLocality($data['data']['result']['originTown']);
        } else {
            $departureAddress->setAddressLocality(self::NC);
        }

        if (isset($data['data']['result']['originName'])) {
            $departureAddress->setStreetAddress($data['data']['result']['originName']);
        } else {
            $departureAddress->setStreetAddress(self::NC);
        }

        if (isset($data['data']['result']['origin'], $data['data']['result']['origin']['latitude'])) {
            $departureAddress->setLatitude($data['data']['result']['origin']['latitude']);
        }
        if (isset($data['data']['result']['origin'], $data['data']['result']['origin']['longitude'])) {
            $departureAddress->setLongitude($data['data']['result']['origin']['longitude']);
        }
        $departure->setAddress($departureAddress);

        $journey->setPTDeparture($departure);

        $arrival = new PTArrival(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
        if ($data['data']['result']['arrivalDate']) {
            $arrival->setDate(new \DateTime($data['data']['result']['arrivalDate']));
        }

        $arrivalAddress = new Address();
        $arrivalAddress->setId(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
        $arrivalAddress->setAddressCountry(self::COUNTRY);

        if (isset($data['data']['result']['destinationTown'])) {
            $arrivalAddress->setAddressLocality($data['data']['result']['destinationTown']);
        } else {
            $arrivalAddress->setAddressLocality(self::NC);
        }

        if (isset($data['data']['result']['destinationName'])) {
            $arrivalAddress->setStreetAddress($data['data']['result']['destinationName']);
        } else {
            $arrivalAddress->setStreetAddress(self::NC);
        }

        if (isset($data['data']['result']['destination'], $data['data']['result']['destination']['latitude'])) {
            $arrivalAddress->setLatitude($data['data']['result']['destination']['latitude']);
        }
        if (isset($data['data']['result']['destination'], $data['data']['result']['destination']['longitude'])) {
            $arrivalAddress->setLongitude($data['data']['result']['destination']['longitude']);
        }
        $arrival->setAddress($arrivalAddress);

        $journey->setPTArrival($arrival);

        if (isset($data['data']['result']['travelSections'])) {
            $nblegs = 0;
            foreach ($data['data']['result']['travelSections'] as $travelSection) {
                $leg = $this->deserializeTravelSection($travelSection, $nblegs);
                if (is_null($leg)) {
                    return null;
                }
                ++$nblegs;
                $journey->addPTLeg($leg);
            }
        }
        if (isset($data['data']['environment']['totalEnvironmentalCost'])) {
            $journey->setCo2($data['data']['environment']['totalEnvironmentalCost']);
        }

        return $journey;
    }

    private function deserializeTravelSection($data, $num)
    {
        $leg = new PTLeg($num);
        if (isset($data['data']) && !is_null($data['data'])) {
            if (self::PT_MODE_WALK == $data['data']['modalityDescription']['modality']) {
                // walk mode
                $travelMode = new TravelMode(TravelMode::TRAVEL_MODE_WALK);
                $leg->setTravelMode($travelMode);
            } elseif (self::PT_MODE_CAR == $data['data']['modalityDescription']['modality']) {
                // car mode
                $travelMode = new TravelMode(TravelMode::TRAVEL_MODE_CAR);
                $leg->setTravelMode($travelMode);
            } elseif (self::PT_MODE_BUS == $data['data']['modalityDescription']['modality']) {
                // bus mode
                $travelMode = new TravelMode(TravelMode::TRAVEL_MODE_BUS);
                $leg->setTravelMode($travelMode);
            } elseif (self::PT_MODE_TRAIN_LOCAL == $data['data']['modalityDescription']['modality']) {
                // train local mode
                $travelMode = new TravelMode(TravelMode::TRAVEL_MODE_TRAIN_LOCAL);
                $leg->setTravelMode($travelMode);
            } elseif (self::PT_MODE_SUBWAY == $data['data']['modalityDescription']['modality']) {
                // subway
                $travelMode = new TravelMode(TravelMode::TRAVEL_MODE_SUBWAY);
                $leg->setTravelMode($travelMode);
            } elseif (self::PT_MODE_WAITING == $data['data']['modalityDescription']['modality']) {
                // waiting
                $travelMode = new TravelMode(TravelMode::TRAVEL_MODE_WAITING);
                $leg->setTravelMode($travelMode);
            } elseif (self::PT_MODE_BIKE == $data['data']['modalityDescription']['modality']) {
                // waiting
                $travelMode = new TravelMode(TravelMode::TRAVEL_MODE_BIKE);
                $leg->setTravelMode($travelMode);
            } else {
                return null;
            }

            if (isset($data['data']['duration']) && !is_null($data['data']['duration'])) {
                $leg->setDuration($this->convertToSeconds($data['data']['duration']));
            }

            if (isset($data['data']['distance']) && !is_null($data['data']['distance'])) {
                $leg->setDistance($data['data']['distance']);
            }

            if (isset($data['data']['origin'])) {
                $departure = new PTDeparture(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
                if ($data['data']['departureDate']) {
                    $departure->setDate(new \DateTime($data['data']['departureDate']));
                }

                $departureAddress = new Address();
                $departureAddress->setId(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
                $departureAddress->setAddressCountry(self::COUNTRY);

                if (isset($data['data']['origin']['town'])) {
                    $departureAddress->setAddressLocality($data['data']['origin']['town']);
                } else {
                    $departureAddress->setAddressLocality(self::NC);
                }

                if (isset($data['data']['origin']['name'])) {
                    $departureAddress->setStreetAddress($data['data']['origin']['name']);
                } else {
                    $departureAddress->setStreetAddress(self::NC);
                }

                if (isset($data['data']['origin']['position'], $data['data']['origin']['position']['latitude'])) {
                    $departureAddress->setLatitude($data['data']['origin']['position']['latitude']);
                }
                if (isset($data['data']['origin']['position'], $data['data']['origin']['position']['longitude'])) {
                    $departureAddress->setLongitude($data['data']['origin']['position']['longitude']);
                }
                $departure->setAddress($departureAddress);

                if (isset($data['data']['origin']['description']) && '' != $data['data']['origin']['description']) {
                    $departure->setName($data['data']['origin']['description']);
                } else {
                    if (isset($data['data']['origin']['name']) && '' != $data['data']['origin']['name']) {
                        $departure->setName($data['data']['origin']['name']);
                    } else {
                        $departure->setName(self::NC);
                    }
                }

                $leg->setPTDeparture($departure);
            }
            if (isset($data['data']['destination'])) {
                $arrival = new PTArrival(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
                if ($data['data']['arrivalDate']) {
                    $arrival->setDate(new \DateTime($data['data']['arrivalDate']));
                }

                $arrivalAddress = new Address();
                $arrivalAddress->setId(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
                $arrivalAddress->setAddressCountry(self::COUNTRY);

                if (isset($data['data']['destination']['town'])) {
                    $arrivalAddress->setAddressLocality($data['data']['destination']['town']);
                } else {
                    $arrivalAddress->setAddressLocality(self::NC);
                }

                if (isset($data['data']['destination']['name'])) {
                    $arrivalAddress->setStreetAddress($data['data']['destination']['name']);
                } else {
                    $arrivalAddress->setStreetAddress(self::NC);
                }

                if (isset($data['data']['destination']['position'], $data['data']['destination']['position']['latitude'])) {
                    $arrivalAddress->setLatitude($data['data']['destination']['position']['latitude']);
                }
                if (isset($data['data']['destination']['position'], $data['data']['destination']['position']['longitude'])) {
                    $arrivalAddress->setLongitude($data['data']['destination']['position']['longitude']);
                }
                $arrival->setAddress($arrivalAddress);

                if (isset($data['data']['destination']['description']) && '' != $data['data']['destination']['description']) {
                    $arrival->setName($data['data']['destination']['description']);
                } else {
                    if (isset($data['data']['destination']['name']) && '' != $data['data']['destination']['name']) {
                        $arrival->setName($data['data']['destination']['name']);
                    } else {
                        $arrival->setName(self::NC);
                    }
                }

                $leg->setPTArrival($arrival);
            }
            if (isset($data['data']['directionName']) && '' !== $data['data']['directionName']) {
                $ptline = new PTLine(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
                $ptline->setTravelMode($leg->getTravelMode());
                if (isset($data['data']['lineShortName'])) {
                    $ptline->setName($data['data']['lineShortName']);
                }
                if (isset($data['data']['networkId'])) {
                    $ptline->setNumber($data['data']['networkId']);
                }
                if (isset($data['data']['directionName'])) {
                    $leg->setDirection($data['data']['directionName']);
                }

                $ptcompany = new PTCompany(1); // we have to set an id as it's mandatory when using a custom data provider (see https://api-platform.com/docs/core/data-providers)
                $ptcompany->setName(self::NC);
                $ptline->setPTCompany($ptcompany);

                $leg->setPTLine($ptline);
            }
        }

        return $leg;
    }

    /**
     * Convert a Duration type hh:ii:ss in seconds.
     *
     * @return int
     */
    private function convertToSeconds(string $duration)
    {
        $durationTab = explode(':', $duration);
        $durationInSeconds = 0;

        if (isset($durationTab[0])) {
            $durationInSeconds += (int) ($durationTab[0]) * 3600;
        }
        if (isset($durationTab[1])) {
            $durationInSeconds += (int) ($durationTab[1]) * 60;
        }
        if (isset($durationTab[2])) {
            $durationInSeconds += (int) ($durationTab[2]);
        }

        return $durationInSeconds;
    }
}