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; } }