Add final stop info to track

This commit is contained in:
Kacper Donat 2020-02-06 20:09:06 +01:00
parent f3fc6cb071
commit 38e6ae52e1
13 changed files with 203 additions and 66 deletions

View File

@ -22,11 +22,14 @@
</div>
<ul class="stop-group__stops list-underlined">
<li v-for="stop in group" :key="stop.id" class="d-flex">
<button @click="select(stop, $event)" class="btn btn-action align-self-start">
<tooltip>dodaj przystanek</tooltip>
<fa :icon="['fal', 'check']" />
</button>
<picker-stop :stop="stop" class="flex-grow-1"></picker-stop>
<picker-stop :stop="stop" class="flex-grow-1">
<template v-slot:primary-action>
<button @click="select(stop, $event)" class="btn btn-action align-self-start">
<tooltip>dodaj przystanek</tooltip>
<fa :icon="['fal', 'check']" />
</button>
</template>
</picker-stop>
</li>
</ul>
</div>

View File

@ -1,19 +1,27 @@
<div class="d-flex flex-wrap">
<stop :stop="stop" />
<div class="finder__stop">
<div class="d-flex">
<slot name="primary-action" />
<div style="overflow: hidden">
<stop :stop="stop" />
<!-- <div style="white-space: nowrap">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Earum, ut!</div>-->
<ul class="stop__destinations">
<li class="stop__destination" v-for="destination in stop.destinations" :key="destination.id"><stop :stop="destination"/></li>
</ul>
</div>
<div class="stop__actions flex-space-left">
<slot name="actions">
<button class="btn btn-action" ref="action-info" @click="details = !details">
<tooltip>dodatkowe informacje</tooltip>
<fa :icon="['fal', details ? 'chevron-circle-up' : 'info-circle']"/>
</button>
<div class="stop__actions flex-space-left">
<slot name="actions">
<button class="btn btn-action" ref="action-info" @click="details = !details">
<tooltip>dodatkowe informacje</tooltip>
<fa :icon="['fal', details ? 'chevron-circle-up' : 'info-circle']"/>
</button>
<button class="btn btn-action" ref="action-map" v-hover:map>
<fa :icon="['fal', 'map-marker-alt']"/>
</button>
</slot>
<button class="btn btn-action" ref="action-map" v-hover:map>
<fa :icon="['fal', 'map-marker-alt']"/>
</button>
</slot>
</div>
</div>
<fold :visible="details" class="stop__details-fold" lazy>
<stop-details :stop="stop"/>
</fold>

View File

@ -11,6 +11,7 @@
font-size: $small-font-size;
overflow-x: hidden;
text-overflow: ellipsis;
max-width: 100%;
&:last-child {
margin-bottom: 0;

View File

@ -5,7 +5,7 @@
}
.stop__name {
flex: 1 0;
//flex: 1 0;
line-height: 1.1;
margin-right: .5em;
}
@ -46,3 +46,15 @@
}
}
}
.stop__destinations {
@extend .favourite__stops;
}
.stop__destination {
@extend .favourite__stop;
}
.finder__stop {
max-width: 100%;
}

View File

@ -8,6 +8,7 @@ export interface Stop {
};
onDemand?: boolean;
variant?: string;
destinations?: Stop[];
}
export type StopGroup = Stop[];

View File

@ -4,32 +4,42 @@ namespace App\Entity;
use App\Model\Fillable;
use App\Model\FillTrait;
use App\Model\Referable;
use App\Model\ReferableTrait;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table("track_stop")
* @ORM\Table("track_stop", uniqueConstraints={
* @ORM\UniqueConstraint(name="stop_in_track_idx", columns={"stop_id", "track_id", "sequence"})
* })
*/
class StopInTrack implements Fillable
class StopInTrack implements Fillable, Referable
{
use FillTrait;
use FillTrait, ReferableEntityTrait;
/**
* Identifier for stop coming from provider
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=StopEntity::class, fetch="EAGER")
* @ORM\Id
*/
private $stop;
/**
* @ORM\ManyToOne(targetEntity=TrackEntity::class, fetch="EAGER", inversedBy="stopsInTrack")
* @ORM\Id
*/
private $track;
/**
* Order in track
* @var int
* @ORM\Id
* @ORM\Column(name="sequence", type="integer")
*/
private $order;

View File

@ -4,6 +4,7 @@ namespace App\Entity;
use App\Model\Fillable;
use App\Model\FillTrait;
use App\Service\IterableUtils;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
@ -50,6 +51,12 @@ class TrackEntity implements Entity, Fillable
*/
private $stopsInTrack;
/**
* Final stop in this track.
* @var StopInTrack
* @ORM\OneToOne(targetEntity=StopInTrack::class, fetch="LAZY")
*/
private $final;
/**
* Track constructor.
@ -98,15 +105,17 @@ class TrackEntity implements Entity, Fillable
}
/**
* @param Collection $stopsInTrack
* @param iterable $stopsInTrack
*/
public function setStopsInTrack(array $stopsInTrack): void
public function setStopsInTrack(iterable $stopsInTrack): void
{
$this->stopsInTrack = new ArrayCollection($stopsInTrack);
$this->stopsInTrack = IterableUtils::toArrayCollection($stopsInTrack);
$this->final = $this->stopsInTrack->last();
}
public function getFinal(): StopInTrack
{
return $this->getStopsInTrack()->last();
return $this->final;
}
}

View File

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20200206183956 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'sqlite', 'Migration can only be executed safely on \'sqlite\'.');
$this->addSql('CREATE TEMPORARY TABLE __temp__track AS SELECT id, line_id, provider_id, variant, description FROM track');
$this->addSql('DROP TABLE track');
$this->addSql('CREATE TABLE track (id VARCHAR(255) NOT NULL COLLATE BINARY, line_id VARCHAR(255) DEFAULT NULL COLLATE BINARY, provider_id VARCHAR(255) DEFAULT NULL COLLATE BINARY, variant VARCHAR(16) DEFAULT NULL COLLATE BINARY, description VARCHAR(256) DEFAULT NULL COLLATE BINARY, final_id INTEGER DEFAULT NULL, PRIMARY KEY(id))');
$this->addSql('INSERT INTO track (id, line_id, provider_id, variant, description) SELECT id, line_id, provider_id, variant, description FROM __temp__track');
$this->addSql('DROP TABLE __temp__track');
$this->addSql('CREATE UNIQUE INDEX UNIQ_D6E3F8A613D41B2D ON track (final_id)');
$this->addSql('CREATE TEMPORARY TABLE __temp__track_stop AS SELECT stop_id, track_id, sequence FROM track_stop');
$this->addSql('DROP TABLE track_stop');
$this->addSql('CREATE TABLE track_stop (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, sequence INTEGER NOT NULL, stop_id VARCHAR(255) DEFAULT NULL, track_id VARCHAR(255) DEFAULT NULL)');
$this->addSql('INSERT INTO track_stop (stop_id, track_id, sequence) SELECT stop_id, track_id, sequence FROM __temp__track_stop');
$this->addSql('DROP TABLE __temp__track_stop');
$this->addSql('CREATE UNIQUE INDEX stop_in_track_idx ON track_stop (stop_id, track_id, sequence)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'sqlite', 'Migration can only be executed safely on \'sqlite\'.');
$this->addSql('DROP INDEX UNIQ_D6E3F8A613D41B2D');
$this->addSql('CREATE TEMPORARY TABLE __temp__track AS SELECT id, variant, description, line_id, provider_id FROM track');
$this->addSql('DROP TABLE track');
$this->addSql('CREATE TABLE track (id VARCHAR(255) NOT NULL, variant VARCHAR(16) DEFAULT NULL, description VARCHAR(256) DEFAULT NULL, line_id VARCHAR(255) DEFAULT NULL, provider_id VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))');
$this->addSql('INSERT INTO track (id, variant, description, line_id, provider_id) SELECT id, variant, description, line_id, provider_id FROM __temp__track');
$this->addSql('DROP TABLE __temp__track');
$this->addSql('DROP INDEX stop_in_track_idx');
$this->addSql('CREATE TEMPORARY TABLE __temp__track_stop AS SELECT sequence, stop_id, track_id FROM track_stop');
$this->addSql('DROP TABLE track_stop');
$this->addSql('CREATE TABLE track_stop (sequence INTEGER NOT NULL, stop_id VARCHAR(255) NOT NULL COLLATE BINARY, track_id VARCHAR(255) NOT NULL COLLATE BINARY, PRIMARY KEY(stop_id, track_id, sequence))');
$this->addSql('INSERT INTO track_stop (sequence, stop_id, track_id) SELECT sequence, stop_id, track_id FROM __temp__track_stop');
$this->addSql('DROP TABLE __temp__track_stop');
}
}

View File

@ -2,6 +2,7 @@
namespace App\Model;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
trait ReferableTrait
@ -38,4 +39,4 @@ trait ReferableTrait
return $result;
}
}
}

View File

@ -3,10 +3,8 @@
namespace App\Provider\Database;
use App\Entity\StopEntity;
use App\Entity\StopInTrack;
use App\Entity\TrackEntity;
use App\Model\Stop;
use App\Model\Track;
use App\Provider\StopRepository;
use Tightenco\Collect\Support\Collection;
use Kadet\Functional as f;
@ -23,7 +21,7 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
public function getById($id): ?Stop
{
$id = $this->id->generate($this->provider, $id);
$id = $this->id->generate($this->provider, $id);
$stop = $this->em->getRepository(StopEntity::class)->find($id);
return $this->convert($stop);
@ -31,7 +29,7 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
public function getManyById($ids): Collection
{
$ids = collect($ids)->map(f\apply(f\ref([$this->id, 'generate']), $this->provider));
$ids = collect($ids)->map(f\apply(f\ref([$this->id, 'generate']), $this->provider));
$stops = $this->em->getRepository(StopEntity::class)->findBy(['id' => $ids->all()]);
return collect($stops)->map(f\ref([$this, 'convert']));
@ -48,12 +46,12 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
$stops = collect($query->execute([':name' => "%$name%"]));
$destinations = collect($this->em->createQueryBuilder()
->select('t', 'ts', 'ts2', 's')
->select('t', 'f', 'fs', 'ts')
->from(TrackEntity::class, 't')
->join('t.stopsInTrack', 'ts')
->join('t.stopsInTrack', 'ts2')
->join('ts2.stop', 's')
->where('ts.stop IN (:stops)')
->join('t.final', 'f')
->join('f.stop', 'fs')
->getQuery()
->execute(['stops' => $stops->map(t\property('id'))->all()]))
->reduce(function ($grouped, TrackEntity $track) {
@ -67,8 +65,7 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
return $tracks->map(function (TrackEntity $track) {
return $this->convert($track->getFinal()->getStop());
})->unique()->values();
})
;
});
return collect($stops)->map(f\ref([$this, 'convert']))->each(function (Stop $stop) use ($destinations) {
$stop->setDestinations($destinations[$this->id->generate($this->provider, $stop->getId())]);

View File

@ -12,7 +12,6 @@ use App\Entity\TripEntity;
use App\Entity\TripStopEntity;
use App\Event\DataUpdateEvent;
use App\Model\Line as LineModel;
use App\Model\Location;
use App\Service\DataUpdater;
use App\Service\IdUtils;
use Carbon\Carbon;
@ -20,13 +19,10 @@ use Cerbero\JsonObjects\JsonObjects;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Tightenco\Collect\Support\Collection;
use function Cerbero\JsonObjects\JsonObjects;
use function Kadet\Functional\ref;
use function Kadet\Functional\Transforms\property;
class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
{
@ -45,6 +41,8 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
private $logger;
private $provider;
private $stopBlacklist = [];
/**
* ZtmGdanskDataUpdateSubscriber constructor.
*
@ -147,6 +145,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
private function getStops(ProviderEntity $provider, DataUpdateEvent $event)
{
$this->stopBlacklist = [];
$output = $event->getOutput();
$output->write('Obtaining stops from ZTM Gdańsk... ');
@ -157,9 +156,12 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
$this->logger->debug(sprintf("Saving %d stops tracks from ZTM Gdańsk.", count($stops)));
return collect($stops)
->filter(function ($stop) {
return $stop['nonpassenger'] !== 1
&& $stop['virtual'] !== 1
&& $stop['depot'] !== 1;
if ($stop['nonpassenger'] === 1 || $stop['virtual'] === 1 || $stop['depot'] === 1) {
$this->stopBlacklist[] = $stop['stopId'];
return false;
}
return true;
})
->map(function ($stop) use ($provider) {
$name = trim($stop['stopName'] ?? $stop['stopDesc']);
@ -178,7 +180,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
;
}
public function getTracks(ProviderEntity $provider, DataUpdateEvent $event, $stops = [])
public function getTracks(ProviderEntity $provider, DataUpdateEvent $event, Collection $stops = null)
{
$output = $event->getOutput();
@ -209,19 +211,24 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
'provider' => $provider,
]);
$stops = $stops->get($track['id'])->map(function ($stop) use ($entity, $provider) {
return StopInTrack::createFromArray([
'stop' => $this->em->getReference(
StopEntity::class,
$this->ids->generate($provider, $stop['stopId'])
),
'track' => $entity,
// HACK! Gdynia has 0 based sequence
'order' => $stop['stopSequence'] + (int)($stop['stopId'] > 30000),
]);
});
$stops = $stops->get($track['id'])
->filter(function ($stop) {
return !in_array($stop['stopId'], $this->stopBlacklist);
})
->map(function ($stop) use ($entity, $provider) {
return StopInTrack::createFromArray([
'stop' => $this->em->getReference(
StopEntity::class,
$this->ids->generate($provider, $stop['stopId'])
),
'track' => $entity,
// HACK! Gdynia has 0 based sequence
'order' => $stop['stopSequence'] + (int)($stop['stopId'] > 30000),
]);
})
->sortBy(property("order"));
$entity->setStopsInTrack($stops->all());
$entity->setStopsInTrack($stops);
return $entity;
});

View File

@ -0,0 +1,28 @@
<?php
namespace App\Service;
use Doctrine\Common\Collections\ArrayCollection;
use Tightenco\Collect\Support\Collection;
final class IterableUtils
{
public static function toArray(iterable $iterable): array
{
if (is_array($iterable)) {
return $iterable;
}
return iterator_to_array($iterable);
}
public static function toArrayCollection(iterable $iterable): ArrayCollection
{
return new ArrayCollection(static::toArray($iterable));
}
public static function toCollection(iterable $iterable): Collection
{
return collect($iterable);
}
}

View File

@ -101,11 +101,14 @@
<ul class="picker__stops list-underlined">
<li v-for="stop in stops" :key="stop.id" class="d-flex align-items-center">
<button @click="remove(stop)" class="btn btn-action align-self-start">
<tooltip>usuń przystanek</tooltip>
<fa :icon="['fal', 'times']"></fa>
</button>
<picker-stop :stop="stop" class="flex-grow-1"></picker-stop>
<picker-stop :stop="stop" class="flex-grow-1">
<template v-slot:primary-action>
<button @click="remove(stop)" class="btn btn-action align-self-start">
<tooltip>usuń przystanek</tooltip>
<fa :icon="['fal', 'times']"></fa>
</button>
</template>
</picker-stop>
</li>
</ul>
@ -143,7 +146,7 @@
</button>
</template>
</header>
<div class="transition-box" style="overflow: hidden;">
<div class="transition-box">
<transition name="fade">
<stop-finder @select="add" :blacklist="stops" v-if="visibility.picker === 'search'"></stop-finder>
<favourites v-else-if="visibility.picker === 'favourites'"></favourites>