getRepository(Stats::class)->findOneBy(['zone' => $insee_code]); if (!$stats) { $this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE.'); return $this->redirectToRoute('app_admin'); } $followups = $stats->getCityFollowUps(); foreach ($followups as $fu) { $em->remove($fu); } $em->flush(); $this->addFlash('success', 'Tous les suivis ont été supprimés pour cette ville.'); return $this->redirectToRoute('admin_followup_graph', ['insee_code' => $insee_code]); } #[Route('/admin/followup/{insee_code}', name: 'admin_followup')] public function followup( string $insee_code, Motocultrice $motocultrice, EntityManagerInterface $em ): Response { // Récupérer la stats de la ville $stats = $em->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]); if (!$stats) { $this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE.'); return $this->redirectToRoute('app_admin'); } // Ne plus supprimer les anciens suivis ! // $followups = $stats->getCityFollowUps(); // foreach ($followups as $fu) { // $em->remove($fu); // } // $em->flush(); // Récupérer les objets OSM $elements = $motocultrice->followUpCity($insee_code); // Séparer les objets par type $types = [ 'fire_hydrant' => [ 'label' => 'Bornes incendie', 'objects' => array_filter($elements, fn($el) => ($el['tags']['emergency'] ?? null) === 'fire_hydrant') ], 'charging_station' => [ 'label' => 'Bornes de recharge', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'charging_station') ], 'toilets' => [ 'label' => 'Toilettes publiques', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'toilets') ], 'bus_stop' => [ 'label' => 'Arrêts de bus', 'objects' => array_filter($elements, fn($el) => ($el['tags']['highway'] ?? null) === 'bus_stop') ], 'defibrillator' => [ 'label' => 'Défibrillateurs', 'objects' => array_filter($elements, fn($el) => ($el['tags']['emergency'] ?? null) === 'defibrillator') ], 'camera' => [ 'label' => 'Caméras de surveillance', 'objects' => array_filter($elements, fn($el) => ($el['tags']['man_made'] ?? null) === 'surveillance') ], 'recycling' => [ 'label' => 'Points de recyclage', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'recycling') ], 'substation' => [ 'label' => 'Sous-stations électriques', 'objects' => array_filter($elements, fn($el) => ($el['tags']['power'] ?? null) === 'substation') ], ]; $now = new \DateTime(); foreach ($types as $type => $data) { // Suivi du nombre $followupCount = new CityFollowUp(); $followupCount->setName($type . '_count') ->setMeasure(count($data['objects'])) ->setDate($now) ->setStats($stats); $em->persist($followupCount); // Suivi de la complétion personnalisé (exemples) $completed = []; if ($type === 'fire_hydrant') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['ref'] ?? null); }); } elseif ($type === 'charging_station') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['charging_station:output'] ?? null) && !empty($el['tags']['capacity'] ?? null); }); } elseif ($type === 'toilets') { $completed = array_filter($data['objects'], function($el) { return ($el['tags']['wheelchair'] ?? null) === 'yes'; }); } elseif ($type === 'bus_stop') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['shelter'] ?? null); }); } elseif ($type === 'defibrillator') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['indoor'] ?? null); }); } elseif ($type === 'camera') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['surveillance:type'] ?? null); }); } elseif ($type === 'recycling') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['recycling_type'] ?? null); }); } elseif ($type === 'substation') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['substation'] ?? null); }); } $completion = count($data['objects']) > 0 ? round(count($completed) / count($data['objects']) * 100) : 0; $followupCompletion = new CityFollowUp(); $followupCompletion->setName($type . '_completion') ->setMeasure($completion) ->setDate($now) ->setStats($stats); $em->persist($followupCompletion); } $em->flush(); $this->addFlash('success', 'Suivi enregistré pour la ville.'); return $this->redirectToRoute('admin_followup_graph', ['insee_code' => $insee_code]); } #[Route('/admin/followup/{insee_code}/graph', name: 'admin_followup_graph')] public function followupGraph( string $insee_code, EntityManagerInterface $em, Motocultrice $motocultrice ) { $stats = $em->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]); if (!$stats) { $this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE.'); return $this->redirectToRoute('app_admin'); } $followups = $stats->getCityFollowUps(); if ($followups->isEmpty()) { // Générer les followup comme dans l'action followup // (ne pas supprimer les anciens, car il n'y en a pas) $elements = $motocultrice->followUpCity($insee_code); // Séparer les objets par type $types = [ 'fire_hydrant' => [ 'label' => 'Bornes incendie', 'objects' => array_filter($elements, fn($el) => ($el['tags']['emergency'] ?? null) === 'fire_hydrant') ], 'charging_station' => [ 'label' => 'Bornes de recharge', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'charging_station') ], 'toilets' => [ 'label' => 'Toilettes publiques', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'toilets') ], 'bus_stop' => [ 'label' => 'Arrêts de bus', 'objects' => array_filter($elements, fn($el) => ($el['tags']['highway'] ?? null) === 'bus_stop') ], 'defibrillator' => [ 'label' => 'Défibrillateurs', 'objects' => array_filter($elements, fn($el) => ($el['tags']['emergency'] ?? null) === 'defibrillator') ], 'camera' => [ 'label' => 'Caméras de surveillance', 'objects' => array_filter($elements, fn($el) => ($el['tags']['man_made'] ?? null) === 'surveillance') ], 'recycling' => [ 'label' => 'Points de recyclage', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'recycling') ], 'substation' => [ 'label' => 'Sous-stations électriques', 'objects' => array_filter($elements, fn($el) => ($el['tags']['power'] ?? null) === 'substation') ], ]; $now = new \DateTime(); foreach ($types as $type => $data) { // Suivi du nombre $followupCount = new CityFollowUp(); $followupCount->setName($type . '_count') ->setMeasure(count($data['objects'])) ->setDate($now) ->setStats($stats); $em->persist($followupCount); // Suivi de la complétion personnalisé (exemples) $completed = []; if ($type === 'fire_hydrant') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['ref'] ?? null); }); } elseif ($type === 'charging_station') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['charging_station:output'] ?? null) && !empty($el['tags']['capacity'] ?? null); }); } elseif ($type === 'toilets') { $completed = array_filter($data['objects'], function($el) { return ($el['tags']['wheelchair'] ?? null) === 'yes'; }); } elseif ($type === 'bus_stop') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['shelter'] ?? null); }); $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['indoor'] ?? null); }); } elseif ($type === 'camera') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['surveillance:type'] ?? null); }); } elseif ($type === 'recycling') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['recycling_type'] ?? null); }); } elseif ($type === 'substation') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['substation'] ?? null); }); } $completion = count($data['objects']) > 0 ? round(count($completed) / count($data['objects']) * 100) : 0; $followupCompletion = new CityFollowUp(); $followupCompletion->setName($type . '_completion') ->setMeasure($completion) ->setDate($now) ->setStats($stats); $em->persist($followupCompletion); } $em->flush(); // Recharger les followups $followups = $stats->getCityFollowUps(); } $followups = $followups->toArray(); usort($followups, fn($a, $b) => $a->getDate() <=> $b->getDate()); // Grouper par type $series = []; foreach ($followups as $fu) { $series[$fu->getName()][] = [ 'date' => $fu->getDate()->format('c'), 'value' => $fu->getMeasure(), 'name' => $fu->getName(), ]; } return $this->render('admin/followup_graph.html.twig', [ 'stats' => $stats, 'series' => $series ]); } #[Route('/admin/followup/all', name: 'admin_followup_all')] public function followupAll( EntityManagerInterface $em, Motocultrice $motocultrice ) { $statsList = $em->getRepository(Stats::class)->findAll(); $now = new \DateTime(); foreach ($statsList as $stats) { // Ne plus supprimer les anciens suivis ! // foreach ($stats->getCityFollowUps() as $fu) { // $em->remove($fu); // } // Générer les followups OSM $insee_code = $stats->getZone(); $elements = $motocultrice->followUpCity($insee_code); $types = [ 'fire_hydrant' => [ 'label' => 'Bornes incendie', 'objects' => array_filter($elements, fn($el) => ($el['tags']['emergency'] ?? null) === 'fire_hydrant') ], 'charging_station' => [ 'label' => 'Bornes de recharge', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'charging_station') ], 'toilets' => [ 'label' => 'Toilettes publiques', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'toilets') ], 'bus_stop' => [ 'label' => 'Arrêts de bus', 'objects' => array_filter($elements, fn($el) => ($el['tags']['highway'] ?? null) === 'bus_stop') ], 'defibrillator' => [ 'label' => 'Défibrillateurs', 'objects' => array_filter($elements, fn($el) => ($el['tags']['emergency'] ?? null) === 'defibrillator') ], 'camera' => [ 'label' => 'Caméras de surveillance', 'objects' => array_filter($elements, fn($el) => ($el['tags']['man_made'] ?? null) === 'surveillance') ], 'recycling' => [ 'label' => 'Points de recyclage', 'objects' => array_filter($elements, fn($el) => ($el['tags']['amenity'] ?? null) === 'recycling') ], 'substation' => [ 'label' => 'Sous-stations électriques', 'objects' => array_filter($elements, fn($el) => ($el['tags']['power'] ?? null) === 'substation') ], ]; foreach ($types as $type => $data) { // Suivi du nombre $followupCount = new CityFollowUp(); $followupCount->setName($type . '_count') ->setMeasure(count($data['objects'])) ->setDate($now) ->setStats($stats); $em->persist($followupCount); // Suivi de la complétion personnalisé (exemples) $completed = []; if ($type === 'fire_hydrant') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['ref'] ?? null); }); } elseif ($type === 'charging_station') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['charging_station:output'] ?? null) && !empty($el['tags']['capacity'] ?? null); }); } elseif ($type === 'toilets') { $completed = array_filter($data['objects'], function($el) { return ($el['tags']['wheelchair'] ?? null) === 'yes'; }); } elseif ($type === 'bus_stop') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['shelter'] ?? null); }); $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['indoor'] ?? null); }); } elseif ($type === 'camera') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['surveillance:type'] ?? null); }); } elseif ($type === 'recycling') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['recycling_type'] ?? null); }); } elseif ($type === 'substation') { $completed = array_filter($data['objects'], function($el) { return !empty($el['tags']['substation'] ?? null); }); } $completion = count($data['objects']) > 0 ? round(count($completed) / count($data['objects']) * 100) : 0; $followupCompletion = new CityFollowUp(); $followupCompletion->setName($type . '_completion') ->setMeasure($completion) ->setDate($now) ->setStats($stats); $em->persist($followupCompletion); } // Ajout du suivi sur le nombre de Places $followupPlaces = new CityFollowUp(); $followupPlaces->setName('places_count') ->setMeasure($stats->getPlacesCount() ?? 0) ->setDate($now) ->setStats($stats); $em->persist($followupPlaces); // Ajout du suivi sur la complétion moyenne $followupCompletion = new CityFollowUp(); $followupCompletion->setName('places_completion') ->setMeasure($stats->getCompletionPercent() ?? 0) ->setDate($now) ->setStats($stats); $em->persist($followupCompletion); } $em->flush(); $this->addFlash('success', 'Suivi généré pour toutes les villes.'); return $this->redirectToRoute('app_admin'); } }