diff --git a/assets/app.js b/assets/app.js
index e81c6af1..40604d5c 100644
--- a/assets/app.js
+++ b/assets/app.js
@@ -244,7 +244,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
}
if (insee) {
- window.location.href = `/admin/labourer/${insee}`;
+ window.location.href = `/add-city-without-labourage/${insee}`;
} else {
alert('Veuillez sélectionner une ville valide.');
}
diff --git a/assets/utils.js b/assets/utils.js
index 610c29b9..1abee1e9 100644
--- a/assets/utils.js
+++ b/assets/utils.js
@@ -184,7 +184,7 @@ export function enableLabourageForm() {
form.action = getLabourerUrl(result_search);
// Changer le texte du bouton et le désactiver
- labourageBtn.innerHTML = ' Labourage de ' + result_search.name + '...';
+ labourageBtn.innerHTML = ' Ajout de ' + result_search.name + '...';
labourageBtn.disabled = true;
// Soumettre le formulaire
@@ -281,7 +281,7 @@ export function setupCitySearch(inputId, suggestionListId, onSelect) {
export function getLabourerUrl(obj) {
if (obj && obj.insee) {
- return `/admin/labourer/${obj.insee}`;
+ return `/add-city-without-labourage/${obj.insee}`;
}
return '#';
}
@@ -290,7 +290,7 @@ export function handleAddCityFormSubmit(event) {
event.preventDefault();
const zipCode = document.getElementById('selectedZipCode').value;
if (zipCode && zipCode.match(/^\d{5}$/)) {
- window.location.href = `/admin/labourer/${zipCode}`;
+ window.location.href = `/add-city-without-labourage/${zipCode}`;
} else {
alert('Veuillez sélectionner une ville valide avec un code postal.');
}
diff --git a/counting_osm_objects/.gitignore b/counting_osm_objects/.gitignore
index 0a0fae49..5fb9335d 100644
--- a/counting_osm_objects/.gitignore
+++ b/counting_osm_objects/.gitignore
@@ -4,6 +4,8 @@ polygons/*.poly
test_data/*
test_temp/*
test_results/*
+temp/*
+resultats/*
osm_config.txt
__pycache__
secrets.sh
diff --git a/counting_osm_objects/README.md b/counting_osm_objects/README.md
index 7a526f1b..f38a03d7 100644
--- a/counting_osm_objects/README.md
+++ b/counting_osm_objects/README.md
@@ -5,6 +5,9 @@ administratives françaises.
Pour fonctionner vous aurez besoin du fichier historisé de la france, pour cela connectez vous à geofabrik avec votre
compte osm (oui c'est relou).
+## TODO
+
+faire une extraction json de toutes les zones insee de france et réaliser des mesures pour tous les thèmes.
## Scripts disponibles
@@ -146,6 +149,89 @@ END
Aucune installation spécifique n'est nécessaire pour ces scripts. Assurez-vous simplement que les dépendances sont
installées.
+### analyze_city_polygons.py
+
+Ce script analyse les polygones de villes et génère des fichiers JSON d'analyse. Il:
+
+1. Parcourt tous les fichiers de polygones dans le dossier "polygons"
+2. Pour chaque polygone, vérifie si un fichier d'analyse JSON existe déjà
+3. Si non, utilise loop_thematics_history_in_zone_to_counts.py pour extraire les données
+4. Sauvegarde les résultats dans un fichier JSON avec une analyse de complétion
+5. Ajoute une date de création à chaque analyse
+
+#### Utilisation
+
+```bash
+python analyze_city_polygons.py [--force] [--single CODE_INSEE]
+```
+
+Options:
+- `--force` ou `-f`: Force la recréation des analyses existantes
+- `--single` ou `-s`: Traite uniquement le polygone spécifié (code INSEE)
+
+#### Exemples
+
+Analyser tous les polygones disponibles:
+
+```bash
+python analyze_city_polygons.py
+```
+
+Analyser uniquement la commune avec le code INSEE 59140:
+
+```bash
+python analyze_city_polygons.py --single 59140
+```
+
+Forcer la recréation des analyses existantes:
+
+```bash
+python analyze_city_polygons.py --force
+```
+
+#### Format de sortie
+
+Les fichiers d'analyse sont au format JSON et sont sauvegardés dans le dossier `city_analysis`. Chaque fichier contient:
+
+- Une section `themes` avec des données pour chaque thématique (nombre d'objets, pourcentage de complétion, etc.)
+- Une section `metadata` avec des informations sur l'analyse (code INSEE, date de création, statistiques globales)
+
+Exemple de contenu:
+
+```json
+{
+ "themes": {
+ "borne-de-recharge": {
+ "date": "2025-08-21",
+ "zone": "commune_59140",
+ "theme": "borne-de-recharge",
+ "nombre_total": 0,
+ "nombre_avec_operator": 0,
+ "nombre_avec_capacity": 0,
+ "pourcentage_completion": 0
+ },
+ "borne-incendie": {
+ "date": "2025-08-21",
+ "zone": "commune_59140",
+ "theme": "borne-incendie",
+ "nombre_total": 0,
+ "nombre_avec_ref": 0,
+ "nombre_avec_colour": 0,
+ "pourcentage_completion": 0
+ }
+ },
+ "metadata": {
+ "insee_code": "59140",
+ "creation_date": "2025-08-21 11:12:20",
+ "polygon_file": "commune_59140.poly",
+ "osm_data_file": "france-latest.osm.pbf",
+ "total_objects": 679,
+ "average_completion": 0.44,
+ "theme_count": 7
+ }
+}
+```
+
## Licence
Ces scripts sont distribués sous la même licence que le projet Osmose-Backend.
\ No newline at end of file
diff --git a/src/Controller/PublicController.php b/src/Controller/PublicController.php
index c7d0d1ff..8c5f8653 100644
--- a/src/Controller/PublicController.php
+++ b/src/Controller/PublicController.php
@@ -859,6 +859,84 @@ class PublicController extends AbstractController
'controller_name' => 'PublicController',
]);
}
+
+ /**
+ * Ajoute une ville sans déclencher le labourage et redirige vers la page thématique des lieux
+ */
+ #[Route('/add-city-without-labourage/{insee_code}', name: 'app_public_add_city_without_labourage')]
+ public function addCityWithoutLabourage(string $insee_code): Response
+ {
+ $this->actionLogger->log('add_city_without_labourage', ['insee_code' => $insee_code]);
+
+ // Vérifier si le code INSEE est valide (composé uniquement de chiffres)
+ if (!ctype_digit($insee_code) || $insee_code == 'undefined' || $insee_code == '') {
+ $this->addFlash('error', 'Code INSEE invalide : il doit être composé uniquement de chiffres.');
+ $this->actionLogger->log('ERROR_add_city_without_labourage_bad_insee', ['insee_code' => $insee_code]);
+ return $this->redirectToRoute('app_public_index');
+ }
+
+ // Récupérer ou créer l'objet Stats
+ $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
+ if (!$stats) {
+ $stats = new Stats();
+ $stats->setZone($insee_code);
+ $stats->setKind('request'); // Set the kind to 'request' as it's created from a user request
+ }
+
+ // Compléter le nom si manquant
+ if (!$stats->getName()) {
+ $cityName = $this->motocultrice->get_city_osm_from_zip_code($insee_code);
+ if ($cityName) {
+ $stats->setName($cityName);
+ }
+ }
+
+ // Compléter la population si manquante
+ if (!$stats->getPopulation()) {
+ try {
+ $apiUrl = 'https://geo.api.gouv.fr/communes/' . $insee_code;
+ $response = @file_get_contents($apiUrl);
+ if ($response !== false) {
+ $data = json_decode($response, true);
+ if (isset($data['population'])) {
+ $stats->setPopulation((int)$data['population']);
+ }
+ }
+ } catch (\Exception $e) {
+ // Ignorer les erreurs
+ }
+ }
+
+ // Compléter les lieux d'intérêt si manquants (lat/lon)
+ if (!$stats->getLat() || !$stats->getLon()) {
+ // On tente de récupérer le centre de la ville via l'API geo.gouv.fr
+ try {
+ $apiUrl = 'https://geo.api.gouv.fr/communes/' . $insee_code . '?fields=centre';
+ $response = @file_get_contents($apiUrl);
+ if ($response !== false) {
+ $data = json_decode($response, true);
+ if (isset($data['centre']['coordinates']) && count($data['centre']['coordinates']) === 2) {
+ $stats->setLon((string)$data['centre']['coordinates'][0]);
+ $stats->setLat((string)$data['centre']['coordinates'][1]);
+ }
+ }
+ } catch (\Exception $e) {
+ // Ignorer les erreurs
+ }
+ }
+
+ // Sauvegarder l'objet Stats
+ $stats->computeCompletionPercent();
+ $this->entityManager->persist($stats);
+ $this->entityManager->flush();
+
+ // Rediriger vers la page thématique des lieux
+ return $this->redirectToRoute('admin_followup_theme_graph', [
+ 'insee_code' => $insee_code,
+ 'theme' => 'places'
+ ]);
+
+ }
#[Route('/stats/{insee_code}/followup-graph/{theme}', name: 'app_public_followup_graph', requirements: ['insee_code' => '\\d+', 'theme' => '[a-zA-Z0-9_]+'])]
public function publicFollowupGraph(string $insee_code, string $theme): Response
@@ -875,6 +953,37 @@ class PublicController extends AbstractController
return $this->redirectToRoute('app_public_index');
}
+ // Vérifier si des mesures ont été enregistrées aujourd'hui
+ $today = new \DateTime();
+ $today->setTime(0, 0, 0); // Début de la journée
+
+ $hasRecentMeasurements = false;
+ foreach ($stats->getCityFollowUps() as $fu) {
+ if ($fu->getName() === $theme . '_count' || $fu->getName() === $theme . '_completion') {
+ $measureDate = clone $fu->getDate();
+ $measureDate->setTime(0, 0, 0);
+ if ($measureDate >= $today) {
+ $hasRecentMeasurements = true;
+ break;
+ }
+ }
+ }
+
+ // Si aucune mesure récente n'existe, générer de nouvelles mesures
+ if (!$hasRecentMeasurements) {
+ $this->actionLogger->log('generate_measurements_on_graph_view', [
+ 'insee_code' => $insee_code,
+ 'theme' => $theme
+ ]);
+
+ // Générer les mesures pour tous les thèmes
+ $this->followUpService->generateCityFollowUps($stats, $this->motocultrice, $this->entityManager, true);
+ $this->entityManager->flush();
+
+ // Re-fetch the Stats entity to ensure it's managed by the EntityManager
+ $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
+ }
+
// Récupérer toutes les données de followup pour ce thème
$followups = $stats->getCityFollowUps();
$countData = [];