Covivo/mobicoop

View on GitHub
api/src/ExternalJourney/Admin/Service/ExternalJourneyManager.php

Summary

Maintainability
D
1 day
Test Coverage
<?php

/**
 * Copyright (c) 2021, 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\ExternalJourney\Admin\Service;

use App\Carpool\Entity\Criteria;
use App\ExternalJourney\Entity\ExternalJourneyProvider;
use App\Geography\Entity\Address;
use App\Rdex\Entity\RdexJourney;
use GuzzleHttp\Client;
use Symfony\Component\HttpFoundation\Request;

/**
 * External journey service in administration context.
 *
 * @author Maxime Bardot <maxime.bardot@mobicoop.org>
 */
class ExternalJourneyManager
{
    private const EXTERNAL_JOURNEY_HASH = 'sha256';         // hash algorithm

    private $operator;
    private $clients;
    private $providers;

    private $data;
    private $currentProvider;

    public function __construct(?array $operator = [], ?array $clients = [], ?array $providers = [])
    {
        $this->operator = $operator;
        $this->clients = $clients;
        foreach ($providers as $providerName => $details) {
            $provider = new ExternalJourneyProvider();
            $provider->setName($providerName);
            $provider->setUrl($details['url']);
            $provider->setResource($details['resource']);
            $provider->setApiKey($details['api_key']);
            $provider->setPrivateKey($details['private_key']);
            $this->providers[] = $provider;
        }
    }

    public function getOperator()
    {
        return $this->operator;
    }

    public function getClients()
    {
        return $this->clients;
    }

    public function getProviders()
    {
        return $this->providers;
    }

    public function getData(): string
    {
        return $this->data;
    }

    public function setData(string $data)
    {
        $this->data = $data;
    }

    public function getCurrentProvider(): ExternalJourneyProvider
    {
        return $this->currentProvider;
    }

    public function setCurrentProvider(ExternalJourneyProvider $currentProvider)
    {
        $this->currentProvider = $currentProvider;
    }

    public function getExternalJourneys(Request $request, array $params): array
    {
        $this->params = $params;

        // initialize client API for any request
        $client = new Client();
        // we collect search parameters here
        $providerName = $request->get('provider');
        $driver = $request->get('driver');
        $passenger = $request->get('passenger');
        $fromLatitude = $request->get('from_latitude');
        $fromLongitude = $request->get('from_longitude');
        $toLatitude = $request->get('to_latitude');
        $toLongitude = $request->get('to_longitude');
        $date = $request->get('date');
        $outwardMinDate = $request->get('outward_mindate');
        $outwardMaxDate = $request->get('outward_maxdate');
        $frequency = $request->get('frequency');
        $rawJson = $request->get('rawJson');
        $days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];

        // then we set these parameters
        $searchParameters = [
            'driver' => [
                'state' => $driver,
            ],
            'passenger' => [
                'state' => $passenger,
            ],
            'from' => [
                'latitude' => $fromLatitude,
                'longitude' => $fromLongitude,
            ],
            'to' => [
                'latitude' => $toLatitude,
                'longitude' => $toLongitude,
            ],
        ];

        if ('' !== $frequency && ('regular' == $frequency || 'punctual' == $frequency)) {
            $searchParameters['frequency'] = $frequency;
        }

        if (!is_null($outwardMinDate) && '' !== $outwardMinDate) {
            $searchParameters['outward']['mindate'] = $outwardMinDate;
        }
        if (!is_null($outwardMaxDate) && '' !== $outwardMaxDate) {
            $searchParameters['outward']['maxdate'] = $outwardMaxDate;
        }
        // Override
        if (!is_null($date) && '' !== $date) {
            $searchParameters['outward']['mindate'] = $date;
            $searchParameters['outward']['maxdate'] = $date;
        }

        // Days treatment for regular journeys
        // which days
        foreach ($days as $day) {
            $currentday = $request->get('days_'.$day);
            if ('' !== $currentday) {
                $searchParameters['days'][$day] = $currentday;
            } else {
                $searchParameters['days'][$day] = 0;
            }
        }

        // mintime and maxtime for days
        foreach ($days as $day) {
            $mintime = $request->get($day.'_mintime');
            if ('' !== $mintime) {
                $searchParameters['outward'][$day]['mintime'] = $mintime;
            }
            $maxtime = $request->get($day.'_maxtime');
            if ('' !== $maxtime) {
                $searchParameters['outward'][$day]['maxtime'] = $maxtime;
            }
        }

        $aggregatedResults = [];
        $providers = $this->getProviders();

        // If a provider is given in parameters, we take only this one
        // Otherwise, we use all providers
        if ('' !== $providerName) {
            foreach ($providers as $provider) {
                if ($provider->getName() == $providerName) {
                    $providers = [$provider];
                }
            }
        }

        // @todo error management (api not responding, bad parameters...)
        foreach ($providers as $provider) {
            $this->setCurrentProvider($provider);

            $query = [
                'timestamp' => time(),
                'apikey' => $provider->getApiKey(),
                'p' => $searchParameters,
            ];
            // construct the requested url
            $url = $provider->getUrl().'/'.$provider->getResource().'?'.http_build_query($query);
            $signature = hash_hmac(self::EXTERNAL_JOURNEY_HASH, $url, $provider->getPrivateKey());
            $signedUrl = $url.'&signature='.$signature;
            // request url
            $data = $client->request('GET', $signedUrl);
            $this->setData($data->getBody()->getContents());

            if ('' !== $this->data) {
                if (1 == $rawJson) {
                    // rawJson flag set. We return RDEX format.
                    $aggregatedResults = json_decode($this->getData(), true);
                } else {
                    // No rawJson flag set or set to 0. We return an array
                    foreach ($this->createCarpoolFromRDEX() as $currentResult) {
                        $aggregatedResults[] = $currentResult;
                    }
                }
            }
        }

