222 lines
7.3 KiB
PHP
222 lines
7.3 KiB
PHP
<?php
|
|
|
|
namespace App\Provider\ZtmGdansk;
|
|
|
|
use App\Model\Departure;
|
|
use App\Model\Line;
|
|
use App\Model\ScheduledStop;
|
|
use App\Model\Stop;
|
|
use App\Model\Vehicle;
|
|
use App\Modifier\FieldFilter;
|
|
use App\Modifier\IdFilter;
|
|
use App\Modifier\Limit;
|
|
use App\Modifier\Modifier;
|
|
use App\Modifier\RelatedFilter;
|
|
use App\Modifier\With;
|
|
use App\Provider\Database\GenericScheduleRepository;
|
|
use App\Provider\DepartureRepository;
|
|
use App\Provider\LineRepository;
|
|
use App\Provider\ScheduleRepository;
|
|
use App\Service\IterableUtils;
|
|
use App\Service\ModifierUtils;
|
|
use App\Service\Proxy\ReferenceFactory;
|
|
use Carbon\Carbon;
|
|
use JMS\Serializer\Tests\Fixtures\Discriminator\Car;
|
|
use Tightenco\Collect\Support\Collection;
|
|
use Kadet\Functional\Transforms as t;
|
|
use function App\Functions\setup;
|
|
use function Kadet\Functional\ref;
|
|
|
|
class ZtmGdanskDepartureRepository implements DepartureRepository
|
|
{
|
|
const ESTIMATES_URL = 'http://ckan2.multimediagdansk.pl/delays';
|
|
|
|
/** @var LineRepository */
|
|
private $lines;
|
|
|
|
/** @var ReferenceFactory */
|
|
private $reference;
|
|
|
|
/** @var ScheduleRepository */
|
|
private $schedule;
|
|
|
|
/**
|
|
* @param LineRepository $lines
|
|
*/
|
|
public function __construct(LineRepository $lines, ScheduleRepository $schedule, ReferenceFactory $reference)
|
|
{
|
|
$this->lines = $lines;
|
|
$this->reference = $reference;
|
|
$this->schedule = $schedule;
|
|
}
|
|
|
|
public function current(iterable $stops, Modifier ...$modifiers)
|
|
{
|
|
$real = IterableUtils::toCollection($stops)
|
|
->flatMap(ref([$this, 'getRealDepartures']))
|
|
->sortBy(t\property('estimated'))
|
|
;
|
|
|
|
$now = Carbon::now()->second(0);
|
|
$first = $real->map(t\getter('scheduled'))->min() ?? $now;
|
|
$scheduled = $this->getScheduledDepartures($stops, $first, ...$this->extractModifiers($modifiers));
|
|
|
|
$result = $this->pair($scheduled, $real)->filter(function (Departure $departure) use ($now) {
|
|
return $departure->getDeparture() > $now;
|
|
});
|
|
|
|
return $this->processResultWithModifiers($result, $modifiers);
|
|
}
|
|
|
|
private function getRealDepartures(Stop $stop)
|
|
{
|
|
try {
|
|
$estimates = file_get_contents(static::ESTIMATES_URL . "?stopId=" . $stop->getId());
|
|
$estimates = json_decode($estimates, true)['delay'];
|
|
} catch (\Error $e) {
|
|
return collect();
|
|
}
|
|
|
|
$estimates = collect($estimates);
|
|
|
|
$lines = $estimates->map(function ($delay) {
|
|
return $delay['routeId'];
|
|
})->unique();
|
|
|
|
$lines = $this->lines->all(new IdFilter($lines))->keyBy(t\property('id'));
|
|
|
|
return collect($estimates)->map(function ($delay) use ($stop, $lines) {
|
|
$scheduled = (new Carbon($delay['theoreticalTime'], 'Europe/Warsaw'))->tz('UTC');
|
|
$estimated = (clone $scheduled)->addSeconds($delay['delayInSeconds']);
|
|
|
|
return Departure::createFromArray([
|
|
'key' => sprintf('%s::%s', $delay['routeId'], $scheduled->format('H:i')),
|
|
'scheduled' => $scheduled,
|
|
'estimated' => $estimated,
|
|
'stop' => $stop,
|
|
'display' => trim($delay['headsign']),
|
|
'vehicle' => $this->reference->get(Vehicle::class, $delay['vehicleCode']),
|
|
'line' => $lines->get($delay['routeId']) ?: Line::createFromArray([
|
|
'symbol' => $delay['routeId'],
|
|
'type' => Line::TYPE_UNKNOWN,
|
|
]),
|
|
]);
|
|
})->values();
|
|
}
|
|
|
|
private function getScheduledDepartures($stop, Carbon $time, Modifier ...$modifiers)
|
|
{
|
|
return $this->schedule->all(
|
|
new RelatedFilter($stop, Stop::class),
|
|
new FieldFilter('departure', $time, '>='),
|
|
new With('track'),
|
|
new With('destination'),
|
|
...$modifiers
|
|
);
|
|
}
|
|
|
|
private function pair(Collection $schedule, Collection $real)
|
|
{
|
|
$key = function ($departure) {
|
|
if ($departure instanceof Departure) {
|
|
return sprintf(
|
|
"%s::%s",
|
|
$departure->getLine()->getId(),
|
|
$departure->getScheduled()->format("H:i")
|
|
);
|
|
} elseif ($departure instanceof ScheduledStop) {
|
|
return sprintf(
|
|
"%s::%s",
|
|
$departure->getTrack()->getLine()->getId(),
|
|
$departure->getDeparture()->format("H:i")
|
|
);
|
|
} else {
|
|
throw new \Exception();
|
|
}
|
|
};
|
|
|
|
$schedule = $schedule->keyBy($key)->all();
|
|
$real = $real->keyBy($key);
|
|
|
|
return $real->map(function (Departure $real, $key) use (&$schedule) {
|
|
$scheduled = null;
|
|
|
|
if (array_key_exists($key, $schedule)) {
|
|
$scheduled = $schedule[$key];
|
|
unset($schedule[$key]);
|
|
}
|
|
|
|
return [
|
|
'estimated' => $real,
|
|
'scheduled' => $scheduled,
|
|
];
|
|
})->merge(collect($schedule)->map(function (ScheduledStop $scheduled) {
|
|
return [
|
|
'estimated' => null,
|
|
'scheduled' => $scheduled,
|
|
];
|
|
}))->map(function ($pair) {
|
|
return $this->merge($pair['estimated'], $pair['scheduled']);
|
|
})->sortBy(function (Departure $departure) {
|
|
$time = $departure->getEstimated() ?? $departure->getScheduled();
|
|
return $time->getTimestamp();
|
|
});
|
|
}
|
|
|
|
private function merge(?Departure $real, ?ScheduledStop $scheduled)
|
|
{
|
|
if (!$real) {
|
|
return $this->convertScheduledStopToDeparture($scheduled);
|
|
}
|
|
|
|
if (!$scheduled) {
|
|
return $real;
|
|
}
|
|
|
|
return setup(clone $real, function (Departure $departure) use ($scheduled, $real) {
|
|
$departure->setDisplay($real->getDisplay());
|
|
$departure->setTrack($scheduled->getTrack());
|
|
$departure->setTrip($scheduled->getTrip());
|
|
});
|
|
}
|
|
|
|
private function convertScheduledStopToDeparture(ScheduledStop $stop): Departure
|
|
{
|
|
return setup(new Departure(), function (Departure $converted) use ($stop) {
|
|
$converted->setDisplay($stop->getTrack()->getDestination()->getName());
|
|
$converted->setLine($stop->getTrack()->getLine());
|
|
$converted->setTrack($stop->getTrack());
|
|
$converted->setTrip($stop->getTrip());
|
|
$converted->setScheduled($stop->getDeparture());
|
|
$converted->setStop($stop->getStop());
|
|
});
|
|
}
|
|
|
|
private function extractModifiers(iterable $modifiers)
|
|
{
|
|
$result = [];
|
|
|
|
/** @var Limit $limit */
|
|
if ($limit = ModifierUtils::getOfType($modifiers, Limit::class)) {
|
|
$result[] = new Limit($limit->getOffset(), $limit->getCount() * 2);
|
|
} else {
|
|
$result[] = Limit::count(16);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function processResultWithModifiers(Collection $result, iterable $modifiers)
|
|
{
|
|
foreach ($modifiers as $modifier) {
|
|
switch (true) {
|
|
case $modifier instanceof Limit:
|
|
$result = $result->slice($modifier->getOffset(), $modifier->getCount());
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|