mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-10-09 17:02:46 +02:00
bubble fraicheur des completions ajouté
This commit is contained in:
parent
cd8369d08c
commit
93086eba60
18 changed files with 179 additions and 66 deletions
|
@ -34,6 +34,12 @@ import {
|
||||||
updateMapHeightForLargeScreens
|
updateMapHeightForLargeScreens
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import Tablesort from 'tablesort';
|
import Tablesort from 'tablesort';
|
||||||
|
import TableSort from 'table-sort-js/table-sort.js';
|
||||||
|
import $ from 'jquery';
|
||||||
|
window.$ = $;
|
||||||
|
window.jQuery = $;
|
||||||
|
// Charger table-sortable (version non minifiée locale)
|
||||||
|
import '../assets/js/table-sortable.js';
|
||||||
|
|
||||||
window.Chart = Chart;
|
window.Chart = Chart;
|
||||||
window.genererCouleurPastel = genererCouleurPastel;
|
window.genererCouleurPastel = genererCouleurPastel;
|
||||||
|
@ -44,6 +50,7 @@ window.ChartDataLabels = ChartDataLabels;
|
||||||
window.maplibregl = maplibregl;
|
window.maplibregl = maplibregl;
|
||||||
window.toggleCompletionInfo = toggleCompletionInfo;
|
window.toggleCompletionInfo = toggleCompletionInfo;
|
||||||
window.updateMapHeightForLargeScreens = updateMapHeightForLargeScreens;
|
window.updateMapHeightForLargeScreens = updateMapHeightForLargeScreens;
|
||||||
|
window.Tablesort = Tablesort;
|
||||||
|
|
||||||
Chart.register(ChartDataLabels);
|
Chart.register(ChartDataLabels);
|
||||||
|
|
||||||
|
@ -112,11 +119,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
}
|
}
|
||||||
updateCompletionProgress();
|
updateCompletionProgress();
|
||||||
|
|
||||||
// Activer le tri sur tous les tableaux désignés
|
|
||||||
document.querySelectorAll('.js-sort-table').forEach(table => {
|
|
||||||
new Tablesort(table);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Focus sur le premier champ texte au chargement
|
// Focus sur le premier champ texte au chargement
|
||||||
// const firstTextInput = document.querySelector('input.form-control');
|
// const firstTextInput = document.querySelector('input.form-control');
|
||||||
// if (firstTextInput) {
|
// if (firstTextInput) {
|
||||||
|
@ -131,13 +133,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
parseCuisine();
|
parseCuisine();
|
||||||
|
|
||||||
// Tri automatique des tableaux
|
|
||||||
// const tables = document.querySelectorAll('table');
|
|
||||||
// tables.forEach(table => {
|
|
||||||
// table.classList.add('js-sort-table');
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
// Modifier la fonction de recherche existante
|
// Modifier la fonction de recherche existante
|
||||||
const searchInput = document.getElementById('app_admin_labourer');
|
const searchInput = document.getElementById('app_admin_labourer');
|
||||||
const suggestionList = document.getElementById('suggestionList');
|
const suggestionList = document.getElementById('suggestionList');
|
||||||
|
@ -201,4 +196,27 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
enableLabourageForm();
|
enableLabourageForm();
|
||||||
adjustListGroupFontSize('.list-group-item');
|
adjustListGroupFontSize('.list-group-item');
|
||||||
|
|
||||||
|
// Activer le tri naturel sur tous les tableaux avec la classe table-sort
|
||||||
|
document.querySelectorAll('table.table-sort').forEach(table => {
|
||||||
|
new TableSort(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialisation du tri et filtrage sur les tableaux du dashboard et de la page stats
|
||||||
|
if (document.querySelector('#dashboard-table')) {
|
||||||
|
$('#dashboard-table').tableSortable({
|
||||||
|
pagination: false,
|
||||||
|
showPaginationLabel: true,
|
||||||
|
searchField: '#dashboard-table-search',
|
||||||
|
responsive: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (document.querySelector('#stats-table')) {
|
||||||
|
$('#stats-table').tableSortable({
|
||||||
|
pagination: false,
|
||||||
|
showPaginationLabel: true,
|
||||||
|
searchField: '#stats-table-search',
|
||||||
|
responsive: false
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,16 +47,17 @@ function waitForChartAndDrawBubble() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcul de la régression linéaire (moindres carrés)
|
// Calcul de la régression linéaire (moindres carrés)
|
||||||
const validPoints = bubbleChartData.filter(d => d.x > 0 && d.y > 0);
|
// On ne fait la régression que si on veut, mais l'axe X = fraicheur, Y = complétion
|
||||||
|
const validPoints = bubbleChartData.filter(d => d.x !== null && d.y !== null);
|
||||||
const n = validPoints.length;
|
const n = validPoints.length;
|
||||||
let regressionLine = null, slope = 0, intercept = 0;
|
let regressionLine = null, slope = 0, intercept = 0;
|
||||||
if (n >= 2) {
|
if (n >= 2) {
|
||||||
let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
|
let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
|
||||||
validPoints.forEach(d => {
|
validPoints.forEach(d => {
|
||||||
sumX += Math.log10(d.x);
|
sumX += d.x;
|
||||||
sumY += d.y;
|
sumY += d.y;
|
||||||
sumXY += Math.log10(d.x) * d.y;
|
sumXY += d.x * d.y;
|
||||||
sumXX += Math.log10(d.x) * Math.log10(d.x);
|
sumXX += d.x * d.x;
|
||||||
});
|
});
|
||||||
const meanX = sumX / n;
|
const meanX = sumX / n;
|
||||||
const meanY = sumY / n;
|
const meanY = sumY / n;
|
||||||
|
@ -65,8 +66,8 @@ function waitForChartAndDrawBubble() {
|
||||||
const xMin = Math.min(...validPoints.map(d => d.x));
|
const xMin = Math.min(...validPoints.map(d => d.x));
|
||||||
const xMax = Math.max(...validPoints.map(d => d.x));
|
const xMax = Math.max(...validPoints.map(d => d.x));
|
||||||
regressionLine = [
|
regressionLine = [
|
||||||
{ x: xMin, y: slope * Math.log10(xMin) + intercept },
|
{ x: xMin, y: slope * xMin + intercept },
|
||||||
{ x: xMax, y: slope * Math.log10(xMax) + intercept }
|
{ x: xMax, y: slope * xMax + intercept }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
window.Chart.register(window.ChartDataLabels);
|
window.Chart.register(window.ChartDataLabels);
|
||||||
|
@ -105,10 +106,8 @@ function waitForChartAndDrawBubble() {
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
// responsive: true,
|
|
||||||
plugins: {
|
plugins: {
|
||||||
datalabels: {
|
datalabels: {
|
||||||
// Désactivé au niveau global, activé par dataset
|
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
legend: { display: true },
|
legend: { display: true },
|
||||||
|
@ -117,14 +116,14 @@ function waitForChartAndDrawBubble() {
|
||||||
label: (context) => {
|
label: (context) => {
|
||||||
const d = context.raw;
|
const d = context.raw;
|
||||||
if (context.dataset.type === 'line') {
|
if (context.dataset.type === 'line') {
|
||||||
return `Régression: y = ${slope.toFixed(2)} × log10(x) + ${intercept.toFixed(2)}`;
|
return `Régression: y = ${slope.toFixed(2)} × x + ${intercept.toFixed(2)}`;
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
`${d.label}`,
|
`${d.label}`,
|
||||||
`Population: ${d.x.toLocaleString()}`,
|
|
||||||
`Nombre de lieux: ${d.r.toFixed(2)}`,
|
|
||||||
`Complétion: ${d.y.toFixed(2)}%`,
|
|
||||||
`Fraîcheur moyenne: ${d.freshnessDays ? d.freshnessDays.toLocaleString() + ' jours' : 'N/A'}`,
|
`Fraîcheur moyenne: ${d.freshnessDays ? d.freshnessDays.toLocaleString() + ' jours' : 'N/A'}`,
|
||||||
|
`Complétion: ${d.y.toFixed(2)}%`,
|
||||||
|
`Population: ${d.population ? d.population.toLocaleString() : 'N/A'}`,
|
||||||
|
`Nombre de lieux: ${d.r.toFixed(2)}`,
|
||||||
`Budget: ${d.budget ? d.budget.toLocaleString() + ' €' : 'N/A'}`,
|
`Budget: ${d.budget ? d.budget.toLocaleString() + ' €' : 'N/A'}`,
|
||||||
`Budget/habitant: ${d.budgetParHabitant ? d.budgetParHabitant.toFixed(2) + ' €' : 'N/A'}`,
|
`Budget/habitant: ${d.budgetParHabitant ? d.budgetParHabitant.toFixed(2) + ' €' : 'N/A'}`,
|
||||||
`Budget/lieu: ${d.budgetParLieu ? d.budgetParLieu.toFixed(2) + ' €' : 'N/A'}`
|
`Budget/lieu: ${d.budgetParLieu ? d.budgetParLieu.toFixed(2) + ' €' : 'N/A'}`
|
||||||
|
@ -135,11 +134,13 @@ function waitForChartAndDrawBubble() {
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
type: 'logarithmic',
|
type: 'linear',
|
||||||
title: { display: true, text: 'Population (échelle log)' }
|
title: { display: true, text: 'Fraîcheur moyenne (jours, plus petit = plus récent)' }
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
title: { display: true, text: 'Completion' }
|
title: { display: true, text: 'Taux de complétion (%)' },
|
||||||
|
min: 0,
|
||||||
|
max: 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
8
assets/js/table-sortable.js
Normal file
8
assets/js/table-sortable.js
Normal file
File diff suppressed because one or more lines are too long
0
assets/js/table-sortable.min.js
vendored
Normal file
0
assets/js/table-sortable.min.js
vendored
Normal file
|
@ -1,11 +1,7 @@
|
||||||
// Gestion du tri des tableaux
|
// Gestion du tri des tableaux
|
||||||
import Tablesort from 'tablesort';
|
// import Tablesort from 'tablesort';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
document.querySelectorAll('.js-sort-table').forEach(table => {
|
|
||||||
new Tablesort(table);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Gestion du toggle gouttes/ronds sur la carte
|
// Gestion du toggle gouttes/ronds sur la carte
|
||||||
const toggle = document.getElementById('toggleMarkers');
|
const toggle = document.getElementById('toggleMarkers');
|
||||||
if (toggle && window.updateMarkers) {
|
if (toggle && window.updateMarkers) {
|
||||||
|
@ -18,6 +14,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
// Exposer une fonction pour (ré)appliquer le tri si besoin
|
// Exposer une fonction pour (ré)appliquer le tri si besoin
|
||||||
export function applyTableSort() {
|
export function applyTableSort() {
|
||||||
document.querySelectorAll('.js-sort-table').forEach(table => {
|
document.querySelectorAll('.js-sort-table').forEach(table => {
|
||||||
new Tablesort(table);
|
new window.Tablesort(table);
|
||||||
});
|
});
|
||||||
}
|
}
|
35
migrations/Version20250624103515.php
Normal file
35
migrations/Version20250624103515.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250624103515 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE place ADD email_content LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_0900_ai_ci`, ADD place_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 place DROP email_content, DROP place_count
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
17
package-lock.json
generated
17
package-lock.json
generated
|
@ -5,6 +5,9 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
|
"dependencies": {
|
||||||
|
"jquery": "^3.7.1"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.0",
|
"@babel/core": "^7.17.0",
|
||||||
"@babel/preset-env": "^7.16.0",
|
"@babel/preset-env": "^7.16.0",
|
||||||
|
@ -14,6 +17,7 @@
|
||||||
"core-js": "^3.38.0",
|
"core-js": "^3.38.0",
|
||||||
"maplibre-gl": "^5.6.0",
|
"maplibre-gl": "^5.6.0",
|
||||||
"regenerator-runtime": "^0.13.9",
|
"regenerator-runtime": "^0.13.9",
|
||||||
|
"table-sort-js": "^1.22.2",
|
||||||
"tablesort": "^5.6.0",
|
"tablesort": "^5.6.0",
|
||||||
"webpack": "^5.74.0",
|
"webpack": "^5.74.0",
|
||||||
"webpack-cli": "^5.1.0"
|
"webpack-cli": "^5.1.0"
|
||||||
|
@ -3904,6 +3908,12 @@
|
||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jquery": {
|
||||||
|
"version": "3.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||||
|
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
@ -5491,6 +5501,13 @@
|
||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/table-sort-js": {
|
||||||
|
"version": "1.22.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/table-sort-js/-/table-sort-js-1.22.2.tgz",
|
||||||
|
"integrity": "sha512-KUpmoYWH1TCnyiylE0HMCtMeAisl0KYBFjZfBL3CPHOlnhA8jy+RFfZbH6DwCpXAvmK73vsDAX54hg9J4DhuRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tablesort": {
|
"node_modules/tablesort": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.6.0.tgz",
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"core-js": "^3.38.0",
|
"core-js": "^3.38.0",
|
||||||
"maplibre-gl": "^5.6.0",
|
"maplibre-gl": "^5.6.0",
|
||||||
"regenerator-runtime": "^0.13.9",
|
"regenerator-runtime": "^0.13.9",
|
||||||
|
"table-sort-js": "^1.22.2",
|
||||||
"tablesort": "^5.6.0",
|
"tablesort": "^5.6.0",
|
||||||
"webpack": "^5.74.0",
|
"webpack": "^5.74.0",
|
||||||
"webpack-cli": "^5.1.0"
|
"webpack-cli": "^5.1.0"
|
||||||
|
@ -19,5 +20,8 @@
|
||||||
"dev": "encore dev",
|
"dev": "encore dev",
|
||||||
"watch": "encore dev --watch",
|
"watch": "encore dev --watch",
|
||||||
"build": "encore production --progress"
|
"build": "encore production --progress"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jquery": "^3.7.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||||
use function uuid_create;
|
use function uuid_create;
|
||||||
use Symfony\Component\Filesystem\Filesystem;
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Twig\Environment;
|
||||||
|
|
||||||
final class AdminController extends AbstractController
|
final class AdminController extends AbstractController
|
||||||
{
|
{
|
||||||
|
@ -23,7 +24,8 @@ final class AdminController extends AbstractController
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private EntityManagerInterface $entityManager,
|
private EntityManagerInterface $entityManager,
|
||||||
private Motocultrice $motocultrice,
|
private Motocultrice $motocultrice,
|
||||||
private BudgetService $budgetService
|
private BudgetService $budgetService,
|
||||||
|
private Environment $twig
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +101,10 @@ final class AdminController extends AbstractController
|
||||||
$this->entityManager->persist($place);
|
$this->entityManager->persist($place);
|
||||||
$stats->addPlace($place);
|
$stats->addPlace($place);
|
||||||
$processedCount++;
|
$processedCount++;
|
||||||
|
|
||||||
|
// Générer le contenu de l'email avec le template
|
||||||
|
$emailContent = $this->twig->render('admin/email_content.html.twig', ['place' => $place]);
|
||||||
|
$place->setEmailContent($emailContent);
|
||||||
} elseif ($updateExisting) {
|
} elseif ($updateExisting) {
|
||||||
// Mettre à jour les données depuis Overpass uniquement si updateExisting est true
|
// Mettre à jour les données depuis Overpass uniquement si updateExisting est true
|
||||||
$existingPlace->update_place_from_overpass_data($placeData);
|
$existingPlace->update_place_from_overpass_data($placeData);
|
||||||
|
@ -443,6 +449,8 @@ final class AdminController extends AbstractController
|
||||||
|
|
||||||
$overpass_osm_ids = array_map(fn($place) => $place['id'], $places_overpass);
|
$overpass_osm_ids = array_map(fn($place) => $place['id'], $places_overpass);
|
||||||
|
|
||||||
|
$batchSize = 200;
|
||||||
|
$i = 0;
|
||||||
foreach ($places_overpass as $placeData) {
|
foreach ($places_overpass as $placeData) {
|
||||||
// Vérifier si le lieu existe déjà (optimisé)
|
// Vérifier si le lieu existe déjà (optimisé)
|
||||||
$existingPlace = $placesByOsmId[$placeData['id']] ?? null;
|
$existingPlace = $placesByOsmId[$placeData['id']] ?? null;
|
||||||
|
@ -471,6 +479,10 @@ final class AdminController extends AbstractController
|
||||||
$this->entityManager->persist($place);
|
$this->entityManager->persist($place);
|
||||||
$stats->addPlace($place);
|
$stats->addPlace($place);
|
||||||
$processedCount++;
|
$processedCount++;
|
||||||
|
|
||||||
|
// Générer le contenu de l'email avec le template
|
||||||
|
$emailContent = $this->twig->render('admin/email_content.html.twig', ['place' => $place]);
|
||||||
|
$place->setEmailContent($emailContent);
|
||||||
} elseif ($updateExisting) {
|
} elseif ($updateExisting) {
|
||||||
$existingPlace->setDead(false);
|
$existingPlace->setDead(false);
|
||||||
$existingPlace->update_place_from_overpass_data($placeData);
|
$existingPlace->update_place_from_overpass_data($placeData);
|
||||||
|
@ -478,19 +490,19 @@ final class AdminController extends AbstractController
|
||||||
$this->entityManager->persist($existingPlace);
|
$this->entityManager->persist($existingPlace);
|
||||||
$updatedCount++;
|
$updatedCount++;
|
||||||
}
|
}
|
||||||
}
|
$i++;
|
||||||
|
// Flush/clear Doctrine tous les X lieux pour éviter l'explosion mémoire
|
||||||
// Supprimer les lieux qui ne sont plus dans la réponse Overpass, si activé
|
if (($i % $batchSize) === 0) {
|
||||||
if ($deleteMissing) {
|
$this->entityManager->flush();
|
||||||
$db_places = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $insee_code]);
|
$this->entityManager->clear();
|
||||||
foreach ($db_places as $db_place) {
|
// Recharger les stats après clear
|
||||||
if (!in_array($db_place->getOsmId(), $overpass_osm_ids)) {
|
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
|
||||||
$this->entityManager->remove($db_place);
|
|
||||||
$deletedCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Flush final
|
||||||
|
$this->entityManager->flush();
|
||||||
|
$this->entityManager->clear();
|
||||||
|
|
||||||
// Récupérer tous les commerces de la zone qui n'ont pas été supprimés
|
// Récupérer tous les commerces de la zone qui n'ont pas été supprimés
|
||||||
$commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $insee_code]);
|
$commerces = $this->entityManager->getRepository(Place::class)->findBy(['zip_code' => $insee_code]);
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,12 @@ class Place
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
private ?int $osm_changeset = null;
|
private ?int $osm_changeset = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::TEXT, nullable: true, options: ['charset' => 'utf8mb4'])]
|
||||||
|
private ?string $emailContent = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::INTEGER, nullable: true)]
|
||||||
|
private ?int $place_count = null;
|
||||||
|
|
||||||
public function getPlaceTypeName(): ?string
|
public function getPlaceTypeName(): ?string
|
||||||
{
|
{
|
||||||
if ($this->main_tag == 'amenity=restaurant') {
|
if ($this->main_tag == 'amenity=restaurant') {
|
||||||
|
@ -734,4 +740,15 @@ class Place
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEmailContent(): ?string
|
||||||
|
{
|
||||||
|
return $this->emailContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setEmailContent(?string $emailContent): static
|
||||||
|
{
|
||||||
|
$this->emailContent = $emailContent;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,16 +252,21 @@ out meta;';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_city_osm_from_zip_code($zip_code) {
|
public function get_city_osm_from_zip_code($zip_code) {
|
||||||
|
// Détection spéciale pour Paris, Lyon, Marseille
|
||||||
|
if (preg_match('/^75(0[1-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|6[0-9]|7[0-9]|8[0-9]|9[0-9])$/', $zip_code)) {
|
||||||
|
$arr = intval(substr($zip_code, 2, 3));
|
||||||
|
return 'Paris ' . $arr . 'e arr.';
|
||||||
|
}
|
||||||
|
if (preg_match('/^69(0[1-9]|1[0-9]|2[0-9])$/', $zip_code)) {
|
||||||
|
$arr = intval(substr($zip_code, 2, 3));
|
||||||
|
return 'Lyon ' . $arr . 'e arr.';
|
||||||
|
}
|
||||||
|
if (preg_match('/^13(0[1-9]|1[0-6])$/', $zip_code)) {
|
||||||
|
$arr = intval(substr($zip_code, 2, 3));
|
||||||
|
return 'Marseille ' . $arr . 'e arr.';
|
||||||
|
}
|
||||||
// Requête Overpass pour obtenir la zone administrative de niveau 8 avec un nom
|
// Requête Overpass pour obtenir la zone administrative de niveau 8 avec un nom
|
||||||
$query = "[out:json][timeout:25];
|
$query = "[out:json][timeout:25];\n area[\"ref:INSEE\"=\"{$zip_code}\"]->.searchArea;\n (\n relation[\"admin_level\"=\"8\"][\"name\"][\"type\"=\"boundary\"][\"boundary\"=\"administrative\"](area.searchArea);\n );\n out body;\n >;\n out skel qt;";
|
||||||
area[\"ref:INSEE\"=\"{$zip_code}\"]->.searchArea;
|
|
||||||
(
|
|
||||||
relation[\"admin_level\"=\"8\"][\"name\"][\"type\"=\"boundary\"][\"boundary\"=\"administrative\"](area.searchArea);
|
|
||||||
);
|
|
||||||
out body;
|
|
||||||
>;
|
|
||||||
out skel qt;";
|
|
||||||
|
|
||||||
$response = $this->client->request('POST', $this->overpassApiUrl, [
|
$response = $this->client->request('POST', $this->overpassApiUrl, [
|
||||||
'body' => ['data' => $query]
|
'body' => ['data' => $query]
|
||||||
]);
|
]);
|
||||||
|
@ -389,10 +394,6 @@ out meta;';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function uuid_create_static() {
|
|
||||||
return $this->uuid_create();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uuid_create() {
|
public function uuid_create() {
|
||||||
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||||
// 32 bits for "time_low"
|
// 32 bits for "time_low"
|
||||||
|
|
|
@ -28,5 +28,7 @@ En vous souhaitant une bonne journée.
|
||||||
<br>
|
<br>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
{% if place.id %}
|
||||||
<a href="{{ path('app_admin_commerce', {'id': place.id}) }}">Ne plus être sollicité pour mettre à jour mon commerce</a>
|
<a href="{{ path('app_admin_commerce', {'id': place.id}) }}">Ne plus être sollicité pour mettre à jour mon commerce</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
|
@ -25,7 +25,7 @@ commerces existants déjà en base: {{ commerces|length }}
|
||||||
</p>
|
</p>
|
||||||
{# {{ dump(commerces[0]) }} #}
|
{# {{ dump(commerces[0]) }} #}
|
||||||
|
|
||||||
<table class="table table-striped js-sort-table">
|
<table class="table table-striped table-sort">
|
||||||
{% include 'admin/stats/table-head.html.twig' %}
|
{% include 'admin/stats/table-head.html.twig' %}
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
{{ stats.name }} - {{ stats.completionPercent }}% complété</h1>
|
{{ stats.name }} - {{ stats.completionPercent }}% complété</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<a href="{{ path('app_admin_labourer', {'insee_code': stats.zone, 'deleteMissing': 0}) }}" class="btn btn-primary" id="labourer">Labourer les mises à jour</a>
|
<a href="{{ path('app_admin_labourer', {'insee_code': stats.zone, 'deleteMissing': 1}) }}" class="btn btn-primary" id="labourer">Labourer les mises à jour</a>
|
||||||
<button id="openInJOSM" class="btn btn-secondary ms-2">
|
<button id="openInJOSM" class="btn btn-secondary ms-2">
|
||||||
<i class="bi bi-map"></i> Ouvrir dans JOSM
|
<i class="bi bi-map"></i> Ouvrir dans JOSM
|
||||||
</button>
|
</button>
|
||||||
|
@ -262,7 +262,8 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-bordered table-striped table-hover table-responsive js-sort-table">
|
<input type="text" id="stats-table-search" class="form-control mb-2" placeholder="Filtrer les lieux...">
|
||||||
|
<table id="stats-table" class="table table-bordered table-striped table-hover table-responsive table-sort">
|
||||||
{% include 'admin/stats/table-head.html.twig' %}
|
{% include 'admin/stats/table-head.html.twig' %}
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for commerce in stats.places %}
|
{% for commerce in stats.places %}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<h1>Commerces fermés</h1>
|
<h1>Commerces fermés</h1>
|
||||||
<p>Voici la liste des commerces fermés :</p>
|
<p>Voici la liste des commerces fermés :</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped js-sort-table">
|
<table class="table table-striped table-sort">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom du commerce</th>
|
<th>Nom du commerce</th>
|
||||||
|
|
|
@ -137,8 +137,9 @@
|
||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h2>Statistiques par ville</h2>
|
<h2>Statistiques par ville</h2>
|
||||||
|
<input type="text" id="dashboard-table-search" class="form-control mb-2" placeholder="Filtrer les villes...">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped js-sort-table">
|
<table id="dashboard-table" class="table table-striped table-sort">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Ville</th>
|
<th>Ville</th>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-6 ">
|
<div class="col-12 col-md-6 ">
|
||||||
<h2>Lieux modifiés</h2>
|
<h2>Lieux modifiés</h2>
|
||||||
<table class="table table-striped table-hover table-responsive js-sort-table">
|
<table class="table table-striped table-hover table-responsive table-sort">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom</th>
|
<th>Nom</th>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6 ">
|
<div class="col-12 col-md-6 ">
|
||||||
<h2>Lieux affichés</h2>
|
<h2>Lieux affichés</h2>
|
||||||
<table class="table table-striped table-hover table-responsive js-sort-table">
|
<table class="table table-striped table-hover table-responsive table-sort">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom</th>
|
<th>Nom</th>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Commerces avec une note</h1>
|
<h1>Commerces avec une note</h1>
|
||||||
<table class="table table-striped js-sort-table table-hover table-responsive">
|
<table class="table table-striped table-hover table-responsive table-sort">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom</th>
|
<th>Nom</th>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue