enrich exports lat et lon

This commit is contained in:
Tykayn 2025-07-05 10:59:37 +02:00 committed by tykayn
parent 46d3b21cf6
commit b5b2880637
6 changed files with 606 additions and 53 deletions

View file

@ -1320,60 +1320,306 @@ final class AdminController extends AbstractController
#[Route('/admin/podium-contributeurs-osm', name: 'app_admin_podium_contributeurs_osm')]
public function podiumContributeursOsm(): Response
{
// Ajout d'un log d'action avec le service ActionLogger
$this->actionLogger->log('podium_contributeurs_osm', []);
// On suppose que le champ "osmUser" existe sur l'entité Place
$placeRepo = $this->entityManager->getRepository(\App\Entity\Place::class);
// Nouvelle requête groupée pour tout calculer d'un coup
$qb = $placeRepo->createQueryBuilder('p')
->select(
'p.osm_user',
'COUNT(p.id) as nb',
'AVG((CASE WHEN p.has_opening_hours = true THEN 1 ELSE 0 END) +'
. ' (CASE WHEN p.has_address = true THEN 1 ELSE 0 END) +'
. ' (CASE WHEN p.has_website = true THEN 1 ELSE 0 END) +'
. ' (CASE WHEN p.has_wheelchair = true THEN 1 ELSE 0 END) +'
. ' (CASE WHEN p.has_note = true THEN 1 ELSE 0 END)) / 5 * 100 as completion_moyen'
)
->where('p.osm_user IS NOT NULL')
->andWhere("p.osm_user != ''")
->groupBy('p.osm_user')
->orderBy('nb', 'DESC')
->setMaxResults(100);
$podium = $qb->getQuery()->getResult();
// Calcul du score pondéré et normalisation
$maxPondere = 0;
foreach ($podium as &$row) {
$row['completion_moyen'] = $row['completion_moyen'] !== null ? round($row['completion_moyen'], 1) : null;
$row['completion_pondere'] = ($row['completion_moyen'] !== null && $row['nb'] > 0)
? round($row['completion_moyen'] * $row['nb'], 1)
: null;
if ($row['completion_pondere'] !== null && $row['completion_pondere'] > $maxPondere) {
$maxPondere = $row['completion_pondere'];
$this->actionLogger->log('admin/podium_contributeurs_osm', []);
// Récupérer tous les lieux avec un utilisateur OSM
$places = $this->entityManager->getRepository(Place::class)->findBy(['osm_user' => null], ['osm_user' => 'ASC']);
$places = array_filter($places, fn($place) => $place->getOsmUser() !== null);
// Compter les contributions par utilisateur
$contributions = [];
foreach ($places as $place) {
$user = $place->getOsmUser();
if ($user) {
if (!isset($contributions[$user])) {
$contributions[$user] = 0;
}
$contributions[$user]++;
}
}
unset($row);
// Normalisation des scores pondérés entre 0 et 100
foreach ($podium as &$row) {
if ($maxPondere > 0 && $row['completion_pondere'] !== null) {
$row['completion_pondere_normalisee'] = round($row['completion_pondere'] / $maxPondere * 100, 1);
} else {
$row['completion_pondere_normalisee'] = null;
}
}
unset($row);
// Trier par nombre de contributions décroissant
arsort($contributions);
// Prendre les 10 premiers
$topContributors = array_slice($contributions, 0, 10, true);
// Tri décroissant sur le score normalisé
usort($podium, function ($a, $b) {
return ($b['completion_pondere_normalisee'] ?? 0) <=> ($a['completion_pondere_normalisee'] ?? 0);
});
return $this->render('admin/podium_contributeurs_osm.html.twig', [
'podium' => $podium
'contributors' => $topContributors
]);
}
#[Route('/admin/import-stats', name: 'app_admin_import_stats', methods: ['GET', 'POST'])]
public function importStats(Request $request): Response
{
$this->actionLogger->log('admin/import_stats', []);
if ($request->isMethod('POST')) {
$uploadedFile = $request->files->get('json_file');
if (!$uploadedFile) {
$this->addFlash('error', 'Aucun fichier JSON n\'a été fourni.');
return $this->redirectToRoute('app_admin_import_stats');
}
// Vérifier le type de fichier
if ($uploadedFile->getClientMimeType() !== 'application/json' &&
$uploadedFile->getClientOriginalExtension() !== 'json') {
$this->addFlash('error', 'Le fichier doit être au format JSON.');
return $this->redirectToRoute('app_admin_import_stats');
}
try {
// Lire le contenu du fichier
$jsonContent = file_get_contents($uploadedFile->getPathname());
$data = json_decode($jsonContent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception('Erreur lors du décodage JSON: ' . json_last_error_msg());
}
if (!is_array($data)) {
throw new \Exception('Le fichier JSON doit contenir un tableau d\'objets Stats.');
}
$createdCount = 0;
$skippedCount = 0;
$errors = [];
foreach ($data as $index => $statData) {
try {
// Vérifier que les champs requis sont présents
if (!isset($statData['zone']) || !isset($statData['name'])) {
$errors[] = "Ligne " . ($index + 1) . ": Champs 'zone' et 'name' requis";
continue;
}
$zone = $statData['zone'];
$name = $statData['name'];
// Vérifier si l'objet Stats existe déjà
$existingStats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zone]);
if ($existingStats) {
$skippedCount++;
continue; // Ignorer les objets existants
}
// Créer un nouvel objet Stats
$stats = new Stats();
$stats->setZone($zone)
->setName($name)
->setDateCreated(new \DateTime())
->setDateModified(new \DateTime());
// Remplir les champs optionnels
if (isset($statData['population'])) {
$stats->setPopulation($statData['population']);
}
if (isset($statData['budgetAnnuel'])) {
$stats->setBudgetAnnuel($statData['budgetAnnuel']);
}
if (isset($statData['siren'])) {
$stats->setSiren($statData['siren']);
}
if (isset($statData['codeEpci'])) {
$stats->setCodeEpci($statData['codeEpci']);
}
if (isset($statData['codesPostaux'])) {
$stats->setCodesPostaux($statData['codesPostaux']);
}
// Remplir les décomptes si disponibles
if (isset($statData['decomptes'])) {
$decomptes = $statData['decomptes'];
if (isset($decomptes['placesCount'])) {
$stats->setPlacesCount($decomptes['placesCount']);
}
if (isset($decomptes['avecHoraires'])) {
$stats->setAvecHoraires($decomptes['avecHoraires']);
}
if (isset($decomptes['avecAdresse'])) {
$stats->setAvecAdresse($decomptes['avecAdresse']);
}
if (isset($decomptes['avecSite'])) {
$stats->setAvecSite($decomptes['avecSite']);
}
if (isset($decomptes['avecAccessibilite'])) {
$stats->setAvecAccessibilite($decomptes['avecAccessibilite']);
}
if (isset($decomptes['avecNote'])) {
$stats->setAvecNote($decomptes['avecNote']);
}
if (isset($decomptes['completionPercent'])) {
$stats->setCompletionPercent($decomptes['completionPercent']);
}
}
$this->entityManager->persist($stats);
$createdCount++;
} catch (\Exception $e) {
$errors[] = "Ligne " . ($index + 1) . ": " . $e->getMessage();
}
}
// Sauvegarder les changements
$this->entityManager->flush();
// Afficher les résultats
$message = "Import terminé : $createdCount objet(s) créé(s), $skippedCount objet(s) ignoré(s) (déjà existants).";
if (!empty($errors)) {
$message .= " Erreurs : " . count($errors);
foreach ($errors as $error) {
$this->addFlash('warning', $error);
}
}
$this->addFlash('success', $message);
$this->actionLogger->log('admin/import_stats_success', [
'created' => $createdCount,
'skipped' => $skippedCount,
'errors' => count($errors)
]);
} catch (\Exception $e) {
$this->addFlash('error', 'Erreur lors de l\'import : ' . $e->getMessage());
$this->actionLogger->log('admin/import_stats_error', ['error' => $e->getMessage()]);
}
return $this->redirectToRoute('app_admin_import_stats');
}
return $this->render('admin/import_stats.html.twig');
}
#[Route('/admin/export-overpass-csv/{insee_code}', name: 'app_admin_export_overpass_csv')]
public function exportOverpassCsv($insee_code): Response
{
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
if (!$stats) {
throw $this->createNotFoundException('Stats non trouvées pour ce code INSEE');
}
// Construire la requête Overpass
$overpassQuery = '[out:csv(::id,::type,name,amenity,shop,office,craft,leisure,healthcare,emergency,man_made,power,highway,railway,public_transport,landuse,historic,barrier,tourism,sport,place,waterway,natural,geological,route,military,traffic_sign,traffic_calming,seamark,route_master,water,airway,aerialway,building,other;true;false;false)]' . "\n";
$overpassQuery .= 'area["ref:INSEE"="' . $insee_code . '"]->.searchArea;' . "\n";
$overpassQuery .= 'nwr["amenity"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["shop"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["office"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["craft"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["leisure"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["healthcare"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["emergency"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["man_made"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["power"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["highway"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["railway"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["public_transport"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["landuse"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["historic"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["barrier"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["tourism"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["sport"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["place"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["waterway"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["natural"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["geological"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["route"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["military"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["traffic_sign"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["traffic_calming"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["seamark"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["route_master"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["water"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["airway"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["aerialway"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["building"]["name"](area.searchArea);' . "\n";
$overpassQuery .= 'nwr["other"]["name"](area.searchArea);' . "\n";
$url = 'https://overpass-api.de/api/interpreter?data=' . urlencode($overpassQuery);
// Rediriger vers l'API Overpass
return $this->redirect($url);
}
#[Route('/admin/export-table-csv/{insee_code}', name: 'app_admin_export_table_csv')]
public function exportTableCsv($insee_code): Response
{
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
if (!$stats) {
throw $this->createNotFoundException('Stats non trouvées pour ce code INSEE');
}
$response = new Response();
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition', 'attachment; filename="lieux_' . $insee_code . '_' . date('Y-m-d') . '.csv"');
$output = fopen('php://temp', 'r+');
// En-têtes CSV
fputcsv($output, [
'Nom',
'Email',
'Contenu email',
'Completion %',
'Type',
'Adresse',
'Numéro',
'Rue',
'Site web',
'Accès PMR',
'Note',
'Texte de la note',
'SIRET',
'SIRET clos',
'Dernière modif. OSM',
'Utilisateur OSM',
'Lien OSM'
], ';');
// Données
foreach ($stats->getPlaces() as $place) {
$osmKind = $place->getOsmKind();
$osmId = $place->getOsmId();
$osmLink = ($osmKind && $osmId) ? 'https://www.openstreetmap.org/' . $osmKind . '/' . $osmId : '';
// Construire l'adresse complète
$address = '';
if ($place->getHousenumber() && $place->getStreet()) {
$address = $place->getHousenumber() . ' ' . $place->getStreet();
} elseif ($place->getStreet()) {
$address = $place->getStreet();
}
fputcsv($output, [
$place->getName() ?: '(sans nom)',
$place->getEmail() ?: '',
$place->getEmailContent() ?: '',
$place->getCompletionPercentage(),
$place->getMainTag() ?: '',
$address,
$place->getHousenumber() ?: '',
$place->getStreet() ?: '',
$place->hasWebsite() ? 'Oui' : 'Non',
$place->hasWheelchair() ? 'Oui' : 'Non',
$place->getNote() ? 'Oui' : 'Non',
$place->getNoteContent() ?: '',
$place->getSiret() ?: '',
'', // SIRET clos - à implémenter si nécessaire
$place->getOsmDataDate() ? $place->getOsmDataDate()->format('Y-m-d H:i') : '',
$place->getOsmUser() ?: '',
$osmLink
], ';');
}
rewind($output);
$csv = stream_get_contents($output);
fclose($output);
$response->setContent($csv);
return $response;
}
}