From 979be016f297a83b72b1c762d984ffeeaf91a131 Mon Sep 17 00:00:00 2001 From: Tykayn Date: Mon, 14 Jul 2025 18:55:53 +0200 Subject: [PATCH] =?UTF-8?q?liens=20de=20th=C3=A8me=20dans=20la=20page=20de?= =?UTF-8?q?=20graphe,=20th=C3=A8mes=20bouche=20d'=C3=A9gout,=20micro=20bib?= =?UTF-8?q?lioth=C3=A8que,=20parc=20=C3=A0=20jeux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- migrations/Version20250714165523.php | 35 +++++++ src/Controller/AdminController.php | 19 ++-- src/Entity/CityFollowUp.php | 2 +- src/Service/FollowUpService.php | 54 ++++------- .../admin/followup_theme_graph.html.twig | 91 ++++++++++--------- 6 files changed, 117 insertions(+), 86 deletions(-) create mode 100644 migrations/Version20250714165523.php diff --git a/README.md b/README.md index 8b94a85..f0bf91d 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ La commande traite la ville la plus ancienne en attente de labourage, si les res Pour labourer environ 300 villes par jour, il faut lancer la commande toutes les 5 minutes (24h * 60min / 5 = 288 passages par jour) : ``` -*/5 * * * * cd /chemin/vers/le/projet && php bin/console app:process-labourage-queue >> var/log/labourage_cron.log 2>&1 +*/5 * * * * cd /poule/encrypted/www/osm-commerces && php bin/console app:process-labourage-queue >> var/log/labourage_cron.log 2>&1 ``` Chaque exécution traite une ville si les ressources le permettent. En adaptant la fréquence, vous pouvez ajuster le débit de traitement. diff --git a/migrations/Version20250714165523.php b/migrations/Version20250714165523.php new file mode 100644 index 0000000..813ce03 --- /dev/null +++ b/migrations/Version20250714165523.php @@ -0,0 +1,35 @@ +addSql(<<<'SQL' + ALTER TABLE place CHANGE email email VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE note note LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE name name VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE note_content note_content VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE street street VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE housenumber housenumber VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE siret siret VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE osm_user osm_user VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, CHANGE email_content email_content LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci` + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE place CHANGE email email VARCHAR(255) DEFAULT NULL, CHANGE note note LONGTEXT DEFAULT NULL, CHANGE name name VARCHAR(255) DEFAULT NULL, CHANGE note_content note_content VARCHAR(255) DEFAULT NULL, CHANGE street street VARCHAR(255) DEFAULT NULL, CHANGE housenumber housenumber VARCHAR(255) DEFAULT NULL, CHANGE siret siret VARCHAR(255) DEFAULT NULL, CHANGE osm_user osm_user VARCHAR(255) DEFAULT NULL, CHANGE email_content email_content LONGTEXT DEFAULT NULL + SQL); + } +} diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index 1fcb3d5..0ded79d 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -243,13 +243,12 @@ final class AdminController extends AbstractController $this->entityManager->flush(); // Générer les suivis (followups) après la mise à jour des Places - $this->followUpService->generateCityFollowUps($stats, $this->motocultrice, $this->entityManager); - - $message = 'Labourage terminé avec succès. ' . $processedCount . ' nouveaux lieux traités.'; - if ($updateExisting) { - $message .= ' ' . $updatedCount . ' lieux existants mis à jour pour la zone ' . $stats->getName() . ' (' . $stats->getZone() . ').'; + $themes = \App\Service\FollowUpService::getFollowUpThemes(); + foreach (array_keys($themes) as $theme) { + $this->followUpService->generateCityFollowUps($stats, $this->motocultrice, $this->entityManager, true, $theme); } - $this->addFlash('success', $message); + $this->entityManager->flush(); + return $this->redirectToRoute('app_admin_stats', ['insee_code' => $insee_code]); } $this->entityManager->flush(); @@ -649,8 +648,9 @@ final class AdminController extends AbstractController 'count_data' => json_encode($countData), 'completion_data' => json_encode($completionData), 'icons' => \App\Service\FollowUpService::getFollowUpIcons(), + 'followup_labels' => $themes, 'geojson' => json_encode($geojson), - 'overpass_query' => $overpass_query | "", + 'overpass_query' => $overpass_query, 'josm_url' => $josm_url, 'center' => $center, 'maptiler_token' => $_ENV['MAPTILER_TOKEN'] ?? null, @@ -765,7 +765,10 @@ final class AdminController extends AbstractController $this->addFlash('warning', "Le serveur est trop sollicité actuellement (RAM insuffisante). La mise à jour des lieux sera effectuée plus tard automatiquement."); } // Toujours générer les CityFollowUp (mais ne jamais les supprimer) - $this->followUpService->generateCityFollowUps($stats, $this->motocultrice, $this->entityManager, true /* disable cleanup: ne supprime rien */); + $themes = \App\Service\FollowUpService::getFollowUpThemes(); + foreach (array_keys($themes) as $theme) { + $this->followUpService->generateCityFollowUps($stats, $this->motocultrice, $this->entityManager, true, $theme); + } $this->entityManager->flush(); return $this->redirectToRoute('app_admin_stats', ['insee_code' => $insee_code]); } diff --git a/src/Entity/CityFollowUp.php b/src/Entity/CityFollowUp.php index 877193b..cbc7c7b 100644 --- a/src/Entity/CityFollowUp.php +++ b/src/Entity/CityFollowUp.php @@ -22,7 +22,7 @@ class CityFollowUp #[ORM\Column] private ?\DateTime $date = null; - #[ORM\ManyToOne(targetEntity: Stats::class, inversedBy: 'cityFollowUps')] + #[ORM\ManyToOne(targetEntity: Stats::class, inversedBy: 'cityFollowUps', cascade: ['persist'])] private ?Stats $stats = null; public function getId(): ?int diff --git a/src/Service/FollowUpService.php b/src/Service/FollowUpService.php index 4514eff..412a95c 100644 --- a/src/Service/FollowUpService.php +++ b/src/Service/FollowUpService.php @@ -68,6 +68,12 @@ class FollowUpService $objects = []; } elseif ($type === 'power_pole') { $objects = array_filter($elements, fn($el) => ($el['tags']['power'] ?? null) === 'pole') ?? []; + } elseif ($type === 'manhole') { + $objects = array_filter($elements, fn($el) => ($el['tags']['manhole'] ?? null) === 'manhole') ?? []; + } elseif ($type === 'little_free_library') { + $objects = array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'little_free_library') ?? []; + } elseif ($type === 'playground') { + $objects = array_filter($elements, fn($el) => ($el['tags']['leisure'] ?? null) === 'playground') ?? []; } else { $objects = []; } @@ -148,40 +154,8 @@ class FollowUpService $em->flush(); $em->clear(); - // Suppression des mesures redondantes (même valeur consécutive, sauf la dernière) - désactivée si $disableCleanup est true - if (!$disableCleanup) { - $repo = $em->getRepository(CityFollowUp::class); - foreach (array_keys(self::getFollowUpThemes()) as $type) { - foreach (['_count', '_completion'] as $suffix) { - $name = $type . $suffix; - $followups = $repo->createQueryBuilder('f') - ->where('f.stats = :stats') - ->andWhere('f.name = :name') - ->setParameter('stats', $stats) - ->setParameter('name', $name) - ->orderBy('f.date', 'ASC') - ->getQuery()->getResult(); - $toDelete = []; - $prev = null; - $n = count($followups); - foreach ($followups as $i => $fu) { - // Si seulement 2 mesures, ne rien supprimer - if ($n == 2) { - break; - } - if ($prev && $fu->getMeasure() === $prev->getMeasure() && $i < $n - 1) { - $toDelete[] = $prev; - } - $prev = $fu; - } - foreach ($toDelete as $del) { - $em->remove($del); - } - } - } - $em->flush(); - $em->clear(); - } + // Suppression des mesures redondantes (même valeur consécutive, sauf la dernière) - désactivée définitivement + // (aucune suppression de CityFollowUp) } public function generateGlobalFollowUps(EntityManagerInterface $em): void @@ -296,6 +270,9 @@ class FollowUpService 'tree' => 'Arbres', 'places' => 'Lieux', 'power_pole' => 'Poteaux électriques', + 'manhole' => "Bouche d'égout", + 'little_free_library' => "Micro bibliothèque", + 'playground' => "Parc à jeux pour enfants", ]; } @@ -325,6 +302,9 @@ class FollowUpService 'tree' => 'bi-tree', 'places' => 'bi-geo-alt', 'power_pole' => 'bi-signpost', + 'manhole' => 'bi-droplet-half', + 'little_free_library' => 'bi-book', + 'playground' => 'bi-emoji-smile', ]; } @@ -359,6 +339,9 @@ class FollowUpService 'drinking_water' => 'nwr["amenity"="drinking_water"](area.searchArea);', 'tree' => 'nwr["natural"="tree"](area.searchArea);', 'power_pole' => 'nwr["power"="pole"](area.searchArea);', + 'manhole' => 'nwr["manhole"](area.searchArea);', + 'little_free_library' => 'nwr["amenity"="little_free_library"](area.searchArea);', + 'playground' => 'nwr["leisure"="playground"](area.searchArea);', ]; } @@ -505,6 +488,9 @@ class FollowUpService 'tree' => ['species', 'leaf_type', 'leaf_cycle'], 'power_pole' => ['ref', 'material'], 'places' => ['name', 'address', 'opening_hours', 'website', 'phone', 'wheelchair', 'siret'], + 'manhole' => ['manhole', 'location'], + 'little_free_library' => ['amenity', 'operator'], + 'playground' => ['playground', 'operator'], ]; } } \ No newline at end of file diff --git a/templates/admin/followup_theme_graph.html.twig b/templates/admin/followup_theme_graph.html.twig index 13d1305..3a7ccf0 100644 --- a/templates/admin/followup_theme_graph.html.twig +++ b/templates/admin/followup_theme_graph.html.twig @@ -187,6 +187,12 @@ + + {% if overpass_query is defined %} + + Vérifier sur Overpass Turbo + + {% endif %} {% if josm_url %} Ouvrir tous les objets dans JOSM @@ -194,17 +200,13 @@ {% else %}
Aucun objet sélectionné pour ce thème, rien à charger dans JOSM.
{% endif %} + {# + #} +
- {% if geojson is defined %} - {# On n'utilise plus geojson côté PHP, la carte sera alimentée dynamiquement via Overpass en JS #} - {% endif %} - {% if overpass_query is defined %} -
- - Vérifier sur Overpass Turbo - -
- {% endif %}
@@ -224,32 +226,7 @@
Dernière mise à jour
- -
-
- {#
-
Progression
- - - - - - - - -
PériodeNombreComplétion (%)
7 jours{{ progressions.count['7j'] is not null ? '%+d'|format(progressions.count['7j']) : '–' }}{{ progressions.completion['7j'] is not null ? '%+.1f'|format(progressions.completion['7j']) : '–' }}
30 jours{{ progressions.count['30j'] is not null ? '%+d'|format(progressions.count['30j']) : '–' }}{{ progressions.completion['30j'] is not null ? '%+.1f'|format(progressions.completion['30j']) : '–' }}
6 mois{{ progressions.count['6m'] is not null ? '%+d'|format(progressions.count['6m']) : '–' }}{{ progressions.completion['6m'] is not null ? '%+.1f'|format(progressions.completion['6m']) : '–' }}
1 an{{ progressions.count['1a'] is not null ? '%+d'|format(progressions.count['1a']) : '–' }}{{ progressions.completion['1a'] is not null ? '%+.1f'|format(progressions.completion['1a']) : '–' }}
-
#} -
-
-
- - -
-
-
+
@@ -290,7 +267,25 @@
- + +{# Bloc navigation autres thématiques #} +{% if followup_labels is defined and icons is defined %} +
+
+

Autres thématiques de suivi :

+ +
+{% endif %} Faire une suggestion @@ -429,15 +424,27 @@ // Affichage des tags manquants let missingHtml = ''; if (missingTags.length > 0) { - missingHtml = `
Manque : ${missingTags.map(t => `${t}`).join(', ')}
`; + missingHtml = `
Manque : ` + missingTags.map(t => `${t}`).join(', ') + `
`; } + // Liens édition JOSM et iD + const josmUrl = `http://127.0.0.1:8111/load_object?objects=${e.type[0].toUpperCase()}${e.id}`; + const idUrl = `https://www.openstreetmap.org/edit?editor=id&${e.type}=${e.id}`; const popupHtml = `
- ${e.tags && e.tags.name ? e.tags.name : '(sans nom)'}
- ${e.type} ${e.id}
- ${e.tags ? Object.entries(e.tags).map(([k,v]) => `${k}: ${v}`).join('
') : ''}

+

${e.tags && e.tags.name ? e.tags.name : '(sans nom)'}


Complétion : ${completion !== null ? completion + '%' : '–'} ${missingHtml} -
Voir sur OSM +
+ + + + Voir sur OSM +
+ JOSM + iD + + + ${e.tags ? Object.entries(e.tags).map(([k,v]) => `${k}: ${v}`).join('
') : ''}

+
`; new maplibregl.Marker({ color: color }) .setLngLat([lon, lat])