presentation thèmes par ville

This commit is contained in:
Tykayn 2025-07-05 14:31:50 +02:00 committed by tykayn
parent 5188f12ad4
commit 6f3d19245e
6 changed files with 417 additions and 63 deletions

View file

@ -62,6 +62,54 @@
background: #388e3c;
border-color: #1b5e20;
}
.compact-theme-card {
transition: transform 0.2s ease-in-out;
}
.compact-theme-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1) !important;
}
.theme-title a {
font-size: 0.85rem;
line-height: 1.2;
}
.theme-stats {
font-size: 0.75rem;
color: #6c757d;
}
.theme-actions .btn {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
.theme-row-scroll {
overflow-x: auto;
white-space: nowrap;
padding-bottom: 0.5rem;
}
.theme-row-scroll .col-auto {
display: inline-block;
float: none;
}
.theme-row-scroll .compact-theme-card {
display: inline-block;
vertical-align: top;
margin-right: 8px;
}
.tab-content {
margin-top: 1.5rem;
}
.table-theme th, .table-theme td {
vertical-align: middle;
font-size: 0.95em;
}
.table-theme th {
background: #f8f9fa;
}
</style>
{% endblock %}
@ -149,70 +197,179 @@
'healthcare': 'nwr["healthcare"](area.searchArea);nwr["amenity"="doctors"](area.searchArea);nwr["amenity"="pharmacy"](area.searchArea);nwr["amenity"="hospital"](area.searchArea);nwr["amenity"="clinic"](area.searchArea);nwr["amenity"="social_facility"](area.searchArea);'
} %}
<div class="row mb-4 latestFollowups ">
{% for type, data in latestFollowups %}
{% set overpass_query = '[out:json][timeout:60];\narea["ref:INSEE"="' ~ stats.zone ~ '"]->.searchArea;\n(' ~ overpass_type_queries[type]|default('') ~ ');\n(._;>;);\nout meta;\n>;' %}
{% set completion = data.completion is defined ? data.completion.getMeasure() : null %}
{% set completion_class = '' %}
{% if completion is not null %}
{% if completion < 40 %}
{% set completion_class = 'completion-low' %}
{% elseif completion < 80 %}
{% set completion_class = 'completion-medium' %}
{% else %}
{% set completion_class = 'completion-high' %}
{% endif %}
{% endif %}
{% if data is defined and (data.count is defined or data.completion is defined) %}
<div class="col-auto mb-2">
<div class="card shadow-sm text-center" style="min-width: 140px;">
<div class="card-body p-2">
<span class="completion-badge {{ completion_class }}"></span><br>
<i class="bi {{ followup_icons[type]|default('bi-question-circle') }} fs-2 mb-1"></i><br>
<a href="http://127.0.0.1:8111/import?url=https://overpass-api.de/api/interpreter?data={{ overpass_query|url_encode }}" target="_blank" class="fw-bold text-decoration-underline text-dark" title="Charger dans JOSM">{{ followup_labels[type]|default(type|capitalize) }}</a><br>
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}" target="_blank" class="btn btn-sm btn-outline-primary mt-1" title="Voir le graphique">
<i class="bi bi-graph-up"></i>
</a><br>
<span title="Nombre"> {{ data.count is defined ? data.count.getMeasure() : '?' }}</span><br>
<span title="Complétion"> {{ completion is not null ? completion : '?' }}%</span>
{% if progression7Days[type] is defined %}
{% set countDelta = progression7Days[type].count %}
{% set completionDelta = progression7Days[type].completion %}
{% if countDelta is not null or completionDelta is not null %}
<small class="text-muted">
{% if countDelta is not null %}
<span title="Progression sur 7 jours - Nombre d'objets">
{{ countDelta > 0 ? '+' ~ countDelta : countDelta == 0 ? '0' : countDelta }}
</span>
{% set theme_groups = {
'emergency': ['fire_hydrant', 'defibrillator'],
'transport': ['bus_stop', 'charging_station'],
'healthcare': ['healthcare', 'laboratory'],
'education': ['school'],
'security': ['police', 'camera'],
'infrastructure': ['toilets', 'recycling', 'substation']
} %}
<div class="row mb-4">
<div class="col-12">
<ul class="nav nav-tabs" id="themeTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="tab-table" data-bs-toggle="tab" data-bs-target="#tabTableContent" type="button" role="tab" aria-controls="tabTableContent" aria-selected="true">Tableau</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="tab-cards" data-bs-toggle="tab" data-bs-target="#tabCardsContent" type="button" role="tab" aria-controls="tabCardsContent" aria-selected="false">Cartes</button>
</li>
</ul>
<div class="tab-content" id="themeTabsContent">
<div class="tab-pane fade show active" id="tabTableContent" role="tabpanel" aria-labelledby="tab-table">
<table class="table table-sm table-theme align-middle">
<thead>
<tr>
<th>Catégorie</th>
<th>Thème</th>
<th>Nombre</th>
<th>Complétion</th>
<th>Progression 7j</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for group_name, group_types in theme_groups %}
{% for type in group_types %}
{% set data = latestFollowups[type]|default(null) %}
{% set completion = data and data.completion is defined ? data.completion.getMeasure() : null %}
{% set count = data and data.count is defined ? data.count.getMeasure() : null %}
{% set completion_class = '' %}
{% if completion is not null %}
{% if completion < 40 %}
{% set completion_class = 'completion-low' %}
{% elseif completion < 80 %}
{% set completion_class = 'completion-medium' %}
{% else %}
{% set completion_class = 'completion-high' %}
{% endif %}
{% if completionDelta is not null %}
<span title="Progression sur 7 jours - Complétion">
{{ completionDelta > 0 ? '+' ~ completionDelta|round(1) : completionDelta == 0 ? '0' : completionDelta|round(1) }}%
</span>
{% endif %}
<tr>
<td class="text-muted">
{% if loop.first %}
{% if group_name == 'emergency' %}🚨 Urgence
{% elseif group_name == 'transport' %}🚌 Transport
{% elseif group_name == 'healthcare' %}🏥 Santé
{% elseif group_name == 'education' %}🎓 Éducation
{% elseif group_name == 'security' %}🛡️ Sécurité
{% elseif group_name == 'infrastructure' %}🏗️ Infrastructure
{% else %}{{ group_name|capitalize }}
{% endif %}
{% endif %}
</td>
<td>
<i class="bi {{ followup_icons[type]|default('bi-question-circle') }}"></i>
{{ followup_labels[type]|default(type|capitalize) }}
</td>
<td>{{ count is not null ? count : '?' }}</td>
<td><span class="completion-badge {{ completion_class }}"></span> {{ completion is not null ? completion ~ '%' : '?' }}</td>
<td>
{% if progression7Days[type] is defined %}
{% set countDelta = progression7Days[type].count %}
{% set completionDelta = progression7Days[type].completion %}
<span class="text-muted small">
{% if countDelta is not null %}
<span title="Progression sur 7 jours - Nombre d'objets">
{{ countDelta > 0 ? '+' ~ countDelta : countDelta == 0 ? '0' : countDelta }}
</span>
{% endif %}
{% if completionDelta is not null %}
<span title="Progression sur 7 jours - Complétion">
{{ completionDelta > 0 ? '+' ~ completionDelta|round(1) : completionDelta == 0 ? '0' : completionDelta|round(1) }}%
</span>
{% endif %}
</span>
{% endif %}
</td>
<td>
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}" target="_blank" class="btn btn-sm btn-outline-primary" title="Voir le graphique">
<i class="bi bi-graph-up"></i>
</a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
<div class="tab-pane fade" id="tabCardsContent" role="tabpanel" aria-labelledby="tab-cards">
{% for group_name, group_types in theme_groups %}
<div class="mb-2">
<div class="mb-1 text-muted">
{% if group_name == 'emergency' %}🚨 Urgence
{% elseif group_name == 'transport' %}🚌 Transport
{% elseif group_name == 'healthcare' %}🏥 Santé
{% elseif group_name == 'education' %}🎓 Éducation
{% elseif group_name == 'security' %}🛡️ Sécurité
{% elseif group_name == 'infrastructure' %}🏗️ Infrastructure
{% else %}{{ group_name|capitalize }}
{% endif %}
</div>
<div class="theme-row-scroll">
{% for type in group_types %}
{% set data = latestFollowups[type]|default(null) %}
{% set overpass_query = '[out:json][timeout:60];\narea["ref:INSEE"="' ~ stats.zone ~ '"]->.searchArea;\n(' ~ overpass_type_queries[type]|default('') ~ ');\n(._;>;);\nout meta;\n>;' %}
{% set completion = data and data.completion is defined ? data.completion.getMeasure() : null %}
{% set completion_class = '' %}
{% if completion is not null %}
{% if completion < 40 %}
{% set completion_class = 'completion-low' %}
{% elseif completion < 80 %}
{% set completion_class = 'completion-medium' %}
{% else %}
{% set completion_class = 'completion-high' %}
{% endif %}
{% endif %}
</small>
{% endif %}
{% endif %}
</div>
<div class="col-auto">
<div class="card shadow-sm text-center compact-theme-card" style="min-width: 120px; max-width: 140px;">
<div class="card-body p-2">
<div class="d-flex align-items-center justify-content-between mb-1">
<span class="completion-badge {{ completion_class }}"></span>
<i class="bi {{ followup_icons[type]|default('bi-question-circle') }} fs-4"></i>
</div>
<div class="theme-title mb-1">
<a href="http://127.0.0.1:8111/import?url=https://overpass-api.de/api/interpreter?data={{ overpass_query|url_encode }}" target="_blank" class="fw-bold text-decoration-none text-dark small" title="Charger dans JOSM">
{{ followup_labels[type]|default(type|capitalize) }}
</a>
</div>
<div class="theme-stats small">
<span title="Nombre">{{ data and data.count is defined ? data.count.getMeasure() : '?' }}</span> |
<span title="Complétion">{{ completion is not null ? completion : '?' }}%</span>
</div>
<div class="theme-actions mt-1">
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}" target="_blank" class="btn btn-sm btn-outline-primary btn-sm" title="Voir le graphique">
<i class="bi bi-graph-up"></i>
</a>
</div>
{% if progression7Days[type] is defined %}
{% set countDelta = progression7Days[type].count %}
{% set completionDelta = progression7Days[type].completion %}
{% if countDelta is not null or completionDelta is not null %}
<small class="text-muted d-block mt-1">
{% if countDelta is not null %}
<span title="Progression sur 7 jours - Nombre d'objets">
{{ countDelta > 0 ? '+' ~ countDelta : countDelta == 0 ? '0' : countDelta }}
</span>
{% endif %}
{% if completionDelta is not null %}
<span title="Progression sur 7 jours - Complétion">
{{ completionDelta > 0 ? '+' ~ completionDelta|round(1) : completionDelta == 0 ? '0' : completionDelta|round(1) }}%
</span>
{% endif %}
</small>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
<div class="col-auto mb-2">
<div class="card shadow-sm text-center bg-light text-muted" style="min-width: 140px;">
<div class="card-body p-2">
<span class="completion-badge" style="background:#eee;"></span><br>
<i class="bi bi-question-circle fs-2 mb-1"></i><br>
<span class="fw-bold">{{ followup_labels[type]|default(type|capitalize) }}</span><br>
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}" target="_blank" class="btn btn-sm btn-outline-secondary mt-1" title="Voir le graphique">
<i class="bi bi-graph-up"></i> Graphique
</a><br>
<span title="Nombre">N = ?</span><br>
<span title="Complétion">?%</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>