mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-06-20 01:44:42 +02:00
396 lines
15 KiB
PHP
396 lines
15 KiB
PHP
<?php
|
|
|
|
|
|
namespace App\Controller;
|
|
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
use App\Entity\Place;
|
|
use App\Entity\Stats;
|
|
use App\Service\Motocultrice;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use function uuid_create;
|
|
|
|
final class AdminController extends AbstractController
|
|
{
|
|
|
|
public function __construct(
|
|
private EntityManagerInterface $entityManager,
|
|
private Motocultrice $motocultrice
|
|
) {
|
|
}
|
|
|
|
|
|
#[Route('/admin', name: 'app_admin')]
|
|
public function index(): Response
|
|
{
|
|
return $this->render('admin/index.html.twig', [
|
|
'controller_name' => 'AdminController',
|
|
]);
|
|
}
|
|
|
|
#[Route('/admin/stats/{zip_code}', name: 'app_admin_stats')]
|
|
public function calculer_stats(string $zip_code): Response
|
|
{
|
|
// Récupérer tous les commerces de la zone
|
|
$commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $zip_code]);
|
|
|
|
// Récupérer les stats existantes pour la zone
|
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]);
|
|
|
|
if(!$stats) {
|
|
$stats = new Stats();
|
|
$stats->setZone($zip_code);
|
|
}
|
|
|
|
// Calculer les statistiques
|
|
$calculatedStats = $this->motocultrice->calculateStats($commerces);
|
|
|
|
// Mettre à jour les stats pour la zone donnée
|
|
$stats->setPlacesCount($calculatedStats['places_count']);
|
|
$stats->setAvecHoraires($calculatedStats['counters']['avec_horaires']);
|
|
$stats->setAvecAdresse($calculatedStats['counters']['avec_adresse']);
|
|
$stats->setAvecSite($calculatedStats['counters']['avec_site']);
|
|
$stats->setAvecAccessibilite($calculatedStats['counters']['avec_accessibilite']);
|
|
$stats->setAvecNote($calculatedStats['counters']['avec_note']);
|
|
$stats->setCompletionPercent($calculatedStats['completion_percent']);
|
|
|
|
// Associer les stats à chaque commerce
|
|
foreach ($commerces as $commerce) {
|
|
$commerce->setStats($stats);
|
|
$this->entityManager->persist($commerce);
|
|
}
|
|
|
|
$this->entityManager->persist($stats);
|
|
$this->entityManager->flush();
|
|
|
|
$stats->computeCompletionPercent();
|
|
$this->entityManager->persist($stats);
|
|
$this->entityManager->flush();
|
|
|
|
|
|
return $this->render('admin/stats.html.twig', [
|
|
'stats' => $stats,
|
|
'zip_code' => $zip_code,
|
|
'query_places' => $this->motocultrice->get_query_places($zip_code),
|
|
'counters' => $calculatedStats['counters'],
|
|
'maptiler_token' => $_ENV['MAPTILER_TOKEN'],
|
|
'mapbox_token' => $_ENV['MAPBOX_TOKEN'],
|
|
]);
|
|
}
|
|
|
|
#[Route('/admin/placeType/{osm_kind}/{osm_id}', name: 'app_admin_by_osm_id')]
|
|
public function placeType(string $osm_kind, string $osm_id): Response
|
|
{
|
|
$place = $this->entityManager->getRepository(Place::class)->findOneBy(['osm_kind' => $osm_kind, 'osmId' => $osm_id]);
|
|
if($place) {
|
|
return $this->redirectToRoute('app_admin_commerce', ['id' => $place->getId()]);
|
|
} else {
|
|
$this->addFlash('error', 'Le lieu n\'existe pas.');
|
|
return $this->redirectToRoute('app_public_index');
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* rediriger vers l'url unique quand on est admin
|
|
*/
|
|
#[Route('/admin/commerce/{id}', name: 'app_admin_commerce')]
|
|
public function commerce(int $id): Response
|
|
{
|
|
|
|
// Vérifier si on est en prod
|
|
if ($this->getParameter('kernel.environment') === 'prod') {
|
|
$this->addFlash('error', 'Vous n\'avez pas accès à cette page en production.');
|
|
return $this->redirectToRoute('app_public_index');
|
|
}
|
|
$commerce = $this->entityManager->getRepository(Place::class)->find($id);
|
|
|
|
if (!$commerce) {
|
|
throw $this->createNotFoundException('Commerce non trouvé');
|
|
}
|
|
|
|
// Redirection vers la page de modification avec les paramètres nécessaires
|
|
return $this->redirectToRoute('app_public_edit', [
|
|
'zipcode' => $commerce->getZipCode(),
|
|
'name' => $commerce->getName()!='' ? $commerce->getName() : '?',
|
|
'uuid' => $commerce->getUuidForUrl()
|
|
]);
|
|
}
|
|
|
|
|
|
/**
|
|
* récupérer les commerces de la zone, créer les nouveaux lieux, et mettre à jour les existants
|
|
*/
|
|
#[Route('/admin/labourer/{zip_code}', name: 'app_admin_labourer')]
|
|
public function labourer_zone(string $zip_code): Response
|
|
{
|
|
$results = [];
|
|
$results = $this->motocultrice->labourer($zip_code);
|
|
|
|
|
|
|
|
// Récupérer ou créer les stats pour cette zone
|
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]);
|
|
|
|
|
|
if (!$stats) {
|
|
$stats = new Stats();
|
|
$stats->setZone($zip_code);
|
|
|
|
// Récupérer les commerces existants dans la base de données pour cette zone
|
|
|
|
|
|
|
|
}
|
|
$commerces = $stats->getPlaces();
|
|
// for commerce, set stats
|
|
foreach ($commerces as $commerce) {
|
|
$commerce->setStats($stats);
|
|
$this->entityManager->persist($commerce);
|
|
$stats->addPlace($commerce);
|
|
}
|
|
|
|
// rebuild et persist
|
|
$stats->computeCompletionPercent();
|
|
$this->entityManager->persist($stats);
|
|
$this->entityManager->flush();
|
|
|
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]);
|
|
// Si le nom de la zone n'est pas défini, le récupérer via OSM
|
|
if (!$stats->getName()) {
|
|
$city_name = $this->motocultrice->get_city_osm_from_zip_code($zip_code);
|
|
if ($city_name) {
|
|
$stats->setName($city_name);
|
|
$this->entityManager->persist($stats);
|
|
$this->entityManager->flush();
|
|
}
|
|
}
|
|
|
|
// Initialiser les compteurs
|
|
$counters = [
|
|
'avec_horaires' => 0,
|
|
'avec_adresse' => 0,
|
|
'avec_site' => 0,
|
|
'avec_accessibilite' => 0,
|
|
'avec_note' => 0
|
|
];
|
|
|
|
// Compter les différents critères pour chaque commerce
|
|
foreach ($stats->getPlaces() as $commerce) {
|
|
if ($commerce->hasOpeningHours()) {
|
|
$counters['avec_horaires']++;
|
|
}
|
|
if ($commerce->hasAddress()) {
|
|
$counters['avec_adresse']++;
|
|
}
|
|
if ($commerce->hasWebsite()) {
|
|
$counters['avec_site']++;
|
|
}
|
|
if ($commerce->hasWheelchair()) {
|
|
$counters['avec_accessibilite']++;
|
|
}
|
|
if ($commerce->hasNote()) {
|
|
$counters['avec_note']++;
|
|
}
|
|
$commerce->setStats($stats);
|
|
}
|
|
|
|
// Mettre à jour les statistiques
|
|
$stats->setPlacesCount(count($commerces));
|
|
$stats->setAvecHoraires($counters['avec_horaires']);
|
|
$stats->setAvecAdresse($counters['avec_adresse']);
|
|
$stats->setAvecSite($counters['avec_site']);
|
|
$stats->setAvecAccessibilite($counters['avec_accessibilite']);
|
|
$stats->setAvecNote($counters['avec_note']);
|
|
$stats->computeCompletionPercent();
|
|
|
|
$this->entityManager->persist($stats);
|
|
$this->entityManager->flush();
|
|
|
|
$osm_object_ids = [];
|
|
|
|
if ($commerces) {
|
|
// Extraire les osm_object_ids des commerces existants
|
|
foreach ($commerces as $commerce) {
|
|
$osm_object_ids[] = $commerce->getOsmKind() . '_' . $commerce->getOsmId();
|
|
}
|
|
}
|
|
// pour chaque résultat, vérifier que l'on a pas déjà un commerce avec le même osm_object_id
|
|
|
|
$new_places_list = array_filter($results, function($commerce) use ($osm_object_ids) {
|
|
return !in_array($commerce['type'] . '_' . $commerce['id'], $osm_object_ids);
|
|
});
|
|
$existing_places_list = array_filter($results, function($commerce) use ($osm_object_ids) {
|
|
return in_array($commerce['type'] . '_' . $commerce['id'], $osm_object_ids);
|
|
});
|
|
|
|
$new_places_counter = 0;
|
|
|
|
// on crée un commerce pour chaque résultat qui reste
|
|
foreach ($new_places_list as $np) {
|
|
|
|
if( !in_array($np['id'] . '_' . $np['type'] , $osm_object_ids )) {
|
|
$new_place = new Place();
|
|
|
|
$main_tag = $this->motocultrice->find_main_tag($np['tags']);
|
|
|
|
$fullMainTag = $main_tag && isset($np['tags'][$main_tag]) ? $main_tag.'='.$np['tags'][$main_tag] : "";
|
|
$new_place
|
|
->setUuidForUrl($this->motocultrice->uuid_create())
|
|
->setModifiedDate(new \DateTime())
|
|
->setStats($stats)
|
|
->setDead(false)
|
|
->setOptedOut(false)
|
|
->setZipCode($zip_code)
|
|
->setOsmId($np['id'])
|
|
->setMainTag($fullMainTag)
|
|
->setOsmKind($np['type'])
|
|
->setAskedHumainsSupport(false)
|
|
->setLastContactAttemptDate(null)
|
|
->update_place_from_overpass_data($np);
|
|
$this->entityManager->persist($new_place);
|
|
$new_place->setStats($stats);
|
|
|
|
$stats->addPlace($new_place);
|
|
$new_places_counter++;
|
|
}
|
|
}
|
|
|
|
// Mise à jour des commerces existants avec les données Overpass
|
|
foreach ($commerces as $existing_place) {
|
|
foreach ($results as $result) {
|
|
if ($existing_place->getOsmId() == $result['id'] && $existing_place->getOsmKind() == $result['type']) {
|
|
$existing_place->update_place_from_overpass_data($result);
|
|
$existing_place->setStats($stats);
|
|
|
|
$this->entityManager->persist($existing_place);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->entityManager->persist($stats);
|
|
$this->entityManager->flush();
|
|
|
|
|
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]);
|
|
return $this->render('admin/labourage_results.html.twig', [
|
|
'results' => $results,
|
|
'commerces' => $stats->getPlaces(),
|
|
'zone' => $zip_code,
|
|
'stats' => $stats,
|
|
'new_places_counter' => $new_places_counter,
|
|
]);
|
|
}
|
|
|
|
#[Route('/admin/delete/{id}', name: 'app_admin_delete')]
|
|
public function delete(int $id): Response
|
|
{
|
|
$commerce = $this->entityManager->getRepository(Place::class)->find($id);
|
|
if($commerce) {
|
|
$this->entityManager->remove($commerce);
|
|
$this->entityManager->flush();
|
|
|
|
$this->addFlash('success', 'Le lieu '.$commerce->getName().' a été supprimé avec succès de OSM Mes commerces, mais pas dans OpenStreetMap.');
|
|
} else {
|
|
$this->addFlash('error', 'Le lieu n\'existe pas.');
|
|
}
|
|
|
|
return $this->redirectToRoute('app_public_dashboard');
|
|
}
|
|
|
|
#[Route('/admin/delete_by_zone/{zip_code}', name: 'app_admin_delete_by_zone')]
|
|
public function delete_by_zone(string $zip_code): Response
|
|
{
|
|
$commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $zip_code]);
|
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]);
|
|
|
|
foreach ($commerces as $commerce) {
|
|
$this->entityManager->remove($commerce);
|
|
}
|
|
$this->entityManager->remove($stats);
|
|
$this->entityManager->flush();
|
|
|
|
$this->addFlash('success', 'Tous les commerces de la zone '.$zip_code.' ont été supprimés avec succès de OSM Mes commerces, mais pas dans OpenStreetMap.');
|
|
|
|
return $this->redirectToRoute('app_public_dashboard');
|
|
}
|
|
|
|
|
|
#[Route('/admin/export', name: 'app_admin_export')]
|
|
public function export(): Response
|
|
{
|
|
$places = $this->entityManager->getRepository(Place::class)->findAll();
|
|
|
|
$csvData = [];
|
|
$csvData[] = [
|
|
'Nom',
|
|
'Email',
|
|
'Code postal',
|
|
'ID OSM',
|
|
'Type OSM',
|
|
'Date de modification',
|
|
'Date dernier contact',
|
|
'Note',
|
|
'Désabonné',
|
|
'Inactif',
|
|
'Support humain demandé',
|
|
'A des horaires',
|
|
'A une adresse',
|
|
'A un site web',
|
|
'A accessibilité',
|
|
'A une note'
|
|
];
|
|
|
|
foreach ($places as $place) {
|
|
$csvData[] = [
|
|
$place->getName(),
|
|
$place->getEmail(),
|
|
$place->getZipCode(),
|
|
$place->getOsmId(),
|
|
$place->getOsmKind(),
|
|
$place->getModifiedDate() ? $place->getModifiedDate()->format('Y-m-d H:i:s') : '',
|
|
$place->getLastContactAttemptDate() ? $place->getLastContactAttemptDate()->format('Y-m-d H:i:s') : '',
|
|
$place->getNote(),
|
|
$place->isOptedOut() ? 'Oui' : 'Non',
|
|
$place->isDead() ? 'Oui' : 'Non',
|
|
$place->isAskedHumainsSupport() ? 'Oui' : 'Non',
|
|
$place->hasOpeningHours() ? 'Oui' : 'Non',
|
|
$place->hasAddress() ? 'Oui' : 'Non',
|
|
$place->hasWebsite() ? 'Oui' : 'Non',
|
|
$place->hasWheelchair() ? 'Oui' : 'Non',
|
|
$place->hasNote() ? 'Oui' : 'Non'
|
|
];
|
|
}
|
|
|
|
$response = new Response();
|
|
$response->headers->set('Content-Type', 'text/csv');
|
|
$response->headers->set('Content-Disposition', 'attachment; filename="export_places.csv"');
|
|
|
|
$handle = fopen('php://temp', 'r+');
|
|
foreach ($csvData as $row) {
|
|
fputcsv($handle, $row, ';');
|
|
}
|
|
rewind($handle);
|
|
$response->setContent(stream_get_contents($handle));
|
|
fclose($handle);
|
|
|
|
return $response;
|
|
}
|
|
#[Route('/admin/export_csv/{zip_code}', name: 'app_admin_export_csv')]
|
|
public function export_csv(string $zip_code): Response
|
|
{
|
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]);
|
|
$response = new Response($this->motocultrice->export($zip_code));
|
|
$response->headers->set('Content-Type', 'text/csv');
|
|
|
|
$slug_name = str_replace(' ', '-', $stats->getName());
|
|
|
|
$response->headers->set('Content-Disposition', 'attachment; filename="osm-commerces-export_' . $zip_code . '_' . $slug_name . '_' . date('Y-m-d_H-i-s') . '.csv"');
|
|
|
|
return $response;
|
|
}
|
|
}
|