osm-commerces/src/Controller/AdminController.php
2025-06-01 23:35:15 +02:00

350 lines
13 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,
'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');
}
}
#[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()
]);
}
#[Route('/admin/labourer/{zip_code}', name: 'app_admin_labourer')]
public function labourer_zone(string $zip_code): Response
{
$results = [];
// $zone = 'Briis sous forges';
$results = $this->motocultrice->labourer($zip_code);
// Récupérer les commerces existants dans la base de données pour cette zone
$commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $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);
// 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();
}
$commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $zip_code]);
// 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 ($commerces 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
$osm_object_ids = array_map(function($commerce) {
return $commerce->getOsmId();
}, $commerces);
}
// pour chaque résultat, vérifier que l'on a pas déjà un commerce avec le même osm_object_id
$results = array_filter($results, function($commerce) use ($osm_object_ids) {
return !in_array($commerce['id'], $osm_object_ids);
});
// on crée un commerce pour chaque résultat qui reste
foreach ($results as $result) {
$commerce = new Place();
if (isset($result['tags']['amenity'])) {
$commerce->setMainTag('amenity='.$result['tags']['amenity']);
}
if (isset($result['tags']['shop'])) {
$commerce->setMainTag('shop='.$result['tags']['shop']);
}
if (isset($result['tags']['tourism'])) {
$commerce->setMainTag('tourism='.$result['tags']['tourism']);
}
$commerce->setOsmId($result['id'])
->setOsmKind($result['type'])
->setName($result['name'])
->setZipCode($zip_code)
->setEmail($result['email'])
->setUuidForUrl($this->motocultrice->uuid_create())
->setOptedOut(false)
->setDead(false)
->setModifiedDate(new \DateTime())
->setAskedHumainsSupport(false)
->setLastContactAttemptDate(null)
->setStats($stats)
->setNote($result['tags'] && isset($result['tags']['note']) ? isset($result['tags']['note']) : null)
->setHasOpeningHours($result['tags'] && isset($result['tags']['opening_hours']) ? isset($result['tags']['opening_hours']) : null)
->setHasAddress(($result['tags'] && isset($result['tags']['address']) || $result['tags'] && isset($result['tags']['contact:address'])) ? isset($result['tags']['address']) : null)
->setHasWebsite($result['tags'] && isset($result['tags']['website']) ? $result['tags']['website'] : null)
->setHasWheelchair($result['tags'] && isset($result['tags']['wheelchair']) ? $result['tags']['wheelchair'] : null)
->setHasNote($result['tags'] && isset($result['tags']['note']) ? $result['tags']['note'] : null)
->setNoteContent($result['tags'] && isset($result['tags']['note']) ? $result['tags']['note'] : null)
;
$this->entityManager->persist($commerce);
}
$this->entityManager->flush();
$commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $zip_code]);
// var_dump($commerces[0]);
return $this->render('admin/labourage_results.html.twig', [
'results' => $results,
'commerces' => $commerces,
'zone' => $zip_code,
]);
}
#[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;
}
}