        return $aggregatedResults;
    }

    private function createCarpoolFromRDEX(): array
    {
        $results = [];
        $journeys = json_decode($this->getData(), true);
        foreach ($journeys as $journey) {
            $currentJourney = $journey['journeys'];

            if (isset($currentJourney['driver']['uuid'])) {
                $currentJourneyCarpooler = $currentJourney['driver'];
            } else {
                $currentJourneyCarpooler = $currentJourney['passenger'];
            }

            // We check all times and if they are all the same, we set the time of the result
            $days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
            $currentTime = '';
            $returnTime = true;
            $time = '';
            foreach ($days as $day) {
                // Only for checked days
                if ($currentJourney['days'][$day] && !is_null($currentJourney['outward'][$day]['mintime'])) {
                    $time = $this->middleHour($currentJourney['outward'][$day]['mintime'], $currentJourney['outward'][$day]['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']);

                    // Only the first time to init the reference
                    if ('' === $currentTime) {
                        $currentTime = $time;
                    }

                    if ($currentTime !== $time) {
                        $returnTime = false;

                        break;
                    }
                }
            }

            $carpool = [
                'external' => true,
                'externalProvider' => $this->getCurrentProvider()->getName(),
                'externalOrigin' => $currentJourney['origin'],
                'journeyId' => $currentJourney['uuid'],
                'matchingId' => null,
                'carpoolerId' => $currentJourneyCarpooler['uuid'],
                'carpoolerGivenName' => $currentJourneyCarpooler['alias'],
                'carpoolerFamilyName' => null,
                'carpoolerAvatar' => $currentJourneyCarpooler['image'],
                'frequency' => ('regular' === $currentJourney['frequency']) ? Criteria::FREQUENCY_REGULAR : Criteria::FREQUENCY_PUNCTUAL,
                'type' => (RdexJourney::TYPE_ONE_WAY == $currentJourney['type']) ? 'oneway' : 'roundtrip',
                'passenger' => (!is_null($currentJourney['passenger'])) ? 1 : 0,
                'driver' => (!is_null($currentJourney['driver'])) ? 1 : 0,
                'solidaryExclusive' => null,
                'fromDate' => $currentJourney['outward']['mindate'],
                'fromTime' => ('' !== $time) ? $time : null,
                'toDate' => null,
                'carpoolerFromDate' => null,
                'carpoolerFromTime' => null,
                'carpoolerToDate' => null,
            ];

            if (Criteria::FREQUENCY_REGULAR == $carpool['frequency']) {
                $carpool['carpoolerSchedule'] = [];
                $carpool['schedule'] = [
                    'mon' => 1 == $currentJourney['days']['monday'] && !is_null($currentJourney['outward']['monday']['mintime']) ? $this->middleHour($currentJourney['outward']['monday']['mintime'], $currentJourney['outward']['monday']['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']) : false,
                    'tue' => 1 == $currentJourney['days']['tuesday'] && !is_null($currentJourney['outward']['tuesday']['mintime']) ? $this->middleHour($currentJourney['outward']['tuesday']['mintime'], $currentJourney['outward']['tuesday']['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']) : false,
                    'wed' => 1 == $currentJourney['days']['wednesday'] && !is_null($currentJourney['outward']['wednesday']['mintime']) ? $this->middleHour($currentJourney['outward']['wednesday']['mintime'], $currentJourney['outward']['wednesday']['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']) : false,
                    'thu' => 1 == $currentJourney['days']['thursday'] && !is_null($currentJourney['outward']['thursday']['mintime']) ? $this->middleHour($currentJourney['outward']['thursday']['mintime'], $currentJourney['outward']['thursday']['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']) : false,
                    'fri' => 1 == $currentJourney['days']['friday'] && !is_null($currentJourney['outward']['friday']['mintime']) ? $this->middleHour($currentJourney['outward']['friday']['mintime'], $currentJourney['outward']['friday']['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']) : false,
                    'sat' => 1 == $currentJourney['days']['saturday'] && !is_null($currentJourney['outward']['saturday']['mintime']) ? $this->middleHour($currentJourney['outward']['saturday']['mintime'], $currentJourney['outward']['saturday']['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']) : false,
                    'sun' => 1 == $currentJourney['days']['sunday'] && !is_null($currentJourney['outward']['sunday']['mintime']) ? $this->middleHour($currentJourney['outward']['sunday']['mintime'], $currentJourney['outward']['sunday']['maxtime'], $currentJourney['outward']['mindate'], $currentJourney['outward']['mindate']) : false,
                ];
            }

            // Origin
            $origin = new Address();
            $origin->setLatitude($currentJourney['from']['latitude']);
            $origin->setLongitude($currentJourney['from']['longitude']);
            $origin->setStreetAddress($currentJourney['from']['address']);
            $origin->setPostalCode(isset($currentJourney['from']['postalcode']) ? $currentJourney['from']['postalcode'] : null);
            $origin->setAddressLocality($currentJourney['from']['city']);
            $origin->setAddressCountry($currentJourney['from']['country']);
            $carpool['origin'] = $origin->jsonSerialize();

            // Destination
            $destination = new Address();
            $destination->setLatitude($currentJourney['to']['latitude']);
            $destination->setLongitude($currentJourney['to']['longitude']);
            $destination->setStreetAddress($currentJourney['to']['address']);
            $destination->setPostalCode(isset($currentJourney['to']['postalcode']) ? $currentJourney['to']['postalcode'] : null);
            $destination->setAddressLocality($currentJourney['to']['city']);
            $destination->setAddressCountry($currentJourney['to']['country']);
            $carpool['destination'] = $destination->jsonSerialize();

            $carpool['detourDuration'] = $currentJourney['duration'];
            $carpool['detourDistance'] = $currentJourney['distance'];

            $results[] = $carpool;
        }

        return $results;
    }

    private function middleHour($heureMin, $heureMax, $dateMin, $dateMax)
    {
        $min = \DateTime::createFromFormat('Y-m-d H:i:s', $dateMin.' '.$heureMin, new \DateTimeZone('UTC'));
        $mintime = $min->getTimestamp();
        $max = \DateTime::createFromFormat('Y-m-d H:i:s', $dateMax.' '.$heureMax, new \DateTimeZone('UTC'));
        $maxtime = $max->getTimestamp();
        $marge = ($maxtime - $mintime) / 2;
        $middleHour = $mintime + $marge;
        $returnHour = new \DateTime();
        $returnHour->setTimestamp($middleHour);

        return $returnHour;
    }
}