mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-06-20 01:44:42 +02:00
compute stats for completion by zone, have base tags, split categories
This commit is contained in:
parent
f15fec6d18
commit
f69b7824af
16 changed files with 1257 additions and 118 deletions
|
@ -6,7 +6,7 @@ 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;
|
||||
|
@ -29,6 +29,48 @@ final class AdminController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
#[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();
|
||||
|
||||
return $this->render('admin/stats.html.twig', [
|
||||
'stats' => $stats,
|
||||
'zip_code' => $zip_code,
|
||||
'counters' => $calculatedStats['counters']
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/labourer/{zip_code}', name: 'app_admin_labourer')]
|
||||
public function labourer_zone(string $zip_code): Response
|
||||
{
|
||||
|
@ -39,10 +81,71 @@ final class AdminController extends AbstractController
|
|||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if ($commerces) {
|
||||
// Extraire les osm_object_ids des commerces existants
|
||||
$osm_object_ids = array_map(function($commerce) {
|
||||
return $commerce->getOsmId();
|
||||
|
@ -53,7 +156,7 @@ final class AdminController extends AbstractController
|
|||
$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();
|
||||
|
@ -65,11 +168,18 @@ final class AdminController extends AbstractController
|
|||
->setUuidForUrl($this->motocultrice->uuid_create())
|
||||
->setOptedOut(false)
|
||||
->setDead(false)
|
||||
->setNote($result['note'] ?? null)
|
||||
|
||||
->setModifiedDate(new \DateTime())
|
||||
->setAskedHumainsSupport(false)
|
||||
->setLastContactAttemptDate(null)
|
||||
->setStats(null);
|
||||
->setStats(null)
|
||||
->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)
|
||||
;
|
||||
$this->entityManager->persist($commerce);
|
||||
}
|
||||
|
||||
|
@ -77,7 +187,99 @@ final class AdminController extends AbstractController
|
|||
|
||||
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);
|
||||
$name = $commerce->getName();
|
||||
$this->entityManager->remove($commerce);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->addFlash('success', 'Le lieu '.$name.' a été supprimé avec succès de OSM Mes commerces, mais pas dans OpenStreetMap.');
|
||||
|
||||
return $this->redirectToRoute('app_admin_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]);
|
||||
|
||||
foreach ($commerces as $commerce) {
|
||||
$this->entityManager->remove($commerce);
|
||||
}
|
||||
$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_admin_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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,12 +40,31 @@ class PublicController extends AbstractController
|
|||
{
|
||||
$place = $this->entityManager->getRepository(Place::class)->findOneBy(['uuid_for_url' => $uuid]);
|
||||
if (!$place) {
|
||||
$this->addFlash('warning', 'Ce lien de modification n\'existe pas.');
|
||||
return $this->redirectToRoute('app_public_index');
|
||||
}
|
||||
$commerce = $this->motocultrice->get_osm_object_data($place->getOsmKind(), $place->getOsmId());
|
||||
|
||||
if ($place->getOsmKind() === 'relation') {
|
||||
$this->addFlash('warning', 'Les objets OSM de type "relation" ne sont pas gérés dans cet outil.');
|
||||
return $this->redirectToRoute('app_public_index');
|
||||
}
|
||||
|
||||
// récupérer les tags de base
|
||||
$base_tags = $this->motocultrice->base_tags;
|
||||
$base_tags = array_fill_keys($base_tags, '');
|
||||
|
||||
$commerce_overpass = $this->motocultrice->get_osm_object_data($place->getOsmKind(), $place->getOsmId());
|
||||
// Fusionner les tags de base avec les tags existants
|
||||
|
||||
$commerce_overpass['tags_converted'] = array_merge($base_tags, $commerce_overpass['tags_converted']);
|
||||
|
||||
// Trier les tags par ordre alphabétique des clés
|
||||
ksort($commerce_overpass['tags_converted']);
|
||||
|
||||
return $this->render('public/edit.html.twig', [
|
||||
'commerce' => $commerce,
|
||||
'commerce_overpass' => $commerce_overpass,
|
||||
'name' => $name,
|
||||
'commerce' => $place,
|
||||
'osm_kind' => $place->getOsmKind(),
|
||||
"mapbox_token" => $_ENV['MAPBOX_TOKEN'],
|
||||
"maptiler_token" => $_ENV['MAPTILER_TOKEN'],
|
||||
|
@ -57,7 +76,8 @@ class PublicController extends AbstractController
|
|||
{
|
||||
// get stats
|
||||
$stats = $this->entityManager->getRepository(Stats::class)->findAll();
|
||||
$places = $this->entityManager->getRepository(Place::class)->findAll();
|
||||
|
||||
$places = $this->entityManager->getRepository(Place::class)->findBy([], ['zip_code' => 'ASC', 'name' => 'ASC']);
|
||||
return $this->render('public/dashboard.html.twig', [
|
||||
'controller_name' => 'PublicController',
|
||||
'stats' => $stats,
|
||||
|
@ -108,6 +128,9 @@ class PublicController extends AbstractController
|
|||
// Récupérer le token OSM depuis les variables d'environnement
|
||||
$osm_api_token = $_ENV['APP_OSM_BEARER'];
|
||||
|
||||
|
||||
$exception = false;
|
||||
$exception_message = "";
|
||||
try {
|
||||
$client = new Client();
|
||||
|
||||
|
@ -186,6 +209,8 @@ class PublicController extends AbstractController
|
|||
}
|
||||
} catch (\Exception $e) {
|
||||
$status = "Erreur lors de la communication avec l'API OSM: " . $e->getMessage();
|
||||
$exception = true;
|
||||
$exception_message = $e->getMessage();
|
||||
// Debug de la réponse en cas d'erreur
|
||||
if (method_exists($e, 'getResponse')) {
|
||||
var_dump($e->getResponse()->getBody()->getContents());
|
||||
|
@ -199,6 +224,8 @@ class PublicController extends AbstractController
|
|||
'controller_name' => 'PublicController',
|
||||
'commerce' => $commerce,
|
||||
'status' => $status,
|
||||
'exception' => $exception,
|
||||
'exception_message' => $exception_message,
|
||||
'mapbox_token' => $_ENV['MAPBOX_TOKEN'],
|
||||
'maptiler_token' => $_ENV['MAPTILER_TOKEN'],
|
||||
]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue