entityManager->getRepository(Stats::class) ->createQueryBuilder('s') ->where('s.date_labourage_requested IS NOT NULL') ->andWhere('s.date_labourage_done IS NULL OR s.date_labourage_done < s.date_labourage_requested') ->andWhere('s.zone != :global_zone') ->setParameter('global_zone', '00000') ->orderBy('s.date_labourage_requested', 'ASC') ->setMaxResults(1) ->getQuery() ->getOneOrNullResult(); if (!$stats) { // 1. Villes jamais labourées (date_labourage_done NULL, hors 00000) $stats = $this->entityManager->getRepository(Stats::class) ->createQueryBuilder('s') ->where('s.zone != :global_zone') ->andWhere('s.date_labourage_done IS NULL') ->setParameter('global_zone', '00000') ->orderBy('s.date_modified', 'ASC') ->setMaxResults(1) ->getQuery() ->getOneOrNullResult(); if ($stats) { $io->note('Aucune ville en attente, on traite en priorité une ville jamais labourée : ' . $stats->getName() . ' (' . $stats->getZone() . ')'); $stats->setDateLabourageRequested(new \DateTime()); $this->entityManager->persist($stats); $this->entityManager->flush(); } else { // 2. Ville la plus anciennement modifiée (hors 00000) $stats = $this->entityManager->getRepository(Stats::class) ->createQueryBuilder('s') ->where('s.zone != :global_zone') ->setParameter('global_zone', '00000') ->orderBy('s.date_modified', 'ASC') ->setMaxResults(1) ->getQuery() ->getOneOrNullResult(); if (!$stats) { $io->success('Aucune ville à traiter.'); return Command::SUCCESS; } $io->note('Aucune ville en attente, on demande le labourage de la ville la plus anciennement modifiée : ' . $stats->getName() . ' (' . $stats->getZone() . ')'); $stats->setDateLabourageRequested(new \DateTime()); $this->entityManager->persist($stats); $this->entityManager->flush(); } } $io->section('Traitement de la ville : ' . $stats->getName() . ' (' . $stats->getZone() . ')'); // Vérifier la RAM disponible (>= 1 Go) $meminfo = @file_get_contents('/proc/meminfo'); $ram_ok = false; if ($meminfo !== false && preg_match('/^MemAvailable:\s+(\d+)/m', $meminfo, $matches)) { $mem_kb = (int)$matches[1]; $ram_ok = ($mem_kb >= 1024 * 1024); // 1 Go } if (!$ram_ok) { $io->warning('RAM insuffisante, on attend le prochain cron.'); return Command::SUCCESS; } // Effectuer le labourage complet (reprendre la logique de création/màj des objets Place) $io->info('RAM suffisante, lancement du labourage...'); $places_overpass = $this->motocultrice->labourer($stats->getZone()); $processedCount = 0; $updatedCount = 0; $existingPlacesQuery = $this->entityManager->getRepository(Place::class) ->createQueryBuilder('p') ->select('p.osmId, p.osm_kind, p.id') ->where('p.zip_code = :zip_code') ->setParameter('zip_code', $stats->getZone()) ->getQuery(); $existingPlacesResult = $existingPlacesQuery->getResult(); $placesByOsmKey = []; foreach ($existingPlacesResult as $placeData) { $osmKey = $placeData['osm_kind'] . '_' . $placeData['osmId']; $placesByOsmKey[$osmKey] = $placeData['id']; } foreach ($places_overpass as $placeData) { $osmKey = $placeData['type'] . '_' . $placeData['id']; $existingPlaceId = $placesByOsmKey[$osmKey] ?? null; if (!$existingPlaceId) { $place = new Place(); $place->setOsmId($placeData['id']) ->setOsmKind($placeData['type']) ->setZipCode($stats->getZone()) ->setUuidForUrl($this->motocultrice->uuid_create()) ->setModifiedDate(new \DateTime()) ->setStats($stats) ->setDead(false) ->setOptedOut(false) ->setMainTag($this->motocultrice->find_main_tag($placeData['tags']) ?? '') ->setStreet($this->motocultrice->find_street($placeData['tags']) ?? '') ->setHousenumber($this->motocultrice->find_housenumber($placeData['tags']) ?? '') ->setSiret($this->motocultrice->find_siret($placeData['tags']) ?? '') ->setAskedHumainsSupport(false) ->setLastContactAttemptDate(null) ->setPlaceCount(0); $place->update_place_from_overpass_data($placeData); $this->entityManager->persist($place); $stats->addPlace($place); $processedCount++; } else { $existingPlace = $this->entityManager->getRepository(Place::class)->find($existingPlaceId); if ($existingPlace) { $existingPlace->setDead(false); $existingPlace->update_place_from_overpass_data($placeData); $stats->addPlace($existingPlace); $this->entityManager->persist($existingPlace); $updatedCount++; } } } $stats->setDateLabourageDone(new \DateTime()); // Update city name from API if available $apiCityName = $this->motocultrice->get_city_osm_from_zip_code($stats->getZone()); if ($apiCityName && $apiCityName !== $stats->getName()) { $io->info(sprintf('Updating city name from "%s" to "%s" based on API data', $stats->getName(), $apiCityName)); $stats->setName($apiCityName); } $io->info('Récupération des followups de cette ville...'); // $this->followUpService->generateCityFollowUps($stats, $this->motocultrice, $this->entityManager); // update completion $stats->computeCompletionPercent(); $followups = $stats->getCityFollowUps(); if ($followups) { $lastFollowUp = $followups[count($followups) - 1]; if ($lastFollowUp) { $name = $lastFollowUp->getName(); $io->success("Followup le plus récent : $name : " . $lastFollowUp->getDate()->format('d/m/Y') . ' : ' . $lastFollowUp->getMeasure()); } } $io->info('Pourcentage de complétion: ' . $stats->getCompletionPercent()); $this->entityManager->persist($stats); $this->entityManager->flush(); $io->success("Labourage terminé : $processedCount nouveaux lieux, $updatedCount lieux mis à jour."); return Command::SUCCESS; } }