mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-10-04 17:04:53 +02:00
enrich exports lat et lon
This commit is contained in:
parent
46d3b21cf6
commit
b5b2880637
6 changed files with 606 additions and 53 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue