mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-10-09 17:02:46 +02:00
ajout de stats sur le budget des villes
This commit is contained in:
parent
1973f85dd4
commit
cd8369d08c
14 changed files with 901 additions and 186 deletions
|
@ -10,6 +10,7 @@ use App\Entity\Place;
|
|||
use App\Entity\Stats;
|
||||
use App\Entity\StatsHistory;
|
||||
use App\Service\Motocultrice;
|
||||
use App\Service\BudgetService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use function uuid_create;
|
||||
|
@ -21,7 +22,8 @@ final class AdminController extends AbstractController
|
|||
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager,
|
||||
private Motocultrice $motocultrice
|
||||
private Motocultrice $motocultrice,
|
||||
private BudgetService $budgetService
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -416,6 +418,23 @@ final class AdminController extends AbstractController
|
|||
|
||||
}
|
||||
|
||||
// Récupérer le budget annuel via l'API des finances publiques
|
||||
try {
|
||||
$budgetAnnuel = $this->budgetService->getBudgetAnnuel($insee_code);
|
||||
if ($budgetAnnuel !== null) {
|
||||
$stats->setBudgetAnnuel((string) $budgetAnnuel);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Pas de message d'erreur pour le budget, c'est optionnel
|
||||
}
|
||||
|
||||
// Récupérer tous les lieux existants de la ville en une seule requête
|
||||
$existingPlaces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $insee_code]);
|
||||
$placesByOsmId = [];
|
||||
foreach ($existingPlaces as $pl) {
|
||||
$placesByOsmId[$pl->getOsmId()] = $pl;
|
||||
}
|
||||
|
||||
// Récupérer toutes les données
|
||||
$places_overpass = $this->motocultrice->labourer($insee_code);
|
||||
$processedCount = 0;
|
||||
|
@ -425,10 +444,8 @@ final class AdminController extends AbstractController
|
|||
$overpass_osm_ids = array_map(fn($place) => $place['id'], $places_overpass);
|
||||
|
||||
foreach ($places_overpass as $placeData) {
|
||||
// Vérifier si le lieu existe déjà
|
||||
$existingPlace = $this->entityManager->getRepository(Place::class)
|
||||
->findOneBy(['osmId' => $placeData['id']]);
|
||||
|
||||
// Vérifier si le lieu existe déjà (optimisé)
|
||||
$existingPlace = $placesByOsmId[$placeData['id']] ?? null;
|
||||
if (!$existingPlace) {
|
||||
$place = new Place();
|
||||
$place->setOsmId($placeData['id'])
|
||||
|
@ -450,15 +467,11 @@ final class AdminController extends AbstractController
|
|||
->setPlaceCount(0)
|
||||
// ->setOsmData($placeData['modified'] ?? null)
|
||||
;
|
||||
|
||||
// Mettre à jour les données depuis Overpass
|
||||
$place->update_place_from_overpass_data($placeData);
|
||||
|
||||
$this->entityManager->persist($place);
|
||||
$stats->addPlace($place);
|
||||
$processedCount++;
|
||||
} elseif ($updateExisting) {
|
||||
// Mettre à jour les données depuis Overpass et s'assurer qu'il est marqué comme "vivant"
|
||||
$existingPlace->setDead(false);
|
||||
$existingPlace->update_place_from_overpass_data($placeData);
|
||||
$stats->addPlace($existingPlace);
|
||||
|
@ -937,4 +950,29 @@ final class AdminController extends AbstractController
|
|||
ksort($villesByBin);
|
||||
return new JsonResponse(['villes_by_bin' => $villesByBin]);
|
||||
}
|
||||
|
||||
#[Route('/admin/labourer-tous-les-budgets', name: 'app_admin_labourer_tous_les_budgets')]
|
||||
public function labourerTousLesBudgets(): Response
|
||||
{
|
||||
$statsRepo = $this->entityManager->getRepository(Stats::class);
|
||||
$query = $statsRepo->createQueryBuilder('s')->getQuery();
|
||||
$allStats = $query->toIterable();
|
||||
$budgetsMisAJour = 0;
|
||||
foreach ($allStats as $stat) {
|
||||
if (!$stat->getBudgetAnnuel() && $stat->getZone()) {
|
||||
$budget = $this->budgetService->getBudgetAnnuel($stat->getZone());
|
||||
if ($budget !== null) {
|
||||
$stat->setBudgetAnnuel((string)$budget);
|
||||
$this->entityManager->persist($stat);
|
||||
$budgetsMisAJour++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($budgetsMisAJour > 0) {
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
$this->addFlash('success', $budgetsMisAJour.' budgets mis à jour.');
|
||||
return $this->redirectToRoute('app_admin');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,14 @@ use Symfony\Component\Filesystem\Filesystem;
|
|||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use App\Entity\Place;
|
||||
use App\Entity\Stats;
|
||||
use App\Service\BudgetService;
|
||||
|
||||
class FraicheurController extends AbstractController
|
||||
{
|
||||
public function __construct(private EntityManagerInterface $entityManager) {}
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager,
|
||||
private BudgetService $budgetService
|
||||
) {}
|
||||
|
||||
#[Route('/admin/fraicheur/histogramme', name: 'admin_fraicheur_histogramme')]
|
||||
public function showFraicheurHistogramme(): Response
|
||||
|
@ -31,7 +35,9 @@ class FraicheurController extends AbstractController
|
|||
$filesystem = new Filesystem();
|
||||
$jsonPath = $this->getParameter('kernel.project_dir') . '/var/fraicheur_osm.json';
|
||||
$now = new \DateTime();
|
||||
$places = $this->entityManager->getRepository(Place::class)->findAll();
|
||||
$placeRepo = $this->entityManager->getRepository(Place::class);
|
||||
$query = $placeRepo->createQueryBuilder('p')->getQuery();
|
||||
$places = $query->toIterable();
|
||||
$histogram = [];
|
||||
$total = 0;
|
||||
foreach ($places as $place) {
|
||||
|
@ -44,6 +50,7 @@ class FraicheurController extends AbstractController
|
|||
$histogram[$key]++;
|
||||
$total++;
|
||||
}
|
||||
$this->entityManager->detach($place);
|
||||
}
|
||||
ksort($histogram);
|
||||
$data = [
|
||||
|
@ -56,7 +63,8 @@ class FraicheurController extends AbstractController
|
|||
// --- 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();
|
||||
$queryStats = $statsRepo->createQueryBuilder('s')->getQuery();
|
||||
$allStats = $queryStats->toIterable();
|
||||
$histogram_lieux_par_habitant = [];
|
||||
$histogram_habitants_par_lieu = [];
|
||||
$villesByBinLph = [];
|
||||
|
@ -83,6 +91,7 @@ class FraicheurController extends AbstractController
|
|||
$villesByBinHpl[$bin_hpl][] = $name;
|
||||
$totalVilles++;
|
||||
}
|
||||
$this->entityManager->detach($stat);
|
||||
}
|
||||
ksort($histogram_lieux_par_habitant);
|
||||
ksort($histogram_habitants_par_lieu);
|
||||
|
@ -98,9 +107,152 @@ class FraicheurController extends AbstractController
|
|||
];
|
||||
$filesystem->dumpFile($distJsonPath, json_encode($distData, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// --- Statistiques budgétaires ---
|
||||
// Remplir les budgets manquants avant le calcul
|
||||
$this->budgetService->fillMissingBudgetsForAllStats($this->entityManager);
|
||||
$this->calculateBudgetStats();
|
||||
|
||||
return $this->redirectToRoute('admin_fraicheur_histogramme');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule les statistiques liées au budget des villes
|
||||
*/
|
||||
private function calculateBudgetStats(): void
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$budgetJsonPath = $this->getParameter('kernel.project_dir') . '/var/budget_stats.json';
|
||||
$now = new \DateTime();
|
||||
|
||||
$statsRepo = $this->entityManager->getRepository(Stats::class);
|
||||
$queryStats = $statsRepo->createQueryBuilder('s')->getQuery();
|
||||
$allStats = $queryStats->toIterable();
|
||||
|
||||
$budgetParHabitant = [];
|
||||
$villesAvecBudget = [];
|
||||
$totalBudget = 0;
|
||||
$totalPopulation = 0;
|
||||
|
||||
$budgetParLieu = [];
|
||||
$villesByBinBudgetParLieu = [];
|
||||
$histogramBudgetParLieu = [];
|
||||
// Collecter les données de budget par habitant et par lieu
|
||||
foreach ($allStats as $stat) {
|
||||
$budgetAnnuel = $stat->getBudgetAnnuel();
|
||||
$population = $stat->getPopulation();
|
||||
$placesCount = $stat->getPlacesCount();
|
||||
$name = $stat->getName();
|
||||
|
||||
if ($budgetAnnuel && $population && $population > 0 && $name) {
|
||||
$budgetParHabitantValue = (float)$budgetAnnuel / $population;
|
||||
$budgetParHabitant[] = [
|
||||
'ville' => $name,
|
||||
'code_insee' => $stat->getZone(),
|
||||
'budget_annuel' => (float)$budgetAnnuel,
|
||||
'population' => $population,
|
||||
'budget_par_habitant' => $budgetParHabitantValue
|
||||
];
|
||||
$villesAvecBudget[] = $name;
|
||||
$totalBudget += (float)$budgetAnnuel;
|
||||
$totalPopulation += $population;
|
||||
}
|
||||
// Ajout budget par lieu
|
||||
if ($budgetAnnuel && $placesCount && $placesCount > 0 && $name) {
|
||||
$budgetParLieuValue = (float)$budgetAnnuel / $placesCount;
|
||||
$budgetParLieu[] = [
|
||||
'ville' => $name,
|
||||
'code_insee' => $stat->getZone(),
|
||||
'budget_annuel' => (float)$budgetAnnuel,
|
||||
'places_count' => $placesCount,
|
||||
'budget_par_lieu' => $budgetParLieuValue
|
||||
];
|
||||
$binBudgetParLieu = (string)(ceil($budgetParLieuValue / 5000) * 5000);
|
||||
if (!isset($histogramBudgetParLieu[$binBudgetParLieu])) $histogramBudgetParLieu[$binBudgetParLieu] = 0;
|
||||
$histogramBudgetParLieu[$binBudgetParLieu]++;
|
||||
if (!isset($villesByBinBudgetParLieu[$binBudgetParLieu])) $villesByBinBudgetParLieu[$binBudgetParLieu] = [];
|
||||
$villesByBinBudgetParLieu[$binBudgetParLieu][] = $name;
|
||||
}
|
||||
$this->entityManager->detach($stat);
|
||||
}
|
||||
|
||||
// Calculer la moyenne du budget par habitant
|
||||
$moyenneBudgetParHabitant = $totalPopulation > 0 ? $totalBudget / $totalPopulation : 0;
|
||||
|
||||
// Calculer l'écart à la moyenne pour chaque ville
|
||||
$ecartsMoyenne = [];
|
||||
foreach ($budgetParHabitant as $ville) {
|
||||
$ecart = $this->budgetService->getEcartMoyenneBudgetParHabitant(
|
||||
$ville['budget_par_habitant'],
|
||||
$moyenneBudgetParHabitant
|
||||
);
|
||||
$ecartsMoyenne[] = [
|
||||
'ville' => $ville['ville'],
|
||||
'code_insee' => $ville['code_insee'],
|
||||
'budget_par_habitant' => $ville['budget_par_habitant'],
|
||||
'ecart_moyenne_pourcent' => $ecart
|
||||
];
|
||||
}
|
||||
|
||||
// Créer des histogrammes
|
||||
$histogramBudgetParHabitant = [];
|
||||
$histogramEcartMoyenne = [];
|
||||
$villesByBinBudget = [];
|
||||
$villesByBinEcart = [];
|
||||
|
||||
foreach ($budgetParHabitant as $ville) {
|
||||
// Histogramme budget par habitant (pas de 100€)
|
||||
$binBudget = (string)(ceil($ville['budget_par_habitant'] / 100) * 100);
|
||||
if (!isset($histogramBudgetParHabitant[$binBudget])) {
|
||||
$histogramBudgetParHabitant[$binBudget] = 0;
|
||||
}
|
||||
$histogramBudgetParHabitant[$binBudget]++;
|
||||
if (!isset($villesByBinBudget[$binBudget])) {
|
||||
$villesByBinBudget[$binBudget] = [];
|
||||
}
|
||||
$villesByBinBudget[$binBudget][] = $ville['ville'];
|
||||
}
|
||||
|
||||
foreach ($ecartsMoyenne as $ville) {
|
||||
// Histogramme écart à la moyenne (pas de 10%)
|
||||
$binEcart = (string)(ceil($ville['ecart_moyenne_pourcent'] / 10) * 10);
|
||||
if (!isset($histogramEcartMoyenne[$binEcart])) {
|
||||
$histogramEcartMoyenne[$binEcart] = 0;
|
||||
}
|
||||
$histogramEcartMoyenne[$binEcart]++;
|
||||
if (!isset($villesByBinEcart[$binEcart])) {
|
||||
$villesByBinEcart[$binEcart] = [];
|
||||
}
|
||||
$villesByBinEcart[$binEcart][] = $ville['ville'];
|
||||
}
|
||||
|
||||
ksort($histogramBudgetParHabitant);
|
||||
ksort($histogramEcartMoyenne);
|
||||
ksort($villesByBinBudget);
|
||||
ksort($villesByBinEcart);
|
||||
|
||||
ksort($histogramBudgetParLieu);
|
||||
ksort($villesByBinBudgetParLieu);
|
||||
$budgetData = [
|
||||
'generated_at' => $now->format('c'),
|
||||
'total_villes_avec_budget' => count($villesAvecBudget),
|
||||
'moyenne_budget_par_habitant' => $moyenneBudgetParHabitant,
|
||||
'total_budget' => $totalBudget,
|
||||
'total_population' => $totalPopulation,
|
||||
'villes_avec_budget' => $villesAvecBudget,
|
||||
'budget_par_habitant' => $budgetParHabitant,
|
||||
'budget_par_lieu' => $budgetParLieu,
|
||||
'ecarts_moyenne' => $ecartsMoyenne,
|
||||
'histogram_budget_par_habitant' => $histogramBudgetParHabitant,
|
||||
'histogram_ecart_moyenne' => $histogramEcartMoyenne,
|
||||
'villes_by_bin_budget' => $villesByBinBudget,
|
||||
'villes_by_bin_ecart' => $villesByBinEcart,
|
||||
'histogram_budget_par_lieu' => $histogramBudgetParLieu,
|
||||
'villes_by_bin_budget_par_lieu' => $villesByBinBudgetParLieu
|
||||
];
|
||||
|
||||
$filesystem->dumpFile($budgetJsonPath, json_encode($budgetData, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
#[Route('/admin/fraicheur/download', name: 'admin_fraicheur_download')]
|
||||
public function downloadFraicheur(): JsonResponse
|
||||
{
|
||||
|
@ -140,4 +292,31 @@ class FraicheurController extends AbstractController
|
|||
'villes_by_bin_10' => $data['villes_by_bin_10'] ?? []
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/budget/download', name: 'admin_budget_download')]
|
||||
public function downloadBudget(): JsonResponse
|
||||
{
|
||||
$jsonPath = $this->getParameter('kernel.project_dir') . '/var/budget_stats.json';
|
||||
if (!file_exists($jsonPath)) {
|
||||
$this->calculateFraicheur();
|
||||
}
|
||||
$content = file_get_contents($jsonPath);
|
||||
$data = json_decode($content, true);
|
||||
return new JsonResponse($data);
|
||||
}
|
||||
|
||||
#[Route('/admin/budget/villes', name: 'admin_budget_villes')]
|
||||
public function downloadBudgetVilles(): JsonResponse
|
||||
{
|
||||
$jsonPath = $this->getParameter('kernel.project_dir') . '/var/budget_stats.json';
|
||||
if (!file_exists($jsonPath)) {
|
||||
$this->calculateFraicheur();
|
||||
}
|
||||
$content = file_get_contents($jsonPath);
|
||||
$data = json_decode($content, true);
|
||||
return new JsonResponse([
|
||||
'villes_by_bin_budget' => $data['villes_by_bin_budget'] ?? [],
|
||||
'villes_by_bin_ecart' => $data['villes_by_bin_ecart'] ?? []
|
||||
]);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue