From b41bbc9696bce062884232132482800293925fc1 Mon Sep 17 00:00:00 2001 From: Tykayn Date: Tue, 24 Jun 2025 00:29:15 +0200 Subject: [PATCH] =?UTF-8?q?infos=20de=20fraicheur=20de=20donn=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controller/AdminController.php | 156 +++++++++++ src/Controller/FraicheurController.php | 143 ++++++++++ .../admin/fraicheur_histogramme.html.twig | 260 ++++++++++++++++++ templates/admin/stats.html.twig | 1 + templates/base.html.twig | 7 + templates/public/nav.html.twig | 6 + 6 files changed, 573 insertions(+) create mode 100644 src/Controller/FraicheurController.php create mode 100644 templates/admin/fraicheur_histogramme.html.twig diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index 77f98ab1..8516b1bb 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -13,6 +13,8 @@ use App\Service\Motocultrice; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Request; use function uuid_create; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\JsonResponse; final class AdminController extends AbstractController { @@ -781,4 +783,158 @@ final class AdminController extends AbstractController $this->addFlash('success', 'Email envoyé avec succès à ' . $place->getName() . ' le ' . $place->getLastContactAttemptDate()->format('d/m/Y H:i:s')); return $this->redirectToRoute('app_public_index'); } + + #[Route('/admin/fraicheur/histogramme', name: 'admin_fraicheur_histogramme')] + public function showFraicheurHistogramme(): Response + { + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/fraicheur_osm.json'; + if (!file_exists($jsonPath)) { + // Générer le fichier si absent + $this->calculateFraicheur(); + } + return $this->render('admin/fraicheur_histogramme.html.twig'); + } + + #[Route('/admin/fraicheur/calculate', name: 'admin_fraicheur_calculate')] + public function calculateFraicheur(): Response + { + $filesystem = new Filesystem(); + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/fraicheur_osm.json'; + $now = new \DateTime(); + // Si le fichier existe et a moins de 12h, on ne régénère pas + if ($filesystem->exists($jsonPath)) { + $fileMTime = filemtime($jsonPath); + if ($fileMTime && ($now->getTimestamp() - $fileMTime) < 43200) { // 12h = 43200s + return $this->redirectToRoute('admin_fraicheur_histogramme'); + } + } + $places = $this->entityManager->getRepository(Place::class)->findAll(); + $histogram = []; + $total = 0; + foreach ($places as $place) { + $date = $place->getOsmDataDate(); + if ($date) { + $key = $date->format('Y-m'); + if (!isset($histogram[$key])) { + $histogram[$key] = 0; + } + $histogram[$key]++; + $total++; + } + } + ksort($histogram); + $data = [ + 'generated_at' => $now->format('c'), + 'total' => $total, + 'histogram' => $histogram + ]; + $filesystem->dumpFile($jsonPath, json_encode($data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); + + // --- Distribution villes selon lieux/habitants --- + $distJsonPath = $this->getParameter('kernel.project_dir') . '/var/distribution_villes_lieux_par_habitant.json'; + // Toujours régénérer + $statsRepo = $this->entityManager->getRepository(Stats::class); + $allStats = $statsRepo->findAll(); + $histogram_lieux_par_habitant = []; + $histogram_habitants_par_lieu = []; + $totalVilles = 0; + foreach ($allStats as $stat) { + $places = $stat->getPlacesCount(); + $population = $stat->getPopulation(); + if ($places && $population && $population > 0) { + // lieux par habitant (pas de 0.01) + $ratio_lph = round($places / $population, 4); + $bin_lph = round(floor($ratio_lph / 0.01) * 0.01, 2); + if (!isset($histogram_lieux_par_habitant[$bin_lph])) $histogram_lieux_par_habitant[$bin_lph] = 0; + $histogram_lieux_par_habitant[$bin_lph]++; + // habitants par lieu (pas de 10) + $ratio_hpl = ceil($population / $places); + $bin_hpl = ceil($ratio_hpl / 10) * 10; + if (!isset($histogram_habitants_par_lieu[$bin_hpl])) $histogram_habitants_par_lieu[$bin_hpl] = 0; + $histogram_habitants_par_lieu[$bin_hpl]++; + $totalVilles++; + } + } + ksort($histogram_lieux_par_habitant); + ksort($histogram_habitants_par_lieu); + $distData = [ + 'generated_at' => $now->format('c'), + 'total_villes' => $totalVilles, + 'histogram_001' => $histogram_lieux_par_habitant, + 'histogram_10' => $histogram_habitants_par_lieu + ]; + $filesystem->dumpFile($distJsonPath, json_encode($distData, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); + + return $this->redirectToRoute('admin_fraicheur_histogramme'); + } + + #[Route('/admin/fraicheur/download', name: 'admin_fraicheur_download')] + public function downloadFraicheur(): JsonResponse + { + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/fraicheur_osm.json'; + if (!file_exists($jsonPath)) { + return new JsonResponse(['error' => 'Fichier non généré'], 404); + } + $content = file_get_contents($jsonPath); + $data = json_decode($content, true); + return new JsonResponse($data); + } + + #[Route('/admin/distribution_villes_lieux_par_habitant_download', name: 'admin_distribution_villes_lieux_par_habitant_download')] + public function downloadDistributionVillesLieuxParHabitant(): JsonResponse + { + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/distribution_villes_lieux_par_habitant.json'; + if (!file_exists($jsonPath)) { + // Générer à la volée si absent + $now = new \DateTime(); + $filesystem = new \Symfony\Component\Filesystem\Filesystem(); + $statsRepo = $this->entityManager->getRepository(\App\Entity\Stats::class); + $allStats = $statsRepo->findAll(); + $distribution = []; + $histogram = []; + $totalVilles = 0; + foreach ($allStats as $stat) { + $places = $stat->getPlacesCount(); + $population = $stat->getPopulation(); + if ($places && $population && $population > 0) { + $ratio = round($places / $population, 4); // lieux par habitant + $bin = round(floor($ratio / 0.01) * 0.01, 2); // pas de 0.01 + if (!isset($histogram[$bin])) $histogram[$bin] = 0; + $histogram[$bin]++; + $totalVilles++; + } + } + ksort($histogram); + $distData = [ + 'generated_at' => $now->format('c'), + 'total_villes' => $totalVilles, + 'histogram_001' => $histogram + ]; + $filesystem->dumpFile($jsonPath, json_encode($distData, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); + } + $content = file_get_contents($jsonPath); + $data = json_decode($content, true); + return new JsonResponse($data); + } + + #[Route('/admin/distribution_villes_lieux_par_habitant_villes', name: 'admin_distribution_villes_lieux_par_habitant_villes')] + public function downloadDistributionVillesLieuxParHabitantVilles(): JsonResponse + { + $statsRepo = $this->entityManager->getRepository(\App\Entity\Stats::class); + $allStats = $statsRepo->findAll(); + $villesByBin = []; + foreach ($allStats as $stat) { + $places = $stat->getPlacesCount(); + $population = $stat->getPopulation(); + $name = $stat->getName(); + if ($places && $population && $population > 0 && $name) { + $ratio = round($places / $population, 4); // lieux par habitant + $bin = round(floor($ratio / 0.01) * 0.01, 2); // pas de 0.01 + if (!isset($villesByBin[$bin])) $villesByBin[$bin] = []; + $villesByBin[$bin][] = $name; + } + } + ksort($villesByBin); + return new JsonResponse(['villes_by_bin' => $villesByBin]); + } } diff --git a/src/Controller/FraicheurController.php b/src/Controller/FraicheurController.php new file mode 100644 index 00000000..57ebeffa --- /dev/null +++ b/src/Controller/FraicheurController.php @@ -0,0 +1,143 @@ +getParameter('kernel.project_dir') . '/var/fraicheur_osm.json'; + if (!file_exists($jsonPath)) { + $this->calculateFraicheur(); + } + return $this->render('admin/fraicheur_histogramme.html.twig'); + } + + #[Route('/admin/fraicheur/calculate', name: 'admin_fraicheur_calculate')] + public function calculateFraicheur(): Response + { + $filesystem = new Filesystem(); + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/fraicheur_osm.json'; + $now = new \DateTime(); + $places = $this->entityManager->getRepository(Place::class)->findAll(); + $histogram = []; + $total = 0; + foreach ($places as $place) { + $date = $place->getOsmDataDate(); + if ($date) { + $key = $date->format('Y-m'); + if (!isset($histogram[$key])) { + $histogram[$key] = 0; + } + $histogram[$key]++; + $total++; + } + } + ksort($histogram); + $data = [ + 'generated_at' => $now->format('c'), + 'total' => $total, + 'histogram' => $histogram + ]; + $filesystem->dumpFile($jsonPath, json_encode($data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); + + // --- Distribution villes selon lieux/habitants --- + $distJsonPath = $this->getParameter('kernel.project_dir') . '/var/distribution_villes_lieux_par_habitant.json'; + $statsRepo = $this->entityManager->getRepository(Stats::class); + $allStats = $statsRepo->findAll(); + $histogram_lieux_par_habitant = []; + $histogram_habitants_par_lieu = []; + $villesByBinLph = []; + $villesByBinHpl = []; + $totalVilles = 0; + foreach ($allStats as $stat) { + $places = $stat->getPlacesCount(); + $population = $stat->getPopulation(); + $name = $stat->getName(); + if ($places && $population && $population > 0 && $name) { + // lieux par habitant (pas de 0.01) + $ratio_lph = $places / $population; + $bin_lph = number_format(floor($ratio_lph / 0.01) * 0.01, 2, '.', ''); + if (!isset($histogram_lieux_par_habitant[$bin_lph])) $histogram_lieux_par_habitant[$bin_lph] = 0; + $histogram_lieux_par_habitant[$bin_lph]++; + if (!isset($villesByBinLph[$bin_lph])) $villesByBinLph[$bin_lph] = []; + $villesByBinLph[$bin_lph][] = $name; + // habitants par lieu (pas de 10) + $ratio_hpl = $population / $places; + $bin_hpl = (string)(ceil($ratio_hpl / 10) * 10); + if (!isset($histogram_habitants_par_lieu[$bin_hpl])) $histogram_habitants_par_lieu[$bin_hpl] = 0; + $histogram_habitants_par_lieu[$bin_hpl]++; + if (!isset($villesByBinHpl[$bin_hpl])) $villesByBinHpl[$bin_hpl] = []; + $villesByBinHpl[$bin_hpl][] = $name; + $totalVilles++; + } + } + ksort($histogram_lieux_par_habitant); + ksort($histogram_habitants_par_lieu); + ksort($villesByBinLph); + ksort($villesByBinHpl); + $distData = [ + 'generated_at' => $now->format('c'), + 'total_villes' => $totalVilles, + 'histogram_001' => $histogram_lieux_par_habitant, + 'histogram_10' => $histogram_habitants_par_lieu, + 'villes_by_bin_001' => $villesByBinLph, + 'villes_by_bin_10' => $villesByBinHpl + ]; + $filesystem->dumpFile($distJsonPath, json_encode($distData, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); + + return $this->redirectToRoute('admin_fraicheur_histogramme'); + } + + #[Route('/admin/fraicheur/download', name: 'admin_fraicheur_download')] + public function downloadFraicheur(): JsonResponse + { + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/fraicheur_osm.json'; + if (!file_exists($jsonPath)) { + return new JsonResponse(['error' => 'Fichier non généré'], 404); + } + $content = file_get_contents($jsonPath); + $data = json_decode($content, true); + return new JsonResponse($data); + } + + #[Route('/admin/distribution_villes_lieux_par_habitant_download', name: 'admin_distribution_villes_lieux_par_habitant_download')] + public function downloadDistributionVillesLieuxParHabitant(): JsonResponse + { + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/distribution_villes_lieux_par_habitant.json'; + if (!file_exists($jsonPath)) { + $this->calculateFraicheur(); + } + $content = file_get_contents($jsonPath); + $data = json_decode($content, true); + return new JsonResponse($data); + } + + #[Route('/admin/distribution_villes_lieux_par_habitant_villes', name: 'admin_distribution_villes_lieux_par_habitant_villes')] + public function downloadDistributionVillesLieuxParHabitantVilles(): JsonResponse + { + $jsonPath = $this->getParameter('kernel.project_dir') . '/var/distribution_villes_lieux_par_habitant.json'; + if (!file_exists($jsonPath)) { + $this->calculateFraicheur(); + } + $content = file_get_contents($jsonPath); + $data = json_decode($content, true); + // On renvoie les deux listes de villes par bin + return new JsonResponse([ + 'villes_by_bin_001' => $data['villes_by_bin_001'] ?? [], + 'villes_by_bin_10' => $data['villes_by_bin_10'] ?? [] + ]); + } +} \ No newline at end of file diff --git a/templates/admin/fraicheur_histogramme.html.twig b/templates/admin/fraicheur_histogramme.html.twig new file mode 100644 index 00000000..1cb0b90c --- /dev/null +++ b/templates/admin/fraicheur_histogramme.html.twig @@ -0,0 +1,260 @@ +{% extends 'base.html.twig' %} + +{% block title %}Histogramme de fraîcheur OSM{% endblock %} + +{% block body %} +
+

Histogramme de fraîcheur des données OSM

+

Par année

+ +

Par trimestre

+ + +

Par mois

+ + +

Distribution des villes selon le nombre d'habitants par lieu (par pas de 10)

+ +
+ Régénérer les statistiques + Télécharger le JSON des lieux + Télécharger le JSON villes/lieux/habitant +
+{% endblock %} + +{% block javascripts %} +{{ parent() }} + + +{% endblock %} \ No newline at end of file diff --git a/templates/admin/stats.html.twig b/templates/admin/stats.html.twig index 1f0199c0..91d1e438 100644 --- a/templates/admin/stats.html.twig +++ b/templates/admin/stats.html.twig @@ -513,6 +513,7 @@ x: { title: { display: true, text: 'Trimestre' } } } } + }); } else if (modifCanvas) { modifCanvas.parentNode.innerHTML = '
Aucune donnée de modification disponible pour cette ville.
'; diff --git a/templates/base.html.twig b/templates/base.html.twig index 7988b550..7613e2c7 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -100,6 +100,13 @@ +
+ +
diff --git a/templates/public/nav.html.twig b/templates/public/nav.html.twig index f1992c0d..c97fade3 100644 --- a/templates/public/nav.html.twig +++ b/templates/public/nav.html.twig @@ -35,6 +35,12 @@ {{ 'display.latest_changes'|trans }} +