add history for zone command

This commit is contained in:
Tykayn 2025-11-26 17:28:34 +01:00 committed by tykayn
parent 2b07e6a4c6
commit 45df535a35
12 changed files with 1071 additions and 854 deletions

View file

@ -5,6 +5,16 @@
{% block stylesheets %}
{{ parent() }}
<link href='{{ asset('css/city-sidebar.css') }}' rel='stylesheet'/>
<style>
/* Colonnes de thème à largeur régulière dans les tableaux ZonePlaces */
.zoneplaces-table th.theme-col,
.zoneplaces-table td.theme-col {
width: 180px;
min-width: 180px;
max-width: 180px;
white-space: nowrap;
}
</style>
{% endblock %}
{% block body %}
@ -19,7 +29,32 @@
<div class="col-md-9 col-lg-10 main-content">
<div class="p-4">
<h1>Historique ZonePlaces - {{ stats.name }} ({{ stats.zone }})</h1>
<p class="text-muted">Historique combiné des suppressions et créations d'objets OSM par thème, groupé par date.</p>
<p class="text-muted">
Historique combiné des suppressions et créations d'objets OSM par thème, groupé par date.
</p>
{# Sélecteur de période (pagination temporelle) #}
<div class="mb-3">
<label class="form-label"><strong>Période affichée :</strong></label>
<div class="btn-group" role="group" aria-label="Période d'historique">
<a href="{{ path('app_public_zone_places_history', { insee_code: stats.zone, period: '3m' }) }}"
class="btn btn-outline-primary {{ period is defined and period == '3m' ? 'active' : (period is not defined ? 'active' : '') }}">
3 derniers mois
</a>
<a href="{{ path('app_public_zone_places_history', { insee_code: stats.zone, period: '6m' }) }}"
class="btn btn-outline-primary {{ period is defined and period == '6m' ? 'active' : '' }}">
6 derniers mois
</a>
<a href="{{ path('app_public_zone_places_history', { insee_code: stats.zone, period: '12m' }) }}"
class="btn btn-outline-primary {{ period is defined and period == '12m' ? 'active' : '' }}">
12 derniers mois
</a>
<a href="{{ path('app_public_zone_places_history', { insee_code: stats.zone, period: 'all' }) }}"
class="btn btn-outline-secondary {{ period is defined and period == 'all' ? 'active' : '' }}">
Tout l'historique
</a>
</div>
</div>
{% if changesByDate is empty %}
<div class="alert alert-info">
@ -86,14 +121,23 @@
</div>
</div>
{# Graphique des créations et suppressions #}
{# Graphiques des créations et suppressions #}
{% if chart_data is defined and chart_data.dates|length > 0 %}
<div class="card mb-4">
<div class="card-header">
<h5><i class="bi bi-graph-up"></i> Évolution des créations et suppressions</h5>
<h5><i class="bi bi-graph-up"></i> Évolution des créations</h5>
</div>
<div class="card-body">
<canvas id="changesChart" style="max-height: 400px;"></canvas>
<canvas id="creationsChart" style="max-height: 300px;"></canvas>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<h5><i class="bi bi-graph-down"></i> Évolution des suppressions</h5>
</div>
<div class="card-body">
<canvas id="deletionsChart" style="max-height: 300px;"></canvas>
</div>
</div>
{% endif %}
@ -114,10 +158,10 @@
<div class="mb-3 theme-section" data-theme="{{ theme }}">
<h5><i class="bi {{ themeIcon }}"></i> {{ themeLabel }} ({{ objects|length }} suppression{{ objects|length > 1 ? 's' : '' }})</h5>
<div class="table-responsive">
<table class="table table-sm table-hover">
<table class="table table-sm table-hover zoneplaces-table">
<thead>
<tr>
<th>Thème</th>
<th class="theme-col">Thème</th>
<th>Type</th>
<th>ID</th>
<th>Contributeur</th>
@ -130,7 +174,7 @@
<tbody>
{% for obj in objects %}
<tr data-theme="{{ theme }}" data-change-type="deletions" data-user="{{ obj.user|default('')|e('html_attr') }}">
<td><span class="badge bg-info">{{ themeLabel }}</span></td>
<td class="theme-col"><span class="badge bg-info">{{ themeLabel }}</span></td>
<td><span class="badge bg-secondary">{{ obj.type|upper }}</span></td>
<td><code>{{ obj.id }}</code></td>
<td>
@ -204,10 +248,10 @@
<div class="mb-3 theme-section" data-theme="{{ theme }}">
<h5><i class="bi {{ themeIcon }}"></i> {{ themeLabel }} ({{ objects|length }} objet{{ objects|length > 1 ? 's' : '' }})</h5>
<div class="table-responsive">
<table class="table table-sm table-hover">
<table class="table table-sm table-hover zoneplaces-table">
<thead>
<tr>
<th>Thème</th>
<th class="theme-col">Thème</th>
<th>Type</th>
<th>ID</th>
<th>Contributeur</th>
@ -219,7 +263,7 @@
<tbody>
{% for obj in objects %}
<tr data-theme="{{ theme }}" data-change-type="creations" data-user="{{ obj.user|default('')|e('html_attr') }}">
<td><span class="badge bg-info">{{ themeLabel }}</span></td>
<td class="theme-col"><span class="badge bg-info">{{ themeLabel }}</span></td>
<td><span class="badge bg-success">{{ obj.type|upper }}</span></td>
<td><code>{{ obj.id }}</code></td>
<td>
@ -414,7 +458,7 @@
// Initialiser les filtres
applyFilters();
// Créer le graphique des créations et suppressions
// Créer les graphiques des créations et suppressions
{% if chart_data is defined and chart_data.dates|length > 0 %}
{% set hasCompletionData = false %}
{% for comp in chart_data.completion %}
@ -422,19 +466,11 @@
{% set hasCompletionData = true %}
{% endif %}
{% endfor %}
const chartCanvas = document.getElementById('changesChart');
if (chartCanvas) {
const chartData = {
const creationsCanvas = document.getElementById('creationsChart');
if (creationsCanvas) {
const creationsData = {
labels: {{ chart_data.dates|json_encode|raw }},
datasets: [
{
label: 'Suppressions',
data: {{ chart_data.deletions|json_encode|raw }},
borderColor: 'rgb(220, 53, 69)',
backgroundColor: 'rgba(220, 53, 69, 0.1)',
tension: 0.4,
fill: true
},
{
label: 'Créations',
data: {{ chart_data.creations|json_encode|raw }},
@ -458,9 +494,9 @@
]
};
new Chart(chartCanvas, {
new Chart(creationsCanvas, {
type: 'line',
data: chartData,
data: creationsData,
options: {
responsive: true,
maintainAspectRatio: true,
@ -532,6 +568,77 @@
}
});
}
const deletionsCanvas = document.getElementById('deletionsChart');
if (deletionsCanvas) {
const deletionsData = {
labels: {{ chart_data.dates|json_encode|raw }},
datasets: [
{
label: 'Suppressions',
data: {{ chart_data.deletions|json_encode|raw }},
borderColor: 'rgb(220, 53, 69)',
backgroundColor: 'rgba(220, 53, 69, 0.1)',
tension: 0.4,
fill: true
}
]
};
new Chart(deletionsCanvas, {
type: 'line',
data: deletionsData,
options: {
responsive: true,
maintainAspectRatio: true,
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
legend: {
display: true,
position: 'top',
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += context.parsed.y;
} else {
label += 'N/A';
}
return label;
}
}
}
},
scales: {
x: {
display: true,
title: {
display: true,
text: 'Date'
}
},
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: 'Nombre d\'objets'
},
beginAtZero: true
}
}
}
});
}
{% endif %}
// Convertir les dates en format relatif