diff --git a/assets/js/map-utils.js b/assets/js/map-utils.js new file mode 100644 index 0000000..e69de29 diff --git a/assets/styles/app.css b/assets/styles/app.css index 49ed4be..d66ce9d 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -20,7 +20,11 @@ body { } .filled { - background-color: #b0dfa0; + background-color: #b0dfa0 !important; +} + +.filled:hover { + background-color: #8abb7a !important; } .no-name { @@ -144,4 +148,9 @@ img { .mb-3 { margin-bottom: 1rem !important; } +} + +table tbody { + max-height: 700px; + overflow: auto; } \ No newline at end of file diff --git a/migrations/Version20250617154118.php b/migrations/Version20250617154118.php new file mode 100644 index 0000000..53ace28 --- /dev/null +++ b/migrations/Version20250617154118.php @@ -0,0 +1,41 @@ +addSql(<<<'SQL' + ALTER TABLE stats ADD siren SMALLINT DEFAULT NULL + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats ADD code_epci SMALLINT DEFAULT NULL + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE stats DROP siren + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats DROP code_epci + SQL); + } +} diff --git a/migrations/Version20250617154309.php b/migrations/Version20250617154309.php new file mode 100644 index 0000000..4236a43 --- /dev/null +++ b/migrations/Version20250617154309.php @@ -0,0 +1,35 @@ +addSql(<<<'SQL' + ALTER TABLE stats ADD codes_postaux VARCHAR(255) DEFAULT NULL + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE stats DROP codes_postaux + SQL); + } +} diff --git a/migrations/Version20250617160626.php b/migrations/Version20250617160626.php new file mode 100644 index 0000000..eadf46d --- /dev/null +++ b/migrations/Version20250617160626.php @@ -0,0 +1,44 @@ +addSql(<<<'SQL' + CREATE TABLE stats_history (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, places_count INT DEFAULT NULL, emails_count INT DEFAULT NULL, completion_percent REAL DEFAULT NULL, emails_sent INT DEFAULT NULL, stats_id INT DEFAULT NULL, PRIMARY KEY(id)) + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_BE00311670AA3482 ON stats_history (stats_id) + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history ADD CONSTRAINT FK_BE00311670AA3482 FOREIGN KEY (stats_id) REFERENCES stats (id) NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE stats_history DROP CONSTRAINT FK_BE00311670AA3482 + SQL); + $this->addSql(<<<'SQL' + DROP TABLE stats_history + SQL); + } +} diff --git a/migrations/Version20250617161207.php b/migrations/Version20250617161207.php new file mode 100644 index 0000000..2d983bb --- /dev/null +++ b/migrations/Version20250617161207.php @@ -0,0 +1,35 @@ +addSql(<<<'SQL' + ALTER TABLE stats ALTER zone TYPE BIGINT + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE stats ALTER zone TYPE VARCHAR(255) + SQL); + } +} diff --git a/migrations/Version20250617165738.php b/migrations/Version20250617165738.php new file mode 100644 index 0000000..eff0168 --- /dev/null +++ b/migrations/Version20250617165738.php @@ -0,0 +1,59 @@ +addSql(<<<'SQL' + ALTER TABLE stats_history ADD names_count INT DEFAULT NULL + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history ADD opening_hours_count INT DEFAULT NULL + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history ADD website_count INT DEFAULT NULL + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history ADD address_count INT DEFAULT NULL + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history ADD siret_count INT DEFAULT NULL + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE stats_history DROP names_count + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history DROP opening_hours_count + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history DROP website_count + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history DROP address_count + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE stats_history DROP siret_count + SQL); + } +} diff --git a/public/assets/img/Panoramax.svg b/public/assets/img/Panoramax.svg new file mode 100644 index 0000000..8e16ec0 --- /dev/null +++ b/public/assets/img/Panoramax.svg @@ -0,0 +1,13 @@ + + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/public/js/utils.js b/public/js/utils.js index bc42570..a75b7f1 100644 --- a/public/js/utils.js +++ b/public/js/utils.js @@ -5,7 +5,7 @@ * @param {string} suggestionListId - ID de la liste des suggestions * @param {Function} onSelect - Callback appelé lors de la sélection d'une ville */ -export function setupCitySearch(inputId, suggestionListId, onSelect) { +function setupCitySearch(inputId, suggestionListId, onSelect) { const searchInput = document.getElementById(inputId); const suggestionList = document.getElementById(suggestionListId); @@ -75,12 +75,12 @@ export function setupCitySearch(inputId, suggestionListId, onSelect) { * @param {string} zipCode - Le code postal * @returns {string} L'URL de labourage */ -export function getLabourerUrl(zipCode) { +function getLabourerUrl(zipCode) { return `/admin/labourer/${zipCode}`; } // Fonction pour gérer la soumission du formulaire d'ajout de ville -export function handleAddCityFormSubmit(event) { +function handleAddCityFormSubmit(event) { event.preventDefault(); const form = event.target; const submitButton = form.querySelector('button[type="submit"]'); @@ -101,7 +101,7 @@ export function handleAddCityFormSubmit(event) { * @param {string} selector - Le sélecteur CSS pour cibler les cellules à colorer * @param {string} color - La couleur de base en format RGB (ex: '154, 205, 50') */ -export function colorizePercentageCells(selector, color = '154, 205, 50') { +function colorizePercentageCells(selector, color = '154, 205, 50') { document.querySelectorAll(selector).forEach(cell => { const percentage = parseInt(cell.textContent); if (!isNaN(percentage)) { @@ -116,7 +116,7 @@ export function colorizePercentageCells(selector, color = '154, 205, 50') { * @param {string} selector - Le sélecteur CSS pour cibler les cellules à colorer * @param {string} color - La couleur de base en format RGB (ex: '154, 205, 50') */ -export function colorizePercentageCellsRelative(selector, color = '154, 205, 50') { +function colorizePercentageCellsRelative(selector, color = '154, 205, 50') { // Récupérer toutes les cellules const cells = document.querySelectorAll(selector); @@ -145,7 +145,7 @@ export function colorizePercentageCellsRelative(selector, color = '154, 205, 50' * @param {number} [minFont=0.8] - Taille de police minimale en rem * @param {number} [maxFont=1.2] - Taille de police maximale en rem */ -export function adjustListGroupFontSize(selector, minFont = 0.8, maxFont = 1.2) { +function adjustListGroupFontSize(selector, minFont = 0.8, maxFont = 1.2) { const items = document.querySelectorAll(selector); const count = items.length; let fontSize = maxFont; diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index 0b3b30f..89782b9 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use App\Entity\Place; use App\Entity\Stats; +use App\Entity\StatsHistory; use App\Service\Motocultrice; use Doctrine\ORM\EntityManagerInterface; use function uuid_create; @@ -30,18 +31,29 @@ final class AdminController extends AbstractController ]); } - #[Route('/admin/stats/{zip_code}', name: 'app_admin_stats')] - public function calculer_stats(string $zip_code): Response + #[Route('/admin/stats/{insee_code}', name: 'app_admin_stats')] + public function calculer_stats(string $insee_code): Response { // Récupérer tous les commerces de la zone - $commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $zip_code]); + $commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $insee_code]); // Récupérer les stats existantes pour la zone - $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]); + $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]); + + + $statsHistory = $this->entityManager->getRepository(StatsHistory::class) + ->createQueryBuilder('sh') + ->where('sh.stats = :stats') + ->setParameter('stats', $stats) + ->orderBy('sh.id', 'DESC') + ->setMaxResults(365) + ->getQuery() + ->getResult(); + if(!$stats) { $stats = new Stats(); - $stats->setZone($zip_code); + $stats->setZone($insee_code); } // Calculer les statistiques @@ -72,11 +84,12 @@ final class AdminController extends AbstractController return $this->render('admin/stats.html.twig', [ 'stats' => $stats, - 'zip_code' => $zip_code, - 'query_places' => $this->motocultrice->get_query_places($zip_code), + 'insee_code' => $insee_code, + 'query_places' => $this->motocultrice->get_query_places($insee_code), 'counters' => $calculatedStats['counters'], 'maptiler_token' => $_ENV['MAPTILER_TOKEN'], 'mapbox_token' => $_ENV['MAPBOX_TOKEN'], + 'statsHistory' => $statsHistory, ]); } @@ -122,19 +135,21 @@ final class AdminController extends AbstractController /** - * récupérer les commerces de la zone, créer les nouveaux lieux, et mettre à jour les existants + * récupérer les commerces de la zone selon le code INSEE, créer les nouveaux lieux, et mettre à jour les existants */ - #[Route('/admin/labourer/{zip_code}', name: 'app_admin_labourer')] - public function labourer(string $zip_code, bool $updateExisting = true): Response + #[Route('/admin/labourer/{insee_code}', name: 'app_admin_labourer')] + public function labourer(string $insee_code, bool $updateExisting = true): Response { try { // Récupérer ou créer les stats pour cette zone - $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]); + $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]); - $city = $this->motocultrice->get_city_osm_from_zip_code($zip_code); + $city = $this->motocultrice->get_city_osm_from_zip_code($insee_code); if (!$stats) { $stats = new Stats(); - $stats->setZone($zip_code) + $stats->setDateCreated(new \DateTime()); + $stats->setDateModified(new \DateTime()); + $stats->setZone($insee_code) ->setPlacesCount(0) ->setAvecHoraires(0) ->setAvecAdresse(0) @@ -150,7 +165,7 @@ final class AdminController extends AbstractController // Récupérer la population via l'API $population = null; try { - $apiUrl = 'https://geo.api.gouv.fr/communes/' . $zip_code . '?fields=population'; + $apiUrl = 'https://geo.api.gouv.fr/communes/' . $insee_code; $response = file_get_contents($apiUrl); if ($response !== false) { $data = json_decode($response, true); @@ -158,13 +173,23 @@ final class AdminController extends AbstractController $population = (int)$data['population']; $stats->setPopulation($population); } + if (isset($data['siren'])) { + $stats->setSiren((int)$data['siren']); + } + if (isset($data['codeEpci'])) { + $stats->setCodeEpci((int)$data['codeEpci']); + } + if (isset($data['codesPostaux'])) { + $stats->setCodesPostaux(implode(';', $data['codesPostaux'])); + } } } catch (\Exception $e) { - // Ne rien faire si l'API échoue + $this->addFlash('error', 'Erreur lors de la récupération des données de l\'API : ' . $e->getMessage()); + } // Récupérer toutes les données - $places = $this->motocultrice->labourer($zip_code); + $places = $this->motocultrice->labourer($insee_code); $processedCount = 0; $updatedCount = 0; @@ -177,7 +202,7 @@ final class AdminController extends AbstractController $place = new Place(); $place->setOsmId($placeData['id']) ->setOsmKind($placeData['type']) - ->setZipCode($zip_code) + ->setZipCode($insee_code) ->setUuidForUrl($this->motocultrice->uuid_create()) ->setModifiedDate(new \DateTime()) ->setStats($stats) @@ -212,7 +237,30 @@ final class AdminController extends AbstractController // Mettre à jour les statistiques finales $stats->computeCompletionPercent(); + if($stats->getDateCreated() == null) { + $stats->setDateCreated(new \DateTime()); + } + $stats->setDateModified(new \DateTime()); + + // Créer un historique des statistiques + $statsHistory = new StatsHistory(); + $statsHistory->setDate(new \DateTime()) + ->setStats($stats); + + $statsHistory->setPlacesCount($stats->getPlaces()->count()) + ->setOpeningHoursCount($stats->getAvecHoraires()) + ->setAddressCount($stats->getAvecAdresse()) + ->setWebsiteCount($stats->getAvecSite()) + ->setSiretCount($stats->getAvecSiret()) + // ->setAccessibiliteCount($stats->getAvecAccessibilite()) + // ->setNoteCount($stats->getAvecNote()) + ->setCompletionPercent($stats->getCompletionPercent()) + ->setStats($stats); + + $this->entityManager->persist($statsHistory); + + $this->entityManager->persist($stats); $this->entityManager->flush(); @@ -223,9 +271,11 @@ final class AdminController extends AbstractController $this->addFlash('success', $message); } catch (\Exception $e) { $this->addFlash('error', 'Erreur lors du labourage : ' . $e->getMessage()); + die(var_dump($e)); } - return $this->redirectToRoute('app_admin_stats', ['zip_code' => $zip_code]); + // return $this->redirectToRoute('app_public_dashboard'); + return $this->redirectToRoute('app_admin_stats', ['insee_code' => $insee_code]); } #[Route('/admin/delete/{id}', name: 'app_admin_delete')] @@ -244,11 +294,11 @@ final class AdminController extends AbstractController return $this->redirectToRoute('app_public_dashboard'); } - #[Route('/admin/delete_by_zone/{zip_code}', name: 'app_admin_delete_by_zone')] - public function delete_by_zone(string $zip_code): Response + #[Route('/admin/delete_by_zone/{insee_code}', name: 'app_admin_delete_by_zone')] + public function delete_by_zone(string $insee_code): Response { - $commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $zip_code]); - $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]); + $commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $insee_code]); + $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]); foreach ($commerces as $commerce) { $this->entityManager->remove($commerce); @@ -256,7 +306,7 @@ final class AdminController extends AbstractController $this->entityManager->remove($stats); $this->entityManager->flush(); - $this->addFlash('success', 'Tous les commerces de la zone '.$zip_code.' ont été supprimés avec succès de OSM Mes commerces, mais pas dans OpenStreetMap.'); + $this->addFlash('success', 'Tous les commerces de la zone '.$insee_code.' ont été supprimés avec succès de OSM Mes commerces, mais pas dans OpenStreetMap.'); return $this->redirectToRoute('app_public_dashboard'); } @@ -322,16 +372,16 @@ final class AdminController extends AbstractController return $response; } - #[Route('/admin/export_csv/{zip_code}', name: 'app_admin_export_csv')] - public function export_csv(string $zip_code): Response + #[Route('/admin/export_csv/{insee_code}', name: 'app_admin_export_csv')] + public function export_csv(string $insee_code): Response { - $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $zip_code]); - $response = new Response($this->motocultrice->export($zip_code)); + $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]); + $response = new Response($this->motocultrice->export($insee_code)); $response->headers->set('Content-Type', 'text/csv'); $slug_name = str_replace(' ', '-', $stats->getName()); - $response->headers->set('Content-Disposition', 'attachment; filename="osm-commerces-export_' . $zip_code . '_' . $slug_name . '_' . date('Y-m-d_H-i-s') . '.csv"'); + $response->headers->set('Content-Disposition', 'attachment; filename="osm-commerces-export_' . $insee_code . '_' . $slug_name . '_' . date('Y-m-d_H-i-s') . '.csv"'); return $response; } diff --git a/src/Entity/History.php b/src/Entity/History.php index 5c7b3f9..50f3992 100644 --- a/src/Entity/History.php +++ b/src/Entity/History.php @@ -16,9 +16,6 @@ class History #[ORM\Column] private ?int $id = null; - - - #[ORM\Column(type: Types::SMALLINT, nullable: true)] private ?int $completion_percent = null; diff --git a/src/Entity/Stats.php b/src/Entity/Stats.php index 6db0de4..4278af8 100644 --- a/src/Entity/Stats.php +++ b/src/Entity/Stats.php @@ -16,10 +16,10 @@ class Stats #[ORM\Column] private ?int $id = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, type: Types::STRING, nullable: true)] private ?string $zone = null; // code insee de la zone - #[ORM\Column(type: Types::SMALLINT)] + #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $completion_percent = null; /** @@ -29,27 +29,27 @@ class Stats private Collection $places; // nombre de commerces dans la zone - #[ORM\Column(type: Types::SMALLINT, nullable: true)] + #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $places_count = null; // nombre de commerces avec horaires - #[ORM\Column(type: Types::SMALLINT, nullable: true)] + #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $avec_horaires = null; // nombre de commerces avec adresse - #[ORM\Column(type: Types::SMALLINT, nullable: true)] + #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $avec_adresse = null; // nombre de commerces avec site - #[ORM\Column(type: Types::SMALLINT, nullable: true)] + #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $avec_site = null; // nombre de commerces avec accessibilité - #[ORM\Column(type: Types::SMALLINT, nullable: true)] + #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $avec_accessibilite = null; // nombre de commerces avec note - #[ORM\Column(type: Types::SMALLINT, nullable: true)] + #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $avec_note = null; #[ORM\Column(length: 255, nullable: true)] @@ -59,6 +59,33 @@ class Stats #[ORM\Column(type: Types::INTEGER, nullable: true)] private ?int $population = null; + #[ORM\Column(type: Types::INTEGER, nullable: true)] + private ?int $siren = null; + + #[ORM\Column(type: Types::INTEGER, nullable: true)] + private ?int $codeEpci = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $codesPostaux = null; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: StatsHistory::class, mappedBy: 'stats')] + private Collection $statsHistories; + + #[ORM\Column(nullable: true)] + private ?\DateTime $date_created = null; + + #[ORM\Column(nullable: true)] + private ?\DateTime $date_modified = null; + + #[ORM\Column(nullable: true)] + private ?int $avec_siret = null; + + #[ORM\Column(nullable: true)] + private ?int $avec_name = null; + // calcule le pourcentage de complétion de la zone public function computeCompletionPercent(): ?int { @@ -115,6 +142,7 @@ class Stats public function __construct() { $this->places = new ArrayCollection(); + $this->statsHistories = new ArrayCollection(); } public function getId(): ?int @@ -270,6 +298,120 @@ class Stats $this->population = $population; return $this; } + + public function getSiren(): ?int + { + return $this->siren; + } + + public function setSiren(?int $siren): static + { + $this->siren = $siren; + + return $this; + } + + public function getCodeEpci(): ?int + { + return $this->codeEpci; + } + + public function setCodeEpci(?int $codeEpci): static + { + $this->codeEpci = $codeEpci; + + return $this; + } + + public function getCodesPostaux(): ?string + { + return $this->codesPostaux; + } + + public function setCodesPostaux(?string $codesPostaux): static + { + $this->codesPostaux = $codesPostaux; + + return $this; + } + + /** + * @return Collection + */ + public function getStatsHistories(): Collection + { + return $this->statsHistories; + } + + public function addStatsHistory(StatsHistory $statsHistory): static + { + if (!$this->statsHistories->contains($statsHistory)) { + $this->statsHistories->add($statsHistory); + $statsHistory->setStats($this); + } + + return $this; + } + + public function removeStatsHistory(StatsHistory $statsHistory): static + { + if ($this->statsHistories->removeElement($statsHistory)) { + // set the owning side to null (unless already changed) + if ($statsHistory->getStats() === $this) { + $statsHistory->setStats(null); + } + } + + return $this; + } + + public function getDateCreated(): ?\DateTime + { + return $this->date_created; + } + + public function setDateCreated(?\DateTime $date_created): static + { + $this->date_created = $date_created; + + return $this; + } + + public function getDateModified(): ?\DateTime + { + return $this->date_modified; + } + + public function setDateModified(?\DateTime $date_modified): static + { + $this->date_modified = $date_modified; + + return $this; + } + + public function getAvecSiret(): ?int + { + return $this->avec_siret; + } + + public function setAvecSiret(?int $avec_siret): static + { + $this->avec_siret = $avec_siret; + + return $this; + } + + public function getAvecName(): ?int + { + return $this->avec_name; + } + + public function setAvecName(?int $avec_name): static + { + $this->avec_name = $avec_name; + + return $this; + } } diff --git a/src/Entity/StatsHistory.php b/src/Entity/StatsHistory.php new file mode 100644 index 0000000..3337cee --- /dev/null +++ b/src/Entity/StatsHistory.php @@ -0,0 +1,188 @@ +id; + } + + public function getPlacesCount(): ?int + { + return $this->places_count; + } + + public function setPlacesCount(?int $places_count): static + { + $this->places_count = $places_count; + + return $this; + } + + public function getEmailsCount(): ?int + { + return $this->emails_count; + } + + public function setEmailsCount(?int $emails_count): static + { + $this->emails_count = $emails_count; + + return $this; + } + + public function getCompletionPercent(): ?float + { + return $this->completion_percent; + } + + public function setCompletionPercent(?float $completion_percent): static + { + $this->completion_percent = $completion_percent; + + return $this; + } + + public function getEmailsSent(): ?int + { + return $this->emails_sent; + } + + public function setEmailsSent(?int $emails_sent): static + { + $this->emails_sent = $emails_sent; + + return $this; + } + + public function getStats(): ?Stats + { + return $this->stats; + } + + public function setStats(?Stats $stats): static + { + $this->stats = $stats; + + return $this; + } + + public function getDate(): ?\DateTime + { + return $this->date; + } + + public function setDate(\DateTime $date): static + { + $this->date = $date; + + return $this; + } + + public function getNamesCount(): ?int + { + return $this->names_count; + } + + public function setNamesCount(?int $names_count): static + { + $this->names_count = $names_count; + + return $this; + } + + public function getOpeningHoursCount(): ?int + { + return $this->opening_hours_count; + } + + public function setOpeningHoursCount(?int $opening_hours_count): static + { + $this->opening_hours_count = $opening_hours_count; + + return $this; + } + + public function getWebsiteCount(): ?int + { + return $this->website_count; + } + + public function setWebsiteCount(?int $website_count): static + { + $this->website_count = $website_count; + + return $this; + } + + public function getAddressCount(): ?int + { + return $this->address_count; + } + + public function setAddressCount(?int $address_count): static + { + $this->address_count = $address_count; + + return $this; + } + + public function getSiretCount(): ?int + { + return $this->siret_count; + } + + public function setSiretCount(?int $siret_count): static + { + $this->siret_count = $siret_count; + + return $this; + } + +} diff --git a/src/Repository/StatsHistoryRepository.php b/src/Repository/StatsHistoryRepository.php new file mode 100644 index 0000000..e53a3d5 --- /dev/null +++ b/src/Repository/StatsHistoryRepository.php @@ -0,0 +1,43 @@ + + */ +class StatsHistoryRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, StatsHistory::class); + } + + // /** + // * @return StatsHistory[] Returns an array of StatsHistory objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('s') + // ->andWhere('s.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('s.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?StatsHistory + // { + // return $this->createQueryBuilder('s') + // ->andWhere('s.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/templates/admin/labourage_results.html.twig b/templates/admin/labourage_results.html.twig index 9794889..93de459 100644 --- a/templates/admin/labourage_results.html.twig +++ b/templates/admin/labourage_results.html.twig @@ -10,8 +10,8 @@

Labourage fait sur la zone "{{ stats.zone }} {{stats.name}}" ✅

- Labourer les mises à jour - Voir les résultats + Labourer les mises à jour + Voir les résultats

lieux trouvés en plus: {{ new_places_counter }} diff --git a/templates/admin/stats.html.twig b/templates/admin/stats.html.twig index a4cf711..0822950 100644 --- a/templates/admin/stats.html.twig +++ b/templates/admin/stats.html.twig @@ -22,6 +22,164 @@ {% endblock %} +{% block javascripts %} + {{ parent() }} + + + + + +{% endblock %} + {% block body %}

@@ -31,7 +189,7 @@ {{ stats.name }} - {{ stats.completionPercent }}% complété
- Labourer les mises à jour + Labourer les mises à jour @@ -53,7 +211,7 @@
- {{ stats.getAvecNote() }} / {{ stats.places|length }} commerces avec note + {{ stats.getAvecNote() }} / {{ stats.places|length }} lieux avec note
@@ -68,63 +226,42 @@
{{ stats.places | length}} - commerces dans la zone. + lieux dans la zone.
{{ stats.getAvecHoraires() }} - commerces avec horaires. + lieux avec horaires.
{{ stats.getAvecAdresse() }} - commerces avec adresse. + lieux avec adresse.
{{ stats.getAvecSite() }} - commerces avec site web renseigné. + lieux avec site web renseigné.
{{ stats.getAvecAccessibilite() }} - commerces avec accessibilité PMR renseignée. + lieux avec accessibilité PMR renseignée.
{{ stats.getAvecNote() }} - commerces avec note renseignée. + lieux avec note renseignée.
-
-
-
- -

Comment est calculé le score de complétion ?

- -
- -
-
+
@@ -148,15 +285,16 @@
- + {% include 'admin/stats_history.html.twig' %}
+

Tableau des {{ stats.places |length }} lieux

- + Exporter en CSV @@ -185,15 +323,70 @@
-
+
+

Historique des {{ statsHistory|length }} stats

+ + + + + + + + + + + + + {# #} + {# #} + + + + {% for stat in statsHistory %} + + + + + + + + + + + {# #} + {# #} + + {% endfor %} + +
DatePlacesComplétionEmails countEmails sentOpening hoursAddressWebsiteSiretAccessibiliteNote
{{ stat.date|date('d/m/Y') }}{{ stat.placesCount }}{{ stat.completionPercent }}%{{ stat.emailsCount }}{{ stat.emailsSent }}{{ stat.openingHoursCount }}{{ stat.addressCount }}{{ stat.websiteCount }}{{ stat.siretCount }}{{ stat.accessibiliteCount }}{{ stat.noteCount }}
+
+
+
+
+ +

Comment est calculé le score de complétion ?

+ +
+ +
+
+ - - - - diff --git a/templates/public/dashboard.html.twig b/templates/public/dashboard.html.twig index b1a21ce..67bf609 100644 --- a/templates/public/dashboard.html.twig +++ b/templates/public/dashboard.html.twig @@ -53,9 +53,8 @@ {% block javascripts %} {{ parent() }} - +