up stats par rue et dans le temps
This commit is contained in:
parent
cd6c14c378
commit
7355600e6b
8 changed files with 409 additions and 5 deletions
|
@ -10,6 +10,12 @@
|
|||
<td>{{ place.modifiedDate | date('Y-m-d H:i:s') }}</td>
|
||||
<td>{{ place.lastContactAttemptDate | date('Y-m-d H:i:s') }}</td>
|
||||
<td>{{ place.modifiedDate | date('Y-m-d H:i:s') }}</td>
|
||||
<td>{% if place.street %}
|
||||
<a href="{{ path('app_public_street', {'cityId': place.stats ? place.stats.zone : place.zipCode, 'streetName': place.street|url_encode }) }}">{{ place.street }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">(inconnue)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ place.zipCode }}</td>
|
||||
<td>
|
||||
<a href="https://www.openstreetmap.org/{{place.osmKind}}/{{ place.osmId }}" ><i class="bi bi-globe"></i></a>
|
||||
|
|
141
templates/public/stats_evolutions.html.twig
Normal file
141
templates/public/stats_evolutions.html.twig
Normal file
|
@ -0,0 +1,141 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Évolutions des objets - {{ stats.name }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container my-5">
|
||||
<h1>Évolutions des objets à {{ stats.name }} ({{ stats.zone }})</h1>
|
||||
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}" class="btn btn-secondary mb-3"><i class="bi bi-arrow-left"></i> Retour aux stats</a>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Variation des décomptes d'objets par type</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-striped table-hover table-responsive table-sort">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Décompte actuel</th>
|
||||
{% for p in periods %}
|
||||
<th>Évolution sur {{ p }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for type, evo in evolutions %}
|
||||
<tr>
|
||||
<td>{{ type }}</td>
|
||||
<td>{{ evo.now }}</td>
|
||||
{% for p in periods %}
|
||||
<td>{% if evo[p] is not null %}{{ evo[p] > 0 ? '+' ~ evo[p] : evo[p] }}{% else %}<span class="text-muted">-</span>{% endif %}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="text-muted">Aucune donnée d'évolution trouvée.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="col-md-4">
|
||||
<h3>Lieux modifiés cette semaine</h3>
|
||||
{% if places_7j|length > 0 %}
|
||||
<ul class="list-group mb-4">
|
||||
{% for place in places_7j %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<strong>
|
||||
<a href="{{ path('app_public_edit', {'zipcode': place.zipCode, 'name': (place.name|default('sans-nom'))|url_encode, 'uuid': place.uuidForUrl}) }}">
|
||||
{{ place.name ?: '(sans nom)' }}
|
||||
</a>
|
||||
</strong><br>
|
||||
<small>
|
||||
{% if place.street %}
|
||||
<a href="{{ path('app_public_street', {'cityId': stats.zone, 'streetName': place.street|url_encode }) }}">{{ place.street }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">(inconnue)</span>
|
||||
{% endif %}
|
||||
{{ place.housenumber }}
|
||||
</small>
|
||||
</span>
|
||||
<span>
|
||||
<span class="badge bg-primary">{{ place.getModifiedDate() ? place.getModifiedDate()|date('Y-m-d H:i') : '' }}</span>
|
||||
<a href="https://www.openstreetmap.org/{{ place.osmKind }}/{{ place.osmId }}" target="_blank" class="btn btn-outline-secondary btn-sm ms-2" title="Voir sur OSM"><i class="bi bi-globe"></i></a>
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">Aucun lieu modifié dans les 7 derniers jours.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h3>Ce mois-ci</h3>
|
||||
{% if places_30j|length > 0 %}
|
||||
<ul class="list-group mb-4">
|
||||
{% for place in places_30j %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<strong>
|
||||
<a href="{{ path('app_public_edit', {'zipcode': place.zipCode, 'name': (place.name|default('sans-nom'))|url_encode, 'uuid': place.uuidForUrl}) }}">
|
||||
{{ place.name ?: '(sans nom)' }}
|
||||
</a>
|
||||
</strong><br>
|
||||
<small>
|
||||
{% if place.street %}
|
||||
<a href="{{ path('app_public_street', {'cityId': stats.zone, 'streetName': place.street|url_encode }) }}">{{ place.street }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">(inconnue)</span>
|
||||
{% endif %}
|
||||
{{ place.housenumber }}
|
||||
</small>
|
||||
</span>
|
||||
<span>
|
||||
<span class="badge bg-primary">{{ place.getModifiedDate() ? place.getModifiedDate()|date('Y-m-d H:i') : '' }}</span>
|
||||
<a href="https://www.openstreetmap.org/{{ place.osmKind }}/{{ place.osmId }}" target="_blank" class="btn btn-outline-secondary btn-sm ms-2" title="Voir sur OSM"><i class="bi bi-globe"></i></a>
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">Aucun lieu modifié dans les 30 derniers jours.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h3>6 derniers mois</h3>
|
||||
{% if places_6mois|length > 0 %}
|
||||
<ul class="list-group mb-4">
|
||||
{% for place in places_6mois %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<strong>
|
||||
<a href="{{ path('app_public_edit', {'zipcode': place.zipCode, 'name': (place.name|default('sans-nom'))|url_encode, 'uuid': place.uuidForUrl}) }}">
|
||||
{{ place.name ?: '(sans nom)' }}
|
||||
</a>
|
||||
</strong><br>
|
||||
<small>
|
||||
{% if place.street %}
|
||||
<a href="{{ path('app_public_street', {'cityId': stats.zone, 'streetName': place.street|url_encode }) }}">{{ place.street }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">(inconnue)</span>
|
||||
{% endif %}
|
||||
{{ place.housenumber }}
|
||||
</small>
|
||||
</span>
|
||||
<span>
|
||||
<span class="badge bg-primary">{{ place.getModifiedDate() ? place.getModifiedDate()|date('Y-m-d H:i') : '' }}</span>
|
||||
<a href="https://www.openstreetmap.org/{{ place.osmKind }}/{{ place.osmId }}" target="_blank" class="btn btn-outline-secondary btn-sm ms-2" title="Voir sur OSM"><i class="bi bi-globe"></i></a>
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">Aucun lieu modifié dans les 6 derniers mois.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<script src="{{ asset('js/table-sortable.js') }}"></script>
|
||||
<script>document.querySelectorAll('table.table-sort').forEach(t => window.TableSortable && TableSortable.initTable(t));</script>
|
||||
</div>
|
||||
{% endblock %}
|
109
templates/public/street.html.twig
Normal file
109
templates/public/street.html.twig
Normal file
|
@ -0,0 +1,109 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{# {% block title %}Suivi de la rue {{ street }} à {{ city }}{% endblock %} #}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<link href='{{ asset('js/maplibre/maplibre-gl.css') }}' rel='stylesheet'/>
|
||||
<style>
|
||||
#streetMap { height: 400px; width: 100%; border-radius: 8px; margin-bottom: 2rem; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container my-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1>Rue <span class="text-primary">{{ street|e('html') }}</span> à <span class="text-success">{{ city }}</span></h1>
|
||||
<a href="{{ stats_url }}" class="btn btn-outline-info my-2"><i class="bi bi-bar-chart"></i> Voir les statistiques de la ville</a>
|
||||
</div>
|
||||
<a href="{{ path('app_public_index') }}" class="btn btn-secondary"><i class="bi bi-arrow-left"></i> Retour à l'accueil</a>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-12">
|
||||
<h3>Répartition de la complétion</h3>
|
||||
<canvas id="completionChart" height="200"></canvas>
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<h3>Carte des lieux</h3>
|
||||
<div id="streetMap" style="height: 300px; width: 100%; border-radius: 8px;"></div>
|
||||
<span class="is-info"> Si il manque des lieux dans cette rue c'est parce que les objets n'ont pas d'attribut "addr:street" et "addr:housenumber"</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="mt-4">Lieux de la rue</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-hover table-responsive table-sort">
|
||||
{% include 'admin/stats/table-head.html.twig' with {stats: {places: places, getAvecAdresse: 0, getAvecSite: 0, getAvecAccessibilite: 0, getAvecNote: 0}} %}
|
||||
<tbody>
|
||||
{% for commerce in places %}
|
||||
{% include 'admin/stats/row.html.twig' with {commerce: commerce, stats: {population: 0, places: places}} %}
|
||||
{% else %}
|
||||
<tr><td colspan="18" class="text-muted">Aucun lieu trouvé pour cette rue.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="{{ asset('js/maplibre/maplibre-gl.js') }}"></script>
|
||||
<script>
|
||||
// Graphe de complétion
|
||||
const completionData = {
|
||||
labels: {{ completion_buckets|keys|json_encode|raw }},
|
||||
datasets: [{
|
||||
label: 'Nombre de lieux',
|
||||
data: {{ completion_buckets_values|json_encode|raw }},
|
||||
backgroundColor: [
|
||||
'#e57373', '#ffb74d', '#fff176', '#81c784', '#64b5f6'
|
||||
]
|
||||
}]
|
||||
};
|
||||
new Chart(document.getElementById('completionChart'), {
|
||||
type: 'bar',
|
||||
data: completionData,
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: { y: { beginAtZero: true, precision: 0 } }
|
||||
}
|
||||
});
|
||||
|
||||
// Carte MapLibre
|
||||
const mapToken = '{{ maptiler_token }}';
|
||||
const places = {{ places_js|json_encode|raw }};
|
||||
let center = [2.35, 48.85];
|
||||
if (places.length > 0) {
|
||||
const avgLat = places.reduce((sum, p) => sum + (p.lat || 0), 0) / places.length;
|
||||
const avgLon = places.reduce((sum, p) => sum + (p.lon || 0), 0) / places.length;
|
||||
center = [avgLon, avgLat];
|
||||
}
|
||||
const map = new maplibregl.Map({
|
||||
container: 'streetMap',
|
||||
style: `https://api.maptiler.com/maps/streets/style.json?key=${mapToken}`,
|
||||
center: center,
|
||||
zoom: 16
|
||||
});
|
||||
// Couleurs selon la complétion
|
||||
function getColor(percentage) {
|
||||
if (percentage < 20) return '#e57373';
|
||||
if (percentage < 40) return '#ffb74d';
|
||||
if (percentage < 60) return '#fff176';
|
||||
if (percentage < 80) return '#81c784';
|
||||
return '#64b5f6';
|
||||
}
|
||||
places.forEach(place => {
|
||||
if (place.lat && place.lon) {
|
||||
new maplibregl.Marker({color: getColor(place.completionPercentage)})
|
||||
.setLngLat([place.lon, place.lat])
|
||||
.setPopup(new maplibregl.Popup().setHTML(`<strong>${place.name || '(sans nom)'}</strong><br>Complétion : ${place.completionPercentage}%`))
|
||||
.addTo(map);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue