harmoniser les décomptes de completion sur la page stats
This commit is contained in:
parent
b9f57e48b5
commit
060b23f87e
3 changed files with 399 additions and 345 deletions
|
@ -303,11 +303,11 @@ final class AdminController extends AbstractController
|
|||
return $this->redirectToRoute('app_admin_import_stats');
|
||||
}
|
||||
|
||||
$completion = $stats->getCompletionPercent();
|
||||
if (!$completion) {
|
||||
$stats->computeCompletionPercent();
|
||||
$completion = $stats->getCompletionPercent();
|
||||
}
|
||||
// $completion = $stats->getCompletionPercent();
|
||||
// if (!$completion) {
|
||||
// $stats->computeCompletionPercent();
|
||||
// $completion = $stats->getCompletionPercent();
|
||||
// }
|
||||
$followups = $stats->getCityFollowUps();
|
||||
$refresh = false;
|
||||
if (!$followups->isEmpty()) {
|
||||
|
@ -378,7 +378,7 @@ final class AdminController extends AbstractController
|
|||
|
||||
// Update the places_count property
|
||||
$stats->setPlacesCount($stats->getPlaces()->count());
|
||||
$this->entityManager->persist($stats);
|
||||
// $this->entityManager->persist($stats);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
@ -433,6 +433,15 @@ final class AdminController extends AbstractController
|
|||
if (isset($commerces) && is_iterable($commerces)) {
|
||||
foreach ($commerces as $commerce) {
|
||||
if ($commerce->getLat() && $commerce->getLon()) {
|
||||
// Collect missing tags
|
||||
$missingTags = [];
|
||||
if (!$commerce->getName()) $missingTags[] = 'name';
|
||||
if (!$commerce->hasAddress()) $missingTags[] = 'address';
|
||||
if (!$commerce->hasOpeningHours()) $missingTags[] = 'opening_hours';
|
||||
if (!$commerce->hasWebsite()) $missingTags[] = 'website';
|
||||
if (!$commerce->hasWheelchair()) $missingTags[] = 'wheelchair';
|
||||
if (!$commerce->getSiret()) $missingTags[] = 'siret';
|
||||
|
||||
$geojson['features'][] = [
|
||||
'type' => 'Feature',
|
||||
'geometry' => [
|
||||
|
@ -445,7 +454,9 @@ final class AdminController extends AbstractController
|
|||
'main_tag' => $commerce->getMainTag(),
|
||||
'address' => $commerce->getStreet() . ' ' . $commerce->getHousenumber(),
|
||||
'note' => $commerce->getNoteContent(),
|
||||
'osm_url' => 'https://www.openstreetmap.org/' . $commerce->getOsmKind() . '/' . $commerce->getOsmId()
|
||||
'osm_url' => 'https://www.openstreetmap.org/' . $commerce->getOsmKind() . '/' . $commerce->getOsmId(),
|
||||
'completion' => $commerce->getCompletionPercentage(),
|
||||
'missing_tags' => $missingTags
|
||||
]
|
||||
];
|
||||
}
|
||||
|
@ -1955,8 +1966,24 @@ final class AdminController extends AbstractController
|
|||
}
|
||||
unset($points);
|
||||
|
||||
|
||||
$now = new \DateTime();
|
||||
$last_week = new \DateTime();
|
||||
$last_week->sub(new \DateInterval('P7D'));
|
||||
|
||||
$adiff_query = '[timeout:60]
|
||||
[adiff:"' . $now->format('Y-m-d') . 'T' . $now->format('H:i:s') . 'Z","2025-08-05T00:00:00Z"][out:xml];
|
||||
area["ref:INSEE"="91111"]->.searchArea;
|
||||
(
|
||||
nwr(area.searchArea);
|
||||
);
|
||||
out meta;';
|
||||
|
||||
return $this->render('admin/followup_graph.html.twig', [
|
||||
'adiff_query' => $adiff_query,
|
||||
'stats' => $stats,
|
||||
|
||||
|
||||
'completion_tags' => \App\Service\FollowUpService::getFollowUpCompletionTags(),
|
||||
'followup_labels' => \App\Service\FollowUpService::getFollowUpThemes(),
|
||||
'followup_icons' => \App\Service\FollowUpService::getFollowUpIcons(),
|
||||
|
|
|
@ -23,10 +23,12 @@
|
|||
<a href="{{ path('admin_followup', {'insee_code': stats.zone}) }}" class="btn btn-warning">
|
||||
<i class="bi bi-arrow-repeat"></i> Mettre à jour les suivis (followup)
|
||||
</a>
|
||||
<a href="{{ path('app_admin_labourer', {'insee_code': stats.zone, 'deleteMissing': 1}) }}" class="btn btn-primary">
|
||||
<a href="{{ path('app_admin_labourer', {'insee_code': stats.zone, 'deleteMissing': 1}) }}"
|
||||
class="btn btn-primary">
|
||||
<i class="bi bi-shovel"></i> Labourer la zone
|
||||
</a>
|
||||
<a href="{{ path('app_admin_labourer', {'insee_code': stats.zone, 'deleteMissing': 1, 'disableFollowUpCleanup': 1}) }}" class="btn btn-warning" title="Labourer sans nettoyer les suivis OSM">
|
||||
<a href="{{ path('app_admin_labourer', {'insee_code': stats.zone, 'deleteMissing': 1, 'disableFollowUpCleanup': 1}) }}"
|
||||
class="btn btn-warning" title="Labourer sans nettoyer les suivis OSM">
|
||||
<i class="bi bi-shield-check"></i> Labourer (sans nettoyage)
|
||||
</a>
|
||||
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}" class="btn btn-info">
|
||||
|
@ -40,7 +42,15 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Thème</th>
|
||||
<th>Évolution 7j</th>
|
||||
<th>
|
||||
{# <pre> #}
|
||||
|
||||
{# {{ dump(adiff_query ) }} #}
|
||||
{# </pre> #}
|
||||
{# <a href="https://overpass-turbo.eu/?Q={{ adiff_query|url_encode }}" target="_blank" #}
|
||||
{# title="Voir les changements des 7 derniers jours sur Overpass Turbo">Évolution 7j #}
|
||||
{# <i class="bi bi-box-arrow-up-right"></i></a> #}
|
||||
</th>
|
||||
<th>Évolution 30j</th>
|
||||
<th>Évolution 6 mois</th>
|
||||
</tr>
|
||||
|
@ -52,7 +62,8 @@
|
|||
{% set has_change = true %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}" class="fw-bold text-decoration-none">
|
||||
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}"
|
||||
class="fw-bold text-decoration-none">
|
||||
{{ tag_emoji(type) }} {{ diff.label }}
|
||||
</a>
|
||||
</td>
|
||||
|
@ -64,7 +75,8 @@
|
|||
{% else %}
|
||||
<i class="bi bi-arrow-right text-secondary"></i>
|
||||
{% endif %}
|
||||
{{ diff.count_diff_7j > 0 ? '+' ~ diff.count_diff_7j : diff.count_diff_7j }} objets
|
||||
{{ diff.count_diff_7j > 0 ? '+' ~ diff.count_diff_7j : diff.count_diff_7j }}
|
||||
objets
|
||||
<br>
|
||||
<span class="small text-muted">Complétion :
|
||||
{% if diff.completion_diff_7j > 0 %}
|
||||
|
@ -85,7 +97,8 @@
|
|||
{% else %}
|
||||
<i class="bi bi-arrow-right text-secondary"></i>
|
||||
{% endif %}
|
||||
{{ diff.count_diff_30j > 0 ? '+' ~ diff.count_diff_30j : diff.count_diff_30j }} objets
|
||||
{{ diff.count_diff_30j > 0 ? '+' ~ diff.count_diff_30j : diff.count_diff_30j }}
|
||||
objets
|
||||
<br>
|
||||
<span class="small text-muted">Complétion :
|
||||
{% if diff.completion_diff_30j > 0 %}
|
||||
|
@ -106,7 +119,8 @@
|
|||
{% else %}
|
||||
<i class="bi bi-arrow-right text-secondary"></i>
|
||||
{% endif %}
|
||||
{{ diff.count_diff_6mois > 0 ? '+' ~ diff.count_diff_6mois : diff.count_diff_6mois }} objets
|
||||
{{ diff.count_diff_6mois > 0 ? '+' ~ diff.count_diff_6mois : diff.count_diff_6mois }}
|
||||
objets
|
||||
<br>
|
||||
<span class="small text-muted">Complétion :
|
||||
{% if diff.completion_diff_6mois > 0 %}
|
||||
|
@ -123,26 +137,37 @@
|
|||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not has_change %}
|
||||
<tr><td colspan="4" class="text-muted">Aucun changement significatif cette semaine.</td></tr>
|
||||
<tr>
|
||||
<td colspan="4" class="text-muted">Aucun changement significatif cette semaine.</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% for type, label in followup_labels %}
|
||||
<h2 id="title-{{ type }}"><i class="bi {{ followup_icons[type]|default('bi-question-circle') }} fs-2"></i> {{ label }}</h2>
|
||||
<h2 id="title-{{ type }}"><i
|
||||
class="bi {{ followup_icons[type]|default('bi-question-circle') }} fs-2"></i> {{ label }}
|
||||
</h2>
|
||||
<canvas id="{{ type }}Chart" width="600" height="400"></canvas>
|
||||
<div class="mb-3">
|
||||
{% set overpass_query = '[out:json][timeout:60];\narea["ref:INSEE"="' ~ stats.zone ~ '"]->.searchArea;\n(' ~ followup_overpass[type]|default('') ~ ');\n\n(._;>;);\n\nout meta;\n>;' %}
|
||||
<a href="https://overpass-turbo.eu/?Q={{ overpass_query|url_encode }}" target="_blank" class="btn btn-sm btn-outline-primary me-2">
|
||||
<a href="https://overpass-turbo.eu/?Q={{ overpass_query|url_encode }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary me-2">
|
||||
<i class="bi bi-geo"></i> Voir sur Overpass Turbo
|
||||
</a>
|
||||
<a href="http://127.0.0.1:8111/import?url=https://overpass-api.de/api/interpreter?data={{ overpass_query|url_encode }}" target="_blank" class="btn btn-sm btn-outline-success">
|
||||
<a href="http://127.0.0.1:8111/import?url=https://overpass-api.de/api/interpreter?data={{ overpass_query|url_encode }}"
|
||||
target="_blank" class="btn btn-sm btn-outline-success">
|
||||
<i class="bi bi-box-arrow-in-up-right"></i> Ouvrir dans JOSM
|
||||
</a>
|
||||
<a href="{{ path('admin_followup_embed_graph', {'insee_code': stats.zone, 'theme': type}) }}" target="_blank" class="btn btn-sm btn-outline-secondary ms-2">
|
||||
<a href="{{ path('admin_followup_embed_graph', {'insee_code': stats.zone, 'theme': type}) }}"
|
||||
target="_blank" class="btn btn-sm btn-outline-secondary ms-2">
|
||||
<i class="bi bi-code-slash"></i> Version embarquée
|
||||
</a>
|
||||
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}"
|
||||
target="_blank" class="btn btn-sm btn-outline-secondary ms-2">
|
||||
<i class="bi bi-graph-up-arrow"></i> Détails
|
||||
</a>
|
||||
</div>
|
||||
{% include 'admin/_followup_completion_tags.html.twig' with {
|
||||
'completion_tags': completion_tags,
|
||||
|
@ -175,7 +200,8 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}" class="btn btn-secondary mt-3"><i class="bi bi-arrow-left"></i> Retour à la fiche ville</a>
|
||||
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}"
|
||||
class="btn btn-secondary mt-3"><i class="bi bi-arrow-left"></i> Retour à la fiche ville</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -227,7 +253,9 @@
|
|||
align: 'top',
|
||||
anchor: 'end',
|
||||
display: true,
|
||||
formatter: function(value) { return value.y; },
|
||||
formatter: function (value) {
|
||||
return value.y;
|
||||
},
|
||||
font: {weight: 'bold'}
|
||||
}
|
||||
},
|
||||
|
@ -242,7 +270,9 @@
|
|||
align: 'bottom',
|
||||
anchor: 'end',
|
||||
display: true,
|
||||
formatter: function(value) { return value.y + '%'; },
|
||||
formatter: function (value) {
|
||||
return value.y + '%';
|
||||
},
|
||||
font: {weight: 'bold'}
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +304,14 @@
|
|||
scales: {
|
||||
x: {type: 'time', time: {unit: 'day'}, title: {display: true, text: 'Date'}},
|
||||
y: {beginAtZero: true, title: {display: true, text: 'Nombre'}},
|
||||
y1: { beginAtZero: true, position: 'right', title: { display: true, text: 'Complétion (%)' }, grid: { drawOnChartArea: false }, min: 0, max: 100 }
|
||||
y1: {
|
||||
beginAtZero: true,
|
||||
position: 'right',
|
||||
title: {display: true, text: 'Complétion (%)'},
|
||||
grid: {drawOnChartArea: false},
|
||||
min: 0,
|
||||
max: 100
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [ChartDataLabels]
|
||||
|
|
|
@ -150,6 +150,60 @@
|
|||
{{ stats.name }} - {{ stats.completionPercent }}% complété</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="carte" class="section-anchor">
|
||||
<h2>Carte</h2>
|
||||
<div id="maploader">
|
||||
<div class="spinner-border" role="status">
|
||||
<i class="bi bi-load bi-spin"></i>
|
||||
<span class="visually-hidden">Chargement de la carte...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end mb-2">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-primary" id="circleMarkersBtn">
|
||||
<i class="bi bi-circle"></i> Cercles
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary active" id="dropMarkersBtn">
|
||||
<i class="bi bi-geo-alt"></i> Gouttes
|
||||
</button>
|
||||
</div>
|
||||
<button id="btn-geolocate" class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-geo-alt"></i> Me localiser
|
||||
</button>
|
||||
</div>
|
||||
<div id="map" style="height: 400px; width: 100%; margin-bottom: 1rem;"></div>
|
||||
|
||||
<div id="graphiques" class="section-anchor">
|
||||
<h2>Graphiques</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<canvas id="repartition_tags" width="600" height="400"
|
||||
style="max-width:100%; margin: 20px 0;"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-calendar-event"></i> Fréquence des mises à jour par
|
||||
trimestre pour {{ stats.name }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="modificationsByQuarterChart"
|
||||
style="min-height: 250px; width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="attribution">
|
||||
<a href="https://www.openstreetmap.org/copyright">Données OpenStreetMap</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if stats.population %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 col-12">
|
||||
|
@ -466,57 +520,6 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div id="carte" class="section-anchor">
|
||||
<h2>Carte</h2>
|
||||
<div id="maploader">
|
||||
<div class="spinner-border" role="status">
|
||||
<i class="bi bi-load bi-spin"></i>
|
||||
<span class="visually-hidden">Chargement de la carte...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end mb-2">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-primary" id="circleMarkersBtn">
|
||||
<i class="bi bi-circle"></i> Cercles
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary active" id="dropMarkersBtn">
|
||||
<i class="bi bi-geo-alt"></i> Gouttes
|
||||
</button>
|
||||
</div>
|
||||
<button id="btn-geolocate" class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-geo-alt"></i> Me localiser
|
||||
</button>
|
||||
</div>
|
||||
<div id="map" style="height: 400px; width: 100%; margin-bottom: 1rem;"></div>
|
||||
|
||||
<div id="graphiques" class="section-anchor">
|
||||
<h2>Graphiques</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<canvas id="repartition_tags" width="600" height="400"
|
||||
style="max-width:100%; margin: 20px 0;"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-calendar-event"></i> Fréquence des mises à jour par
|
||||
trimestre pour {{ stats.name }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="modificationsByQuarterChart"
|
||||
style="min-height: 250px; width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="attribution">
|
||||
<a href="https://www.openstreetmap.org/copyright">Données OpenStreetMap</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mt-4">
|
||||
{% include 'admin/stats_history.html.twig' with {stat: stats} %}
|
||||
<canvas id="distribution_completion" class="mt-4 mb-4" height="400"></canvas>
|
||||
|
@ -718,23 +721,10 @@
|
|||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const geojsonData = {{ geojson|raw }};
|
||||
const map_token = "{{ maptiler_token }}";
|
||||
// Liste des tags attendus pour la complétion des lieux
|
||||
const completionTags = {{ completion_tags['places']|json_encode|raw }};
|
||||
// Calcul de la complétion et des tags manquants pour chaque lieu
|
||||
// Les valeurs de complétion et tags manquants sont maintenant calculées côté serveur
|
||||
// et incluses directement dans les données GeoJSON
|
||||
geojsonData.features.forEach(f => {
|
||||
let filled = 0;
|
||||
let missing = [];
|
||||
if (completionTags && completionTags.length > 0) {
|
||||
completionTags.forEach(tag => {
|
||||
if (f.properties && typeof f.properties[tag] !== 'undefined' && f.properties[tag] !== null && f.properties[tag] !== '') {
|
||||
filled++;
|
||||
} else {
|
||||
missing.push(tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
f.properties.completion = completionTags && completionTags.length > 0 ? Math.round(100 * filled / completionTags.length) : null;
|
||||
// Correction : toujours un tableau
|
||||
// Assurons-nous que missing_tags est toujours un tableau
|
||||
f.properties.missing_tags = Array.isArray(f.properties.missing_tags) ? f.properties.missing_tags : (f.properties.missing_tags ? [f.properties.missing_tags] : []);
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue