Skip to content

Commit

Permalink
Add support for multiple region per new mapper (osmbe#808)
Browse files Browse the repository at this point in the history
* Update entities

* Create migration

* Update RegionProvider.php

* Update NewMapperCommand.php

* Update controllers

* Update templates

* Apply fixes from StyleCI

Co-authored-by: StyleCI Bot <[email protected]>
  • Loading branch information
jbelien and StyleCIBot authored Nov 19, 2022
1 parent 50e8187 commit 1b62529
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 56 deletions.
41 changes: 41 additions & 0 deletions migrations/Version20221116194655.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20221116194655 extends AbstractMigration
{
public function getDescription(): string
{
return 'Migrate to multiple regions per user';
}

public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE mapper_region (mapper_id INTEGER NOT NULL, region_id VARCHAR(255) NOT NULL, PRIMARY KEY(mapper_id, region_id), CONSTRAINT FK_6E8B9023B9CA839A FOREIGN KEY (mapper_id) REFERENCES mapper (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_6E8B902398260155 FOREIGN KEY (region_id) REFERENCES region (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
$this->addSql('CREATE INDEX IDX_6E8B9023B9CA839A ON mapper_region (mapper_id)');
$this->addSql('CREATE INDEX IDX_6E8B902398260155 ON mapper_region (region_id)');
$this->addSql('CREATE TEMPORARY TABLE __temp__mapper AS SELECT id, region, display_name, account_created, changesets_count, status, image FROM mapper');
$this->addSql('DROP TABLE mapper');
$this->addSql('CREATE TABLE mapper (id INTEGER NOT NULL, display_name VARCHAR(255) NOT NULL, account_created DATETIME NOT NULL, changesets_count INTEGER NOT NULL, status VARCHAR(255) NOT NULL, image CLOB DEFAULT NULL, PRIMARY KEY(id))');
$this->addSql('INSERT INTO mapper (id, display_name, account_created, changesets_count, status, image) SELECT id, display_name, account_created, changesets_count, status, image FROM __temp__mapper');
$this->addSql('INSERT INTO mapper_region (mapper_id, region_id) SELECT id, region FROM __temp__mapper');
$this->addSql('DROP TABLE __temp__mapper');
}

public function down(Schema $schema): void
{
$this->addSql('CREATE TEMPORARY TABLE __temp__mapper_region AS SELECT mapper_id, region_id FROM mapper_region');
$this->addSql('DROP TABLE mapper_region');
$this->addSql('CREATE TEMPORARY TABLE __temp__mapper AS SELECT id, display_name, account_created, changesets_count, status, image FROM mapper');
$this->addSql('DROP TABLE mapper');
$this->addSql('CREATE TABLE mapper (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, display_name VARCHAR(255) NOT NULL, account_created DATETIME NOT NULL, changesets_count INTEGER NOT NULL, status VARCHAR(255) NOT NULL, image CLOB DEFAULT NULL, region VARCHAR(255) NOT NULL)');
$this->addSql('INSERT INTO mapper (id, display_name, account_created, changesets_count, status, image, region) SELECT m.id, m.display_name, m.account_created, m.changesets_count, m.status, m.image, mr.region_id FROM __temp__mapper m LEFT JOIN (SELECT mapper_id, region_id FROM __temp__mapper_region mr GROUP BY 1) mr ON m.id = mr.mapper_id');
$this->addSql('DROP TABLE __temp__mapper');
$this->addSql('DROP TABLE __temp__mapper_region');
}
}
12 changes: 11 additions & 1 deletion src/Command/NewMapperCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,18 @@ private function getUsers(string $key, array $ids, SymfonyStyle $io): array

/** @var Mapper[] */
$mappers = array_map(function (array $array) use ($key): Mapper {
$region = $this->regionsProvider->getEntity($key);

if (null === $region) {
$region = new Region();
$region->setId($key);
$region->setLastUpdate(new \DateTime('1970-01-01'));

$this->entityManager->persist($region);
}

$mapper = $this->mapperProvider->fromOSM($array);
$mapper->setRegion($key);
$mapper->addRegion($region);

return $mapper;
}, $usersArray['users']);
Expand Down
3 changes: 2 additions & 1 deletion src/Controller/API/RegionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public function index(string $continent, string $regionKey): Response
#[Route('/api/region/{continent}/{regionKey}/count.{_format}', name: 'api_region_count', format: 'json', requirements: ['_format' => 'json'])]
public function count(string $continent, string $regionKey): Response
{
$mappers = $this->mapperRepository->findBy(['region' => $regionKey]);
$region = $this->provider->getEntity($regionKey);
$mappers = null === $region ? [] : $region->getMappers()->toArray();

$count = [];
foreach ($mappers as $mapper) {
Expand Down
2 changes: 1 addition & 1 deletion src/Controller/App/HomeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function index(): Response

foreach ($regions as $continent => &$group) {
foreach ($group as $key => &$region) {
$region['lastUpdate'] = $this->provider->getLastUpdate($key);
$region['lastUpdate'] = $this->provider->getEntity($key)?->getLastUpdate();
$region['count'] = $this->provider->getPercentage($key);
}
}
Expand Down
32 changes: 16 additions & 16 deletions src/Controller/App/ListController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public function redirectToList(string $regionKey): Response
public function index(string $regionKey, ?string $continent, ?int $year = null, ?int $month = null): Response
{
$region = $this->provider->getRegion($continent, $regionKey);
$region['lastUpdate'] = $this->provider->getLastUpdate($regionKey);
$regionEntity = $this->provider->getEntity($regionKey);
$region['lastUpdate'] = null === $regionEntity ? null : $regionEntity->getLastUpdate();
$region['count'] = $this->provider->getPercentage($regionKey);

if (null === $year && null === $month) {
Expand All @@ -48,25 +49,24 @@ public function index(string $regionKey, ?string $continent, ?int $year = null,
return $this->redirectToRoute('app_list_full', ['continent' => $region['continent'], 'regionKey' => $region['key'], 'year' => $year, 'month' => $month]);
}

/** @var Mapper[] */
$mappers = $this->entityManager
->getRepository(Mapper::class)
->findBy(['region' => $regionKey]);
$mappers = null === $regionEntity ? [] : $regionEntity->getMappers()->toArray();

$firstChangetsetCreatedAt = array_map(fn (Mapper $mapper): ?\DateTimeImmutable => $mapper->getFirstChangeset()->getCreatedAt(), $mappers);
array_multisort($firstChangetsetCreatedAt, \SORT_DESC, $mappers);
if (\count($mappers) > 0) {
$firstChangetsetCreatedAt = array_map(fn (Mapper $mapper): ?\DateTimeImmutable => $mapper->getFirstChangeset()->getCreatedAt(), $mappers);
array_multisort($firstChangetsetCreatedAt, \SORT_DESC, $mappers);

$month = (new \DateTime())->setDate($year, $month, 1);
$month = (new \DateTime())->setDate($year, $month, 1);

$mappers = array_filter(
$mappers,
function (Mapper $mapper) use ($month): bool {
/** @var \DateTimeImmutable */
$createdAt = $mapper->getFirstChangeset()->getCreatedAt();
$mappers = array_filter(
$mappers,
function (Mapper $mapper) use ($month): bool {
/** @var \DateTimeImmutable */
$createdAt = $mapper->getFirstChangeset()->getCreatedAt();

return $createdAt->format('Ym') === $month->format('Ym');
}
);
return $createdAt->format('Ym') === $month->format('Ym');
}
);
}

return $this->render('app/list/index.html.twig', [
'region' => $region,
Expand Down
18 changes: 12 additions & 6 deletions src/Controller/App/MapperController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Entity\Mapper;
use App\Entity\Note;
use App\Entity\Region;
use App\Entity\Template;
use App\Entity\Welcome;
use App\Service\RegionsProvider;
Expand Down Expand Up @@ -31,13 +32,20 @@ public function __construct(
) {
}

#[Route('/mapper/{id}', name: 'app_mapper')]
#[Route('/{regionKey}/mapper/{id}', name: 'app_mapper', requirements: ['regionKey' => '[\w\-_]+'])]
#[Route('/{continent}/{regionKey}/mapper/{id}', name: 'app_mapper_full', requirements: ['continent' => 'asia|africa|australia|europe|north-america|south-america', 'regionKey' => '[\w\-_]+'])]
#[IsGranted('ROLE_USER')]
public function index(Request $request, Mapper $mapper): Response
public function index(Request $request, Mapper $mapper, string $regionKey, ?string $continent): Response
{
$region = $this->provider->getRegion($continent, $regionKey);

$this->mapper = $mapper;

$region = $this->provider->getRegion(null, $this->mapper->getRegion());
$mapperRegions = array_map(fn (Region $region) => $region->getId(), $this->mapper->getRegion()->toArray());

if (!\in_array($regionKey, $mapperRegions, true)) {
throw $this->createNotFoundException('This mapper was not detected in this region.');
}

// Welcome
if ($request->query->has('welcome')) {
Expand All @@ -63,9 +71,7 @@ public function index(Request $request, Mapper $mapper): Response

// Prev/Next mapper
/** @var Mapper[] */
$mappers = $this->entityManager
->getRepository(Mapper::class)
->findBy(['region' => $region['key']]);
$mappers = $this->provider->getEntity($regionKey)?->getMappers()->toArray() ?? [];

$firstChangetsetCreatedAt = array_map(fn (Mapper $mapper): ?\DateTimeImmutable => $mapper->getFirstChangeset()->getCreatedAt(), $mappers);
array_multisort($firstChangetsetCreatedAt, \SORT_DESC, $mappers);
Expand Down
43 changes: 28 additions & 15 deletions src/Entity/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ class Mapper
#[ORM\Column(type: 'integer')]
private $id;

#[ORM\Column(type: 'string', length: 255)]
private $region;

#[ORM\Column(type: 'string', length: 255)]
private $display_name;

Expand All @@ -41,10 +38,14 @@ class Mapper
#[ORM\OneToOne(targetEntity: Welcome::class, mappedBy: 'mapper', cascade: ['persist'])]
private $welcome;

#[ORM\ManyToMany(targetEntity: Region::class, inversedBy: 'mappers')]
private Collection $region;

public function __construct()
{
$this->changesets = new ArrayCollection();
$this->notes = new ArrayCollection();
$this->region = new ArrayCollection();
}

public function getId(): ?int
Expand All @@ -59,18 +60,6 @@ public function setId(int $id): self
return $this;
}

public function getRegion(): ?string
{
return $this->region;
}

public function setRegion(string $region): self
{
$this->region = $region;

return $this;
}

public function getDisplayName(): ?string
{
return $this->display_name;
Expand Down Expand Up @@ -220,4 +209,28 @@ public function setWelcome(Welcome $welcome): self

return $this;
}

/**
* @return Collection<int, region>
*/
public function getRegion(): Collection
{
return $this->region;
}

public function addRegion(region $region): self
{
if (!$this->region->contains($region)) {
$this->region->add($region);
}

return $this;
}

public function removeRegion(region $region): self
{
$this->region->removeElement($region);

return $this;
}
}
37 changes: 37 additions & 0 deletions src/Entity/Region.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace App\Entity;

use App\Repository\RegionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: RegionRepository::class)]
Expand All @@ -15,6 +17,14 @@ class Region
#[ORM\Column(type: 'datetime')]
private $lastUpdate;

#[ORM\ManyToMany(targetEntity: Mapper::class, mappedBy: 'region')]
private Collection $mappers;

public function __construct()
{
$this->mappers = new ArrayCollection();
}

public function getId(): ?string
{
return $this->id;
Expand All @@ -38,4 +48,31 @@ public function setLastUpdate(\DateTime $lastUpdate): self

return $this;
}

/**
* @return Collection<int, Mapper>
*/
public function getMappers(): Collection
{
return $this->mappers;
}

public function addMapper(Mapper $mapper): self
{
if (!$this->mappers->contains($mapper)) {
$this->mappers->add($mapper);
$mapper->addRegion($this);
}

return $this;
}

public function removeMapper(Mapper $mapper): self
{
if ($this->mappers->removeElement($mapper)) {
$mapper->removeRegion($this);
}

return $this;
}
}
15 changes: 5 additions & 10 deletions src/Service/RegionsProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Service;

use App\Entity\Mapper;
use App\Entity\Region;
use App\Repository\MapperRepository;
use App\Repository\RegionRepository;
use App\Repository\WelcomeRepository;
Expand Down Expand Up @@ -70,21 +71,15 @@ public function getGeometry(string $continent, string $key): array
return $data;
}

public function getLastUpdate(string $key): ?\DateTime
public function getEntity(string $key): ?Region
{
$region = $this->regionRepository->find($key);

if (null === $region) {
return null;
}

return $region->getLastUpdate();
return $this->regionRepository->find($key);
}

public function getPercentage(string $key): array
{
/** @var Mapper[] */
$mappers = $this->mapperRepository->findBy(['region' => $key]);
$region = $this->getEntity($key);
$mappers = null === $region ? [] : $region->getMappers()->toArray();

$checked = array_filter($mappers, fn (Mapper $mapper): bool => null !== $mapper->getWelcome() || false === $mapper->getNotes()->isEmpty());

Expand Down
2 changes: 1 addition & 1 deletion templates/app/breadcrumb.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
</svg>
<a href="{{ path('app_mapper', {id: mapper.id}) }}" class="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700">{{ 'Mapper'|trans }}</a>
<a href="{{ path('app_mapper_full', {continent: region.continent, regionKey: region.key, id: mapper.id}) }}" class="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700">{{ 'Mapper'|trans }}</a>
</div>
</li>

Expand Down
4 changes: 2 additions & 2 deletions templates/app/list/table.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
<tbody>
{% for mapper in mappers %}

<tr class="{{ loop.index0 % 2 == 0 ? 'bg-white' : 'bg-gray-50' }}">
<tr class="{{ loop.index0 % 2 == 0 ? 'bg-white' : 'bg-gray-50' }} {{ mapper.region|length > 3 ? 'opacity-25 hover:opacity-75' }}">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{% if app.user is not null %}
<a class="shrink-0 group block"
href="{{ path('app_mapper', {id: mapper.id}) }}">
href="{{ path('app_mapper_full', {continent: region.continent, regionKey: region.key, id: mapper.id}) }}">
{{ include('app/list/avatar.html.twig', {mapper}, with_context=false) }}
</a>
{% else %}
Expand Down
2 changes: 1 addition & 1 deletion templates/app/mapper/form.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<!-- Current: "bg-gray-100 text-gray-900", Default: "text-gray-600 hover:bg-gray-50 hover:text-gray-900" -->
{% set active = template == selectedTemplate %}
<a class="group flex items-center px-3 py-2 text-sm font-medium rounded-md {{ active ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-50' }}"
href="{{ path('app_mapper', {id: mapper.id}) }}?locale={{ template.locale }}&amp;template={{ template.filename }}">
href="{{ path('app_mapper_full', {continent: region.continent, regionKey: region.key, id: mapper.id}) }}?locale={{ template.locale }}&amp;template={{ template.filename }}">
<span class="truncate">
{{ template.name }} ({{ template.locale|language_name }})
</span>
Expand Down
7 changes: 5 additions & 2 deletions templates/app/mapper/header.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
{% set date = mapper.accountCreated|format_date('long') %}
{{ 'Mapper since {date}'|trans({'{date}': date}) }}
</p>
<p class="text-sm font-medium text-gray-500">
{{ 'Mapper detected in {count} region(s)'|trans({'{count}': mapper.region|length}) }}
</p>
</div>
</div>
<div class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
Expand All @@ -32,7 +35,7 @@

<span class="relative z-0 inline-flex shadow-sm rounded-md">
<a class="{{ prev_mapper is null ? 'opacity-50 cursor-default' : '' }} relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
href="{{ prev_mapper is null ? '#' : path('app_mapper', {id: prev_mapper.id}) }}"
href="{{ prev_mapper is null ? '#' : path('app_mapper_full', {continent: region.continent, regionKey: region.key, id: prev_mapper.id}) }}"
title="{{ prev_mapper is null ? '' : prev_mapper.displayName }}">
<span class="sr-only">{{ 'Previous mapper'|trans }}</span>
<!-- Heroicon name: solid/chevron-left -->
Expand All @@ -44,7 +47,7 @@
</svg>
</a>
<a class="-ml-px {{ next_mapper is null ? 'opacity-50 cursor-default' : '' }} relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
href="{{ next_mapper is null ? '#' : path('app_mapper', {id: next_mapper.id}) }}"
href="{{ next_mapper is null ? '#' : path('app_mapper_full', {continent: region.continent, regionKey: region.key, id: next_mapper.id}) }}"
title="{{ next_mapper is null ? '' : next_mapper.displayName }}">
<span class="sr-only">{{ 'Next mapper'|trans }}</span>
<!-- Heroicon name: solid/chevron-right -->
Expand Down

0 comments on commit 1b62529

Please sign in to comment.