recup sources
This commit is contained in:
parent
86622a19ea
commit
65fe2a35f9
155 changed files with 50969 additions and 0 deletions
57
templates/admin/_wiki_navigation.html.twig
Normal file
57
templates/admin/_wiki_navigation.html.twig
Normal file
|
@ -0,0 +1,57 @@
|
|||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ path('app_admin_wiki') }}">Wiki OSM</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#wikiNavbar" aria-controls="wikiNavbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="wikiNavbar">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki' %}active{% endif %}" href="{{ path('app_admin_wiki') }}">
|
||||
<i class="bi bi-list-ul"></i> Liste des pages
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_random_suggestion' %}active{% endif %}" href="{{ path('app_admin_wiki_random_suggestion') }}">
|
||||
<i class="bi bi-shuffle"></i> Suggestion aléatoire
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_tag_proposals' %}active{% endif %}" href="{{ path('app_admin_wiki_tag_proposals') }}">
|
||||
<i class="bi bi-tag"></i> Propositions de tags
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_archived_proposals' %}active{% endif %}" href="{{ path('app_admin_wiki_archived_proposals') }}">
|
||||
<i class="bi bi-archive"></i> Propositions archivées
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_suspicious_deletions' %}active{% endif %}" href="{{ path('app_admin_wiki_suspicious_deletions') }}">
|
||||
<i class="bi bi-exclamation-triangle"></i> Suppressions suspectes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_missing_translations' %}active{% endif %}" href="{{ path('app_admin_wiki_missing_translations') }}">
|
||||
<i class="bi bi-translate"></i> Pages sans traduction
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_pages_unavailable_in_french' %}active{% endif %}" href="{{ path('app_admin_wiki_pages_unavailable_in_french') }}">
|
||||
<i class="bi bi-globe"></i> Pages à traduire en français
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_osm_fr_groups' %}active{% endif %}" href="{{ path('app_admin_wiki_osm_fr_groups') }}">
|
||||
<i class="bi bi-people"></i> Groupes OSM-FR
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_recent_changes' %}active{% endif %}" href="{{ path('app_admin_wiki_recent_changes') }}">
|
||||
<i class="bi bi-clock-history"></i> Changements récents
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
506
templates/admin/wiki.html.twig
Normal file
506
templates/admin/wiki.html.twig
Normal file
|
@ -0,0 +1,506 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki OSM{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki OpenStreetMap</h1>
|
||||
<p class="lead">Outil de qualité des des pages wiki OpenStreetMap en français et en anglais pour les clés OSM
|
||||
les plus utilisées.
|
||||
<a href="https://forum.openstreetmap.fr/t/fabriquer-un-outil-de-qualite-pour-le-wiki-osm/36814">
|
||||
Venez discuter QualiWiki sur le forum
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Liste des pages wiki</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th rowspan="2">Image</th>
|
||||
<th rowspan="2">Clé</th>
|
||||
<th colspan="4" class="text-center">Différences FR vs EN</th>
|
||||
<th rowspan="2" class="text-center">Score de<br>décrépitude</th>
|
||||
<th rowspan="2" class="text-center">Liens</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-center">Sections</th>
|
||||
<th class="text-center">Mots</th>
|
||||
<th class="text-center">Liens</th>
|
||||
<th class="text-center">Images</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, languages in wiki_pages %}
|
||||
{% if languages['en'] is defined and languages['fr'] is defined %}
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
<img src="{{ languages['en'].description_img_url }}" alt="image"
|
||||
style="height: 2rem;">
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ key }}</strong>
|
||||
</td>
|
||||
|
||||
{% set diff = page_differences[key] %}
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.section_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.section_diff_formatted }}</span>
|
||||
{% elseif diff.section_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.section_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.word_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.word_diff_formatted }}</span>
|
||||
{% elseif diff.word_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.word_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.link_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.link_diff_formatted }}</span>
|
||||
{% elseif diff.link_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.link_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.media_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.media_diff_formatted }}</span>
|
||||
{% elseif diff.media_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.media_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% set score = languages['en'].staleness_score|default(0) %}
|
||||
{% if score > 50 %}
|
||||
<span class="badge bg-danger">{{ score }}</span>
|
||||
{% elseif score > 20 %}
|
||||
<span class="badge bg-warning text-dark">{{ score }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">{{ score }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ languages['en'].url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Version anglaise">
|
||||
<i class="bi bi-translate"></i> EN
|
||||
</a>
|
||||
<a href="{{ languages['fr'].url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-translate"></i> FR
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_compare', {'key': key}) }}"
|
||||
class="btn btn-sm btn-outline-secondary" title="Comparer les versions">
|
||||
<i class="bi bi-arrows-angle-expand"></i> Comparer
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if missing_translations|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h2>Pages manquantes en français ({{ missing_translations|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki ont une version anglaise mais pas de traduction française.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Clé</th>
|
||||
<th>Sections</th>
|
||||
<th>Mots</th>
|
||||
<th>Liens</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, page in missing_translations %}
|
||||
<tr>
|
||||
<td><strong>{{ key }}</strong></td>
|
||||
<td>{{ page.sections }}</td>
|
||||
<td>{{ page.word_count }}</td>
|
||||
<td>{{ page.link_count }}</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version anglaise">
|
||||
<i class="bi bi-flag-fill"></i> EN
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_create_french', {'key': key}) }}"
|
||||
class="btn btn-sm btn-success"
|
||||
title="Créer une traduction française">
|
||||
<i class="bi bi-plus-circle"></i> Traduire
|
||||
</a>
|
||||
{# <a href="{{ path('app_admin_wiki_compare', {'key': key}) }}" #}
|
||||
{# class="btn btn-sm btn-outline-secondary" #}
|
||||
{# title="Voir les détails et comparer"> #}
|
||||
{# <i class="bi bi-arrows-angle-expand"></i> Comparer #}
|
||||
{# </a> #}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if specific_pages is defined and specific_pages|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>Pages spécifiques ({{ specific_pages|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki sont des pages spécifiques qui ont été sélectionnées pour une comparaison
|
||||
particulière.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Raison</th>
|
||||
<th>Score de décrépitude</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in specific_pages %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if page.en_page.description_img_url is defined and page.en_page.description_img_url %}
|
||||
<div class="me-3">
|
||||
<img src="{{ page.en_page.description_img_url }}"
|
||||
alt="{{ page.key }}"
|
||||
style="max-width: 80px; max-height: 60px; object-fit: contain;">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<strong>{{ page.key }}</strong>
|
||||
<span class="badge bg-primary">Spécifique</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ page.reason }}
|
||||
</td>
|
||||
<td>
|
||||
{% if page.staleness_score is defined %}
|
||||
<div class="progress" style="height: 20px;">
|
||||
{% set score_class = page.staleness_score > 70 ? 'bg-danger' : (page.staleness_score > 40 ? 'bg-warning' : 'bg-success') %}
|
||||
<div class="progress-bar {{ score_class }}" role="progressbar"
|
||||
style="width: {{ page.staleness_score }}%;"
|
||||
aria-valuenow="{{ page.staleness_score }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
{{ page.staleness_score }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Non disponible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.en_page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Version anglaise">
|
||||
<i class="bi bi-translate"></i> EN
|
||||
</a>
|
||||
{% if page.fr_page %}
|
||||
<a href="{{ page.fr_page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-translate"></i> FR
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_compare', {'key': page.key}) }}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
title="Comparer les versions">
|
||||
<i class="bi bi-arrows-angle-expand"></i> Comparer
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ path('app_admin_wiki_create_french', {'key': page.key}) }}"
|
||||
class="btn btn-sm btn-success"
|
||||
title="Créer une traduction française">
|
||||
<i class="bi bi-plus-circle"></i> Traduire
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if pages_unavailable_in_english|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-info text-dark">
|
||||
<h2>Pages françaises non disponibles en Anglais ({{ pages_unavailable_in_english|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki ont une version française mais pas de traduction anglaise.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Score de décrépitude</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in pages_unavailable_in_english %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if page.description_img_url is defined and page.description_img_url %}
|
||||
<div class="me-3">
|
||||
<img src="{{ page.description_img_url }}" alt="{{ page.title }}"
|
||||
style="max-width: 80px; max-height: 60px; object-fit: contain;">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<strong>{{ page.title }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{% if page.outdatedness_score is defined %}
|
||||
<div class="progress" style="height: 20px;">
|
||||
{% set score_class = page.outdatedness_score > 70 ? 'bg-danger' : (page.outdatedness_score > 40 ? 'bg-warning' : 'bg-success') %}
|
||||
<div class="progress-bar {{ score_class }}" role="progressbar"
|
||||
style="width: {{ page.outdatedness_score }}%;"
|
||||
aria-valuenow="{{ page.outdatedness_score }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
{{ page.outdatedness_score }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Non disponible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-flag-fill"></i> FR
|
||||
</a>
|
||||
{% set en_url = page.url|replace({'FR:': ''}) %}
|
||||
<a href="{{ en_url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
title="Créer une traduction anglaise">
|
||||
<i class="bi bi-translate"></i> créer EN
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if newly_created_pages is defined and newly_created_pages|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2>Pages françaises récemment créées ({{ newly_created_pages|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki françaises ont été récemment créées et étaient auparavant dans la liste des pages
|
||||
non disponibles en français.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Version anglaise</th>
|
||||
<th>Date de création</th>
|
||||
<th>Créée par</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in newly_created_pages %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<strong>{{ page.title }}</strong>
|
||||
<span class="badge bg-success">Nouvelle</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ page.en_title }}
|
||||
</td>
|
||||
<td>
|
||||
{{ page.created_at }}
|
||||
</td>
|
||||
<td>
|
||||
{{ page.created_by }}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-flag-fill"></i> FR
|
||||
</a>
|
||||
<a href="{{ page.en_url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Version anglaise">
|
||||
<i class="bi bi-flag-fill"></i> EN
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
le score de fraîcheur prend en compte d'avantage la différence entre le nombre de mots que l'ancienneté de
|
||||
modification.
|
||||
On compte aussi le nombre de sections et de liens.
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Graphe de décrépitude</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="decrepitudeChart" height="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Collect data from the table
|
||||
const labels = [];
|
||||
const scores = [];
|
||||
const colors = [];
|
||||
|
||||
{% for key, languages in wiki_pages %}
|
||||
{% if languages['en'] is defined and languages['fr'] is defined %}
|
||||
labels.push("{{ key }}");
|
||||
{% set score = languages['en'].staleness_score|default(0) %}
|
||||
scores.push({{ score }});
|
||||
|
||||
// Set color based on score
|
||||
{% if score > 50 %}
|
||||
colors.push('rgba(220, 53, 69, 0.7)'); // danger
|
||||
{% elseif score > 20 %}
|
||||
colors.push('rgba(255, 193, 7, 0.7)'); // warning
|
||||
{% else %}
|
||||
colors.push('rgba(25, 135, 84, 0.7)'); // success
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
// Sort data by score (descending)
|
||||
const indices = Array.from(Array(scores.length).keys())
|
||||
.sort((a, b) => scores[b] - scores[a]);
|
||||
|
||||
const sortedLabels = indices.map(i => labels[i]);
|
||||
const sortedScores = indices.map(i => scores[i]);
|
||||
const sortedColors = indices.map(i => colors[i]);
|
||||
|
||||
// Limit to top 20 pages for readability
|
||||
const displayLimit = 20;
|
||||
const displayLabels = sortedLabels.slice(0, displayLimit);
|
||||
const displayScores = sortedScores.slice(0, displayLimit);
|
||||
const displayColors = sortedColors.slice(0, displayLimit);
|
||||
|
||||
// Create the chart
|
||||
const ctx = document.getElementById('decrepitudeChart').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: displayLabels,
|
||||
datasets: [{
|
||||
label: 'Score de décrépitude',
|
||||
data: displayScores,
|
||||
backgroundColor: displayColors,
|
||||
borderColor: displayColors.map(c => c.replace('0.7', '1')),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
return `Score: ${context.raw}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Score de décrépitude (0-100)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
701
templates/admin/wiki_archived_proposals.html.twig
Normal file
701
templates/admin/wiki_archived_proposals.html.twig
Normal file
|
@ -0,0 +1,701 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Propositions archivées OSM{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.vote-bar {
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.vote-approve {
|
||||
background-color: #28a745;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-abstain {
|
||||
background-color: #ffc107;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-oppose {
|
||||
background-color: #dc3545;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-count {
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.proposal-card {
|
||||
margin-bottom: 1.5rem;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.proposal-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.proposal-metadata {
|
||||
font-size: 0.85rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.voter-badge {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.voter-approve {
|
||||
background-color: rgba(40, 167, 69, 0.2);
|
||||
border: 1px solid rgba(40, 167, 69, 0.4);
|
||||
}
|
||||
|
||||
.voter-abstain {
|
||||
background-color: rgba(255, 193, 7, 0.2);
|
||||
border: 1px solid rgba(255, 193, 7, 0.4);
|
||||
}
|
||||
|
||||
.voter-oppose {
|
||||
background-color: rgba(220, 53, 69, 0.2);
|
||||
border: 1px solid rgba(220, 53, 69, 0.4);
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.stats-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.stats-value {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stats-label {
|
||||
font-size: 0.9rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.top-voters-table th, .top-voters-table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.vote-duration {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #e9ecef;
|
||||
margin-top: 5px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
font-style: italic;
|
||||
color: #495057;
|
||||
background-color: #f8f9fa;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 3px solid #dee2e6;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Propositions archivées OpenStreetMap</h1>
|
||||
<p class="lead">Analyse des votes sur les propositions archivées du wiki OSM</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
<form class="d-inline-flex align-items-center" method="get" action="{{ path('app_admin_wiki_archived_proposals') }}">
|
||||
<div class="me-2">
|
||||
<label for="limit" class="me-2">Limiter à:</label>
|
||||
<select name="limit" id="limit" class="form-select form-select-sm d-inline-block" style="width: auto;">
|
||||
<option value="">Toutes les propositions</option>
|
||||
<option value="10" {% if limit == 10 %}selected{% endif %}>10 propositions</option>
|
||||
<option value="20" {% if limit == 20 %}selected{% endif %}>20 propositions</option>
|
||||
<option value="50" {% if limit == 50 %}selected{% endif %}>50 propositions</option>
|
||||
<option value="100" {% if limit == 100 %}selected{% endif %}>100 propositions</option>
|
||||
{% if limit and limit not in [10, 20, 50, 100] %}
|
||||
<option value="{{ limit }}" selected>{{ limit }} propositions</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</div>
|
||||
<input type="hidden" name="refresh" value="1">
|
||||
<button type="submit" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-arrow-clockwise"></i> Rafraîchir les données
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% if limit %}
|
||||
<div class="mt-2">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-info-circle"></i> Les données sont limitées à {{ limit }} propositions.
|
||||
<a href="{{ path('app_admin_wiki_archived_proposals') }}">Voir toutes les propositions</a>
|
||||
</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if statistics %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Statistiques globales</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.total_proposals }}</div>
|
||||
<div class="stats-label">Propositions analysées</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.total_votes }}</div>
|
||||
<div class="stats-label">Votes au total</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.avg_votes_per_proposal }}</div>
|
||||
<div class="stats-label">Votes par proposition (moyenne)</div>
|
||||
<div class="small text-muted">Excluant les propositions sans votes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.unique_voters }}</div>
|
||||
<div class="stats-label">Votants uniques</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.median_votes_per_proposal }}</div>
|
||||
<div class="stats-label">Votes par proposition (médiane)</div>
|
||||
<div class="small text-muted">Excluant les propositions sans votes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.std_dev_votes_per_proposal }}</div>
|
||||
<div class="stats-label">Votes par proposition (écart type)</div>
|
||||
<div class="small text-muted">Excluant les propositions sans votes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if statistics.avg_vote_duration_days is defined %}
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.avg_vote_duration_days }}</div>
|
||||
<div class="stats-label">Durée moyenne des votes (jours)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if statistics.status_distribution is defined and statistics.status_distribution|length > 0 %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<h4>Répartition par statut</h4>
|
||||
<div class="chart-container">
|
||||
<canvas id="statusChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4>Détail des statuts</h4>
|
||||
<table class="table table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Statut</th>
|
||||
<th>Nombre</th>
|
||||
<th>Pourcentage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for status, count in statistics.status_distribution %}
|
||||
<tr>
|
||||
<td>{{ status }}</td>
|
||||
<td>{{ count }}</td>
|
||||
<td>{{ (count / statistics.total_proposals * 100)|round(1) }}%</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<h4>Répartition des années des propositions</h4>
|
||||
<div class="chart-container">
|
||||
<canvas id="yearDistributionChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Contributeurs les plus actifs</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover top-voters-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Total votes</th>
|
||||
<th>Approbations</th>
|
||||
<th>Abstentions</th>
|
||||
<th>Oppositions</th>
|
||||
<th>Répartition</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for voter in statistics.top_voters %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>
|
||||
<a href="https://wiki.openstreetmap.org/wiki/User:{{ voter.username }}" target="_blank">
|
||||
{{ voter.username }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ voter.total }}</td>
|
||||
<td>{{ voter.approve }}</td>
|
||||
<td>{{ voter.abstain }}</td>
|
||||
<td>{{ voter.oppose }}</td>
|
||||
<td>
|
||||
<div class="vote-bar">
|
||||
{% if voter.approve > 0 %}
|
||||
<div class="vote-approve" style="width: {{ (voter.approve / voter.total * 100)|round }}%">
|
||||
<span class="vote-count">{{ (voter.approve / voter.total * 100)|round }}%</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if voter.abstain > 0 %}
|
||||
<div class="vote-abstain" style="width: {{ (voter.abstain / voter.total * 100)|round }}%">
|
||||
<span class="vote-count">{{ (voter.abstain / voter.total * 100)|round }}%</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if voter.oppose > 0 %}
|
||||
<div class="vote-oppose" style="width: {{ (voter.oppose / voter.total * 100)|round }}%">
|
||||
<span class="vote-count">{{ (voter.oppose / voter.total * 100)|round }}%</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Liste des propositions archivées</h2>
|
||||
<div>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm filter-btn active" data-filter="all">Toutes</button>
|
||||
<button type="button" class="btn btn-outline-success btn-sm filter-btn" data-filter="approved">Approuvées</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm filter-btn" data-filter="rejected">Rejetées</button>
|
||||
<button type="button" class="btn btn-outline-warning btn-sm filter-btn" data-filter="neutral">Neutres</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row" id="proposals-container">
|
||||
{% for proposal in proposals %}
|
||||
{% set total_votes = proposal.votes.approve.count + proposal.votes.oppose.count + proposal.votes.abstain.count %}
|
||||
{% set is_approved = proposal.votes.approve.count > proposal.votes.oppose.count %}
|
||||
{% set is_rejected = proposal.votes.approve.count < proposal.votes.oppose.count %}
|
||||
{% set is_neutral = proposal.votes.approve.count == proposal.votes.oppose.count %}
|
||||
|
||||
<div class="col-md-6 mb-4 proposal-item {% if is_approved %}approved{% elseif is_rejected %}rejected{% else %}neutral{% endif %}">
|
||||
<div class="card proposal-card h-100 {% if is_approved %}border-success{% elseif is_rejected %}border-danger{% else %}border-warning{% endif %}">
|
||||
<div class="card-header {% if is_approved %}bg-success text-white{% elseif is_rejected %}bg-danger text-white{% else %}bg-warning{% endif %}">
|
||||
<h5 class="card-title mb-0">
|
||||
<a href="{{ proposal.url }}" target="_blank" class="text-white">
|
||||
{{ proposal.title }}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="proposal-metadata mb-3">
|
||||
{% if proposal.proposer %}
|
||||
<div><strong>Proposé par :</strong> {{ proposal.proposer }}</div>
|
||||
{% endif %}
|
||||
{% if proposal.last_modified %}
|
||||
<div><strong>Dernière modification :</strong> {{ proposal.last_modified }}</div>
|
||||
{% endif %}
|
||||
<div><strong>Sections :</strong> {{ proposal.section_count }}</div>
|
||||
<div><strong>Liens :</strong> {{ proposal.link_count }}</div>
|
||||
<div><strong>Mots :</strong> {{ proposal.word_count }}</div>
|
||||
{% if proposal.votes.duration_days is defined %}
|
||||
<div class="mt-2">
|
||||
<span class="vote-duration">
|
||||
<i class="bi bi-calendar-range"></i>
|
||||
<strong>Durée du vote :</strong> {{ proposal.votes.duration_days }} jours
|
||||
({{ proposal.votes.first_vote }} → {{ proposal.votes.last_vote }})
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if total_votes > 0 %}
|
||||
<h6>Résultats des votes ({{ total_votes }} votes)</h6>
|
||||
<div class="vote-bar">
|
||||
{% if proposal.votes.approve.count > 0 %}
|
||||
<div class="vote-approve" style="width: {{ proposal.approve_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.approve.count }} ({{ proposal.approve_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.votes.abstain.count > 0 %}
|
||||
<div class="vote-abstain" style="width: {{ proposal.abstain_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.abstain.count }} ({{ proposal.abstain_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.votes.oppose.count > 0 %}
|
||||
<div class="vote-oppose" style="width: {{ proposal.oppose_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.oppose.count }} ({{ proposal.oppose_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="accordion" id="votersAccordion{{ loop.index }}">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="headingVoters{{ loop.index }}">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseVoters{{ loop.index }}" aria-expanded="false"
|
||||
aria-controls="collapseVoters{{ loop.index }}">
|
||||
Voir les votants
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseVoters{{ loop.index }}" class="accordion-collapse collapse"
|
||||
aria-labelledby="headingVoters{{ loop.index }}"
|
||||
data-bs-parent="#votersAccordion{{ loop.index }}">
|
||||
<div class="accordion-body">
|
||||
{% if proposal.votes.approve.users|length > 0 %}
|
||||
<div class="mb-2">
|
||||
<strong>Approbations ({{ proposal.votes.approve.count }}):</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.approve.users %}
|
||||
<div class="mb-2">
|
||||
<span class="voter-badge voter-approve">{{ user.username }}</span>
|
||||
{% if user.comment is defined and user.comment is not empty %}
|
||||
<div class="comment-text">{{ user.comment }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.abstain.users|length > 0 %}
|
||||
<div class="mb-2">
|
||||
<strong>Abstentions ({{ proposal.votes.abstain.count }}):</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.abstain.users %}
|
||||
<div class="mb-2">
|
||||
<span class="voter-badge voter-abstain">{{ user.username }}</span>
|
||||
{% if user.comment is defined and user.comment is not empty %}
|
||||
<div class="comment-text">{{ user.comment }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.oppose.users|length > 0 %}
|
||||
<div>
|
||||
<strong>Oppositions ({{ proposal.votes.oppose.count }}):</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.oppose.users %}
|
||||
<div class="mb-2">
|
||||
<span class="voter-badge voter-oppose">{{ user.username }}</span>
|
||||
{% if user.comment is defined and user.comment is not empty %}
|
||||
<div class="comment-text">{{ user.comment }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-secondary">
|
||||
Aucun vote trouvé pour cette proposition.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{{ proposal.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="bi bi-exclamation-triangle"></i> Aucune proposition archivée n'a été trouvée.
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Filtering functionality
|
||||
const filterButtons = document.querySelectorAll('.filter-btn');
|
||||
const proposalItems = document.querySelectorAll('.proposal-item');
|
||||
|
||||
filterButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active class from all buttons
|
||||
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked button
|
||||
this.classList.add('active');
|
||||
|
||||
const filter = this.getAttribute('data-filter');
|
||||
|
||||
// Show/hide proposals based on filter
|
||||
proposalItems.forEach(item => {
|
||||
if (filter === 'all') {
|
||||
item.style.display = 'block';
|
||||
} else if (filter === 'approved' && item.classList.contains('approved')) {
|
||||
item.style.display = 'block';
|
||||
} else if (filter === 'rejected' && item.classList.contains('rejected')) {
|
||||
item.style.display = 'block';
|
||||
} else if (filter === 'neutral' && item.classList.contains('neutral')) {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize status distribution chart if it exists
|
||||
const statusChartCanvas = document.getElementById('statusChart');
|
||||
if (statusChartCanvas) {
|
||||
// Get status distribution data from the template
|
||||
const statusDistribution = {{ statistics.status_distribution|json_encode|raw }};
|
||||
|
||||
if (statusDistribution && Object.keys(statusDistribution).length > 0) {
|
||||
const labels = Object.keys(statusDistribution);
|
||||
const data = Object.values(statusDistribution);
|
||||
|
||||
// Generate colors for each status
|
||||
const backgroundColors = [
|
||||
'#28a745', // Approved - green
|
||||
'#dc3545', // Rejected - red
|
||||
'#ffc107', // Voting/Proposed - yellow
|
||||
'#6c757d', // Abandoned/Inactive - gray
|
||||
'#17a2b8', // Other statuses - blue
|
||||
'#6610f2', // Other statuses - purple
|
||||
'#fd7e14', // Other statuses - orange
|
||||
'#20c997', // Other statuses - teal
|
||||
'#e83e8c' // Other statuses - pink
|
||||
];
|
||||
|
||||
// Create the chart
|
||||
new Chart(statusChartCanvas, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: data,
|
||||
backgroundColor: backgroundColors.slice(0, labels.length),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'right',
|
||||
labels: {
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
const label = context.label || '';
|
||||
const value = context.raw || 0;
|
||||
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||
const percentage = Math.round((value / total) * 100);
|
||||
return `${label}: ${value} (${percentage}%)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize year distribution chart
|
||||
const yearChartCanvas = document.getElementById('yearDistributionChart');
|
||||
if (yearChartCanvas) {
|
||||
// Get proposals data from the template
|
||||
const proposals = {{ proposals|json_encode|raw }};
|
||||
|
||||
if (proposals && proposals.length > 0) {
|
||||
// Extract years from last_modified dates
|
||||
const yearCounts = {};
|
||||
|
||||
proposals.forEach(proposal => {
|
||||
if (proposal.last_modified) {
|
||||
// Extract year from the date string (format: "DD Month YYYY")
|
||||
const yearMatch = proposal.last_modified.match(/\d{4}$/);
|
||||
if (yearMatch) {
|
||||
const year = yearMatch[0];
|
||||
yearCounts[year] = (yearCounts[year] || 0) + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sort years chronologically
|
||||
const sortedYears = Object.keys(yearCounts).sort();
|
||||
const counts = sortedYears.map(year => yearCounts[year]);
|
||||
|
||||
// Generate a color gradient for the bars
|
||||
const colors = sortedYears.map((year, index) => {
|
||||
// Create a gradient from blue to green
|
||||
const ratio = index / (sortedYears.length - 1 || 1);
|
||||
return `rgba(${Math.round(33 + (20 * ratio))}, ${Math.round(150 + (50 * ratio))}, ${Math.round(243 - (100 * ratio))}, 0.7)`;
|
||||
});
|
||||
|
||||
// Create the chart
|
||||
new Chart(yearChartCanvas, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: sortedYears,
|
||||
datasets: [{
|
||||
label: 'Nombre de propositions',
|
||||
data: counts,
|
||||
backgroundColor: colors,
|
||||
borderColor: colors.map(color => color.replace('0.7', '1')),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
precision: 0
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Nombre de propositions'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Année'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
title: function(context) {
|
||||
return `Année ${context[0].label}`;
|
||||
},
|
||||
label: function(context) {
|
||||
const count = context.raw;
|
||||
return count > 1 ? `${count} propositions` : `${count} proposition`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
1030
templates/admin/wiki_compare.html.twig
Normal file
1030
templates/admin/wiki_compare.html.twig
Normal file
File diff suppressed because it is too large
Load diff
115
templates/admin/wiki_create_french.html.twig
Normal file
115
templates/admin/wiki_create_french.html.twig
Normal file
|
@ -0,0 +1,115 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Créer une traduction française pour {{ key }}{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.iframe-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: calc(100vh - 200px);
|
||||
min-height: 600px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.iframe-container iframe {
|
||||
flex: 1;
|
||||
border: 1px solid #dee2e6;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator {
|
||||
width: 10px;
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator::after {
|
||||
content: "⟺";
|
||||
font-size: 20px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.iframe-header {
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-bottom: none;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.iframe-container {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.iframe-container iframe {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator::after {
|
||||
content: "⟻⟼";
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container-fluid mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Créer une traduction française pour "{{ key }}"</h1>
|
||||
<p class="lead">Utilisez cette page pour traduire la page wiki en français. La page anglaise est affichée à gauche pour référence, et le formulaire d'édition de la page française est à droite.</p>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Conseils pour la traduction :
|
||||
<ul>
|
||||
<li>Conservez la structure de la page anglaise (sections, sous-sections, etc.)</li>
|
||||
<li>Traduisez le contenu en français en adaptant les exemples au contexte français si nécessaire</li>
|
||||
<li>N'hésitez pas à consulter d'autres pages françaises pour vous inspirer du style et de la terminologie</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="iframe-header">
|
||||
<i class="bi bi-flag-fill"></i> Version anglaise (référence)
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="iframe-header">
|
||||
<i class="bi bi-translate"></i> <a href="{{ french_edit_url }}">Création de la version française</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="iframe-container">
|
||||
<iframe src="{{ english_url }}" title="Version anglaise"></iframe>
|
||||
<div class="iframe-separator"></div>
|
||||
<iframe src="{{ french_edit_url }}" title="Édition française"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
<a href="{{ english_url }}" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Ouvrir la version anglaise dans un nouvel onglet
|
||||
</a>
|
||||
<a href="{{ french_edit_url }}" target="_blank" class="btn btn-outline-info">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Ouvrir l'éditeur français dans un nouvel onglet
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
83
templates/admin/wiki_missing_translations.html.twig
Normal file
83
templates/admin/wiki_missing_translations.html.twig
Normal file
|
@ -0,0 +1,83 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki françaises sans traduction anglaise{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki françaises sans traduction anglaise</h1>
|
||||
<p class="lead">Liste des pages françaises du wiki OSM qui n'ont pas de traduction en anglais.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Pages françaises uniquement</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if untranslated_pages|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Clé</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in untranslated_pages %}
|
||||
<tr>
|
||||
<td><strong>{{ page.title }}</strong></td>
|
||||
<td>{{ page.key }}</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank" class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-translate"></i> FR
|
||||
</a>
|
||||
<a href="https://wiki.openstreetmap.org/wiki/{{ page.key }}" target="_blank" class="btn btn-sm btn-success" title="Créer la version anglaise">
|
||||
<i class="bi bi-plus-circle"></i> Créer EN
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune page française sans traduction anglaise n'a été trouvée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des pages sans traduction anglaise</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages sont des contenus originaux créés en français qui n'ont pas encore été traduits en anglais.</p>
|
||||
<p>Bien que la langue principale du wiki OSM soit l'anglais, il est parfois utile de créer d'abord du contenu dans sa langue maternelle, puis de le traduire.</p>
|
||||
<p>Contribuer à la traduction de ces pages en anglais permet de :</p>
|
||||
<ul>
|
||||
<li>Partager les connaissances avec la communauté internationale</li>
|
||||
<li>Améliorer la visibilité des contributions françaises</li>
|
||||
<li>Faciliter la collaboration entre contributeurs de différentes langues</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
238
templates/admin/wiki_osm_fr_groups.html.twig
Normal file
238
templates/admin/wiki_osm_fr_groups.html.twig
Normal file
|
@ -0,0 +1,238 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Groupes OSM-FR{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Groupes OSM-FR</h1>
|
||||
<p class="lead">Liste des groupes de travail et des groupes locaux d'OpenStreetMap France.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Carte uMap des groupes locaux -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>
|
||||
<a href="{{ umap_url }}">
|
||||
|
||||
Carte des groupes locaux
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="ratio ratio-16x9">
|
||||
<iframe src="{{ umap_url }}" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a href="{{ umap_url }}" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir la carte en plein écran
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Groupes de travail -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2>Groupes de travail</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if working_groups|length > 0 %}
|
||||
<div class="accordion" id="workingGroupsAccordion">
|
||||
{% for category, groups in working_groups %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading{{ loop.index }}">
|
||||
<button class="accordion-button {% if not loop.first %}collapsed{% endif %}"
|
||||
type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapse{{ loop.index }}"
|
||||
aria-expanded="{{ loop.first ? 'true' : 'false' }}"
|
||||
aria-controls="collapse{{ loop.index }}">
|
||||
{{ category }} ({{ groups|length }})
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse{{ loop.index }}"
|
||||
class="accordion-collapse collapse {% if loop.first %}show{% endif %}"
|
||||
aria-labelledby="heading{{ loop.index }}" data-bs-parent="#workingGroupsAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="list-group">
|
||||
{% for group in groups %}
|
||||
<a href="{{ group.url }}" target="_blank"
|
||||
class="list-group-item list-group-item-action">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">{{ group.name }}</h5>
|
||||
</div>
|
||||
{% if group.description %}
|
||||
<p class="mb-1">{{ group.description }}</p>
|
||||
{% endif %}
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki
|
||||
</small>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucun groupe de travail n'a été trouvé.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Groupes locaux -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h2>Groupes locaux</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if local_groups|length > 0 %}
|
||||
<!-- Filtres -->
|
||||
<div class="mb-4">
|
||||
<div class="btn-group" role="group" aria-label="Filtres">
|
||||
<button type="button" class="btn btn-outline-primary active filter-btn" data-filter="all">Tous</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="wiki">Wiki</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="framacalc">Framacalc</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="has-wiki">Avec page wiki</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="no-wiki">Sans page wiki</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
{% for group in local_groups %}
|
||||
{% set source = group.source|default('wiki') %}
|
||||
{% set has_wiki = group.has_wiki_page|default(true) %}
|
||||
{% set filter_classes = source ~ ' ' ~ (has_wiki ? 'has-wiki' : 'no-wiki') %}
|
||||
|
||||
<div class="col group-item {{ filter_classes }}">
|
||||
<div class="card h-100 {% if source == 'framacalc' and not has_wiki %}border-danger{% endif %}">
|
||||
{% if source == 'framacalc' %}
|
||||
<div class="card-header bg-light">
|
||||
<span class="badge bg-secondary">Framacalc</span>
|
||||
{% if has_wiki %}
|
||||
<span class="badge bg-success">Page wiki</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">Pas de page wiki</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ group.name }}</h5>
|
||||
{% if group.description %}
|
||||
<p class="card-text">{{ group.description }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if source == 'framacalc' and group.contact %}
|
||||
<p class="card-text"><small class="text-muted">Contact: {{ group.contact }}</small></p>
|
||||
{% endif %}
|
||||
|
||||
{% if source == 'framacalc' and group.website %}
|
||||
<p class="card-text">
|
||||
<a href="{{ group.website }}" target="_blank" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-globe"></i> Site web
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
{% if source == 'wiki' or has_wiki %}
|
||||
<a href="{{ group.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="https://wiki.openstreetmap.org/wiki/Special:Search?search={{ group.name|url_encode }}"
|
||||
target="_blank" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-search"></i> Rechercher sur le wiki
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- JavaScript pour les filtres -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const filterButtons = document.querySelectorAll('.filter-btn');
|
||||
const groupItems = document.querySelectorAll('.group-item');
|
||||
|
||||
filterButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active class from all buttons
|
||||
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked button
|
||||
this.classList.add('active');
|
||||
|
||||
const filter = this.getAttribute('data-filter');
|
||||
|
||||
// Show/hide items based on filter
|
||||
groupItems.forEach(item => {
|
||||
if (filter === 'all') {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
if (item.classList.contains(filter)) {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucun groupe local n'a été trouvé.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des groupes OSM-FR</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>Groupes de travail</h5>
|
||||
<p>Les groupes de travail sont des équipes thématiques qui se concentrent sur des aspects spécifiques
|
||||
d'OpenStreetMap en France. Ils permettent de coordonner les efforts sur des sujets particuliers
|
||||
comme l'import de données, la cartographie des transports, etc.</p>
|
||||
|
||||
<h5>Groupes locaux</h5>
|
||||
<p>Les groupes locaux sont des communautés géographiques de contributeurs OpenStreetMap. Ils organisent
|
||||
des rencontres, des ateliers de cartographie et d'autres événements pour promouvoir OSM dans leur
|
||||
région.</p>
|
||||
|
||||
<div class="d-grid gap-2 col-md-6 mx-auto mt-3">
|
||||
<a href="https://wiki.openstreetmap.org/wiki/France/OSM-FR/Groupes_de_travail" target="_blank"
|
||||
class="btn btn-outline-success">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir tous les groupes de travail sur le wiki
|
||||
</a>
|
||||
<a href="https://wiki.openstreetmap.org/wiki/France/OSM-FR#Groupes_locaux" target="_blank"
|
||||
class="btn btn-outline-info">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir tous les groupes locaux sur le wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
215
templates/admin/wiki_pages_unavailable_in_french.html.twig
Normal file
215
templates/admin/wiki_pages_unavailable_in_french.html.twig
Normal file
|
@ -0,0 +1,215 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki non disponibles en français{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki non disponibles en français</h1>
|
||||
<p class="lead">Liste des pages du wiki OSM qui n'ont pas de traduction française, groupées par langue d'origine.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Statistiques</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Total des pages</h5>
|
||||
<p class="card-text display-4">{{ all_pages|length }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-success mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Pages en anglais</h5>
|
||||
<p class="card-text display-4">{{ grouped_pages['En']|default([])|length }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-info mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Langues différentes</h5>
|
||||
<p class="card-text display-4">{{ grouped_pages|length }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pages groupées par langue -->
|
||||
<div class="accordion" id="languageAccordion">
|
||||
{% for lang_prefix, pages in grouped_pages %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading{{ lang_prefix }}">
|
||||
<button class="accordion-button {% if lang_prefix != 'En' %}collapsed{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ lang_prefix }}" aria-expanded="{{ lang_prefix == 'En' ? 'true' : 'false' }}" aria-controls="collapse{{ lang_prefix }}">
|
||||
{% if lang_prefix == 'En' %}
|
||||
<strong class="text-success">Pages en anglais ({{ pages|length }})</strong>
|
||||
{% elseif lang_prefix == 'Other' %}
|
||||
<strong>Autres pages ({{ pages|length }})</strong>
|
||||
{% else %}
|
||||
<strong>Pages en {{ lang_prefix }} ({{ pages|length }})</strong>
|
||||
{% endif %}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse{{ lang_prefix }}" class="accordion-collapse collapse {% if lang_prefix == 'En' %}show{% endif %}" aria-labelledby="heading{{ lang_prefix }}" data-bs-parent="#languageAccordion">
|
||||
<div class="accordion-body">
|
||||
{% if lang_prefix == 'En' %}
|
||||
<div class="mb-3">
|
||||
<button id="copyEnglishTitlesBtn" class="btn btn-outline-primary">
|
||||
<i class="bi bi-clipboard"></i> Copier les titres au format MediaWiki
|
||||
</button>
|
||||
<span id="copyStatus" class="ms-2 text-success" style="display: none;">
|
||||
<i class="bi bi-check-circle"></i> Copié !
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Score de décrépitude</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in pages %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if page.description_img_url is defined and page.description_img_url %}
|
||||
<div class="me-3">
|
||||
<img src="{{ page.description_img_url }}" alt="{{ page.title }}"
|
||||
style="max-width: 80px; max-height: 60px; object-fit: contain;">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<strong>{{ page.title }}</strong>
|
||||
{% if page.is_english %}
|
||||
<span class="badge bg-success">Priorité</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{% if page.outdatedness_score is defined %}
|
||||
<div class="progress" style="height: 20px;">
|
||||
{% set score_class = page.outdatedness_score > 70 ? 'bg-danger' : (page.outdatedness_score > 40 ? 'bg-warning' : 'bg-success') %}
|
||||
<div class="progress-bar {{ score_class }}" role="progressbar"
|
||||
style="width: {{ page.outdatedness_score }}%;"
|
||||
aria-valuenow="{{ page.outdatedness_score }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
{{ page.outdatedness_score }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Non disponible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank" class="btn btn-sm btn-outline-primary" title="Voir la page originale">
|
||||
<i class="bi bi-eye"></i> Voir
|
||||
</a>
|
||||
{% set fr_url = page.url|replace({'/wiki/': '/wiki/FR:'}) %}
|
||||
<a href="{{ fr_url }}" target="_blank" class="btn btn-sm btn-success" title="Créer la traduction française">
|
||||
<i class="bi bi-plus-circle"></i> Traduire
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="card mt-4 mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des pages non disponibles en français</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages sont des contenus du wiki OpenStreetMap qui n'ont pas encore été traduits en français.</p>
|
||||
<p>Contribuer à la traduction de ces pages permet de :</p>
|
||||
<ul>
|
||||
<li>Rendre la documentation OSM plus accessible aux contributeurs francophones</li>
|
||||
<li>Améliorer la qualité des contributions en français</li>
|
||||
<li>Faciliter l'apprentissage et l'utilisation d'OpenStreetMap pour les nouveaux utilisateurs</li>
|
||||
</ul>
|
||||
<p><strong>Priorité aux pages anglaises :</strong> Les pages commençant par "En:" sont prioritaires car l'anglais est la langue principale du wiki OSM.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const copyButton = document.getElementById('copyEnglishTitlesBtn');
|
||||
const copyStatus = document.getElementById('copyStatus');
|
||||
|
||||
if (copyButton) {
|
||||
copyButton.addEventListener('click', function() {
|
||||
// Get all English page titles from the table
|
||||
const englishSection = document.getElementById('collapseEn');
|
||||
const titleElements = englishSection.querySelectorAll('tbody tr td:first-child strong');
|
||||
|
||||
// Format titles in MediaWiki format
|
||||
let mediawikiText = '';
|
||||
const rows = englishSection.querySelectorAll('tbody tr');
|
||||
|
||||
rows.forEach(function(row) {
|
||||
const title = row.querySelector('td:first-child strong').textContent.trim();
|
||||
const imgElement = row.querySelector('td:first-child img');
|
||||
|
||||
if (imgElement) {
|
||||
const imgSrc = imgElement.getAttribute('src');
|
||||
mediawikiText += '* [[' + title + ']] - Image: ' + imgSrc + '\n';
|
||||
} else {
|
||||
mediawikiText += '* [[' + title + ']]\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Copy to clipboard
|
||||
navigator.clipboard.writeText(mediawikiText).then(function() {
|
||||
// Show success message
|
||||
copyStatus.style.display = 'inline';
|
||||
|
||||
// Hide success message after 3 seconds
|
||||
setTimeout(function() {
|
||||
copyStatus.style.display = 'none';
|
||||
}, 3000);
|
||||
}).catch(function(err) {
|
||||
console.error('Erreur lors de la copie: ', err);
|
||||
alert('Erreur lors de la copie dans le presse-papier. Veuillez réessayer.');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
124
templates/admin/wiki_random_suggestion.html.twig
Normal file
124
templates/admin/wiki_random_suggestion.html.twig
Normal file
|
@ -0,0 +1,124 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Suggestion de page Wiki à améliorer{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Suggestion de page Wiki à améliorer</h1>
|
||||
<p class="lead">Voici une page wiki qui a besoin d'être améliorée.</p>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>{{ page.key }}</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<h3>Raisons d'amélioration</h3>
|
||||
<p>{{ page.reason }}</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h3>Version anglaise</h3>
|
||||
<p class="mb-0">
|
||||
<small>Dernière modification: {{ page.en_page.last_modified }}</small>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group mb-3">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Sections
|
||||
<span class="badge bg-primary rounded-pill">{{ page.en_page.sections }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Mots
|
||||
<span class="badge bg-primary rounded-pill">{{ page.en_page.word_count|default(0) }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Liens
|
||||
<span class="badge bg-primary rounded-pill">{{ page.en_page.link_count|default(0) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{{ page.en_page.url }}" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir la page anglaise
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h3>Version française</h3>
|
||||
{% if page.fr_page %}
|
||||
<p class="mb-0">
|
||||
<small>Dernière modification: {{ page.fr_page.last_modified }}</small>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="mb-0">
|
||||
<small>Page non existante</small>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if page.fr_page %}
|
||||
<ul class="list-group mb-3">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Sections
|
||||
<span class="badge bg-info rounded-pill">{{ page.fr_page.sections }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Mots
|
||||
<span class="badge bg-info rounded-pill">{{ page.fr_page.word_count|default(0) }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Liens
|
||||
<span class="badge bg-info rounded-pill">{{ page.fr_page.link_count|default(0) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{{ page.fr_page.url }}" target="_blank" class="btn btn-outline-info">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir la page française
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<p><i class="bi bi-exclamation-triangle"></i> <strong>La page wiki pour la clé
|
||||
"{{ page.key }}" n'existe pas en français.</strong></p>
|
||||
<p>Vous pouvez contribuer en créant cette page sur le wiki OpenStreetMap.</p>
|
||||
</div>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?title=FR:Key:{{ page.key }}&action=edit"
|
||||
target="_blank" class="btn btn-success">
|
||||
<i class="bi bi-plus-circle"></i> Créer la page française
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-grid gap-2">
|
||||
<a href="{{ path('app_admin_wiki_compare', {'key': page.key}) }}" class="btn btn-primary">
|
||||
<i class="bi bi-arrows-angle-expand"></i> Voir la comparaison détaillée
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_random_suggestion') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-shuffle"></i> Autre suggestion aléatoire
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
156
templates/admin/wiki_recent_changes.html.twig
Normal file
156
templates/admin/wiki_recent_changes.html.twig
Normal file
|
@ -0,0 +1,156 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Changements récents Wiki OSM{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Changements récents Wiki OpenStreetMap</h1>
|
||||
<p class="lead">Liste des changements récents dans l'espace de noms français du wiki OpenStreetMap.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if team_members|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2>Équipe Wiki OSM FR</h2>
|
||||
<p class="mb-0">Contributeurs classés par nombre de modifications</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
{% for member in team_members %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="{{ member.user_url }}" target="_blank" class="text-decoration-none">
|
||||
<span class="fw-bold">{{ member.username }}</span>
|
||||
</a>
|
||||
<span class="badge bg-primary ms-2">{{ member.contributions }}</span>
|
||||
<div class="ms-2 small">
|
||||
<span class="text-success" title="Caractères ajoutés">+{{ member.chars_added }}</span>
|
||||
{% if member.chars_changed > 0 %}
|
||||
<span class="text-warning" title="Caractères modifiés">~{{ member.chars_changed }}</span>
|
||||
{% endif %}
|
||||
{% if member.chars_deleted > 0 %}
|
||||
<span class="text-danger" title="Caractères supprimés">-{{ member.chars_deleted }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>Changements récents</h2>
|
||||
<p class="mb-0">
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?hidebots=1&hidepreviousrevisions=1&hidecategorization=1&hideWikibase=1&hidelog=1&hidenewuserlog=1&namespace=202&limit=500&days=30&enhanced=1&title=Special:RecentChanges&urlversion=2"
|
||||
target="_blank" class="text-white">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki OSM
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if recent_changes|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Date</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Commentaire</th>
|
||||
<th>Taille</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for change in recent_changes %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
<a href="{{ change.page_url }}" target="_blank" class="text-decoration-none">
|
||||
{{ change.page_name }}
|
||||
</a>
|
||||
</strong>
|
||||
</td>
|
||||
<td>{{ change.timestamp }}</td>
|
||||
<td>
|
||||
{% if change.user_url %}
|
||||
<a href="{{ change.user_url }}" target="_blank" class="text-decoration-none">
|
||||
{{ change.user }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ change.user }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ change.comment }}
|
||||
{% if change.added_text or change.removed_text %}
|
||||
<div class="mt-1 small">
|
||||
{% if change.added_text %}
|
||||
<span class="text-success">{{ change.added_text }}</span>
|
||||
{% endif %}
|
||||
{% if change.removed_text %}
|
||||
<span class="text-danger">{{ change.removed_text }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if change.change_size starts with '+' or change.change_size > 0 %}
|
||||
<span class="text-success">{{ change.change_size }}</span>
|
||||
{% elseif change.change_size starts with '−' or change.change_size < 0 %}
|
||||
<span class="text-danger">{{ change.change_size }}</span>
|
||||
{% else %}
|
||||
{{ change.change_size }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ change.page_url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir
|
||||
</a>
|
||||
{% if change.diff_url %}
|
||||
<a href="{{ change.diff_url }}" target="_blank" class="btn btn-sm btn-outline-secondary mt-1">
|
||||
<i class="bi bi-file-diff"></i> Diff
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucun changement récent n'a été trouvé.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des changements récents</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Cette page affiche les changements récents dans l'espace de noms français (FR:) du wiki OpenStreetMap.</p>
|
||||
<p>Ces informations sont utiles pour suivre les traductions manquantes et les mises à jour des pages wiki.</p>
|
||||
<p>Les données sont mises à jour automatiquement toutes les heures.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
121
templates/admin/wiki_suspicious_deletions.html.twig
Normal file
121
templates/admin/wiki_suspicious_deletions.html.twig
Normal file
|
@ -0,0 +1,121 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki avec suppressions suspectes{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki avec suppressions suspectes</h1>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
Cette page présente deux types de suppressions suspectes :
|
||||
<ul>
|
||||
<li><strong>Suppressions récentes</strong> : Détectées en temps réel dans les changements récents du
|
||||
wiki (suppressions > 20 caractères)
|
||||
</li>
|
||||
<li><strong>Différences de contenu</strong> : Pages françaises contenant significativement moins de mots
|
||||
que leurs équivalents anglais
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Suppressions récentes -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h2>Suppressions récentes suspectes</h2>
|
||||
{% if last_updated %}
|
||||
<small>Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if recent_deletions is defined and recent_deletions|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Suppression</th>
|
||||
<th>Date</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Commentaire</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for deletion in recent_deletions %}
|
||||
<tr>
|
||||
<td><strong>{{ deletion.page_title }}</strong></td>
|
||||
<td>
|
||||
<span class="badge bg-danger">{{ deletion.deletion_size }} caractères</span>
|
||||
</td>
|
||||
<td>{{ deletion.timestamp }}</td>
|
||||
<td>{{ deletion.user }}</td>
|
||||
<td>{{ deletion.comment }}</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ deletion.page_url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Voir la page">
|
||||
<i class="bi bi-eye"></i> Voir
|
||||
</a>
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?title={{ deletion.page_title|url_encode }}&action=history"
|
||||
target="_blank" class="btn btn-sm btn-outline-secondary"
|
||||
title="Historique">
|
||||
<i class="bi bi-clock-history"></i> Historique
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?hidebots=1&hidenewpages=1&hidecategorization=1&hideWikibase=1&hidelog=1&hidenewuserlog=1&namespace=202&limit=250&days=30&enhanced=1&title=Special:RecentChanges&urlversion=2"
|
||||
target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-arrow-right-circle"></i> Voir tous les changements récents sur le wiki
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune suppression suspecte récente n'a été détectée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Différences de contenu -->
|
||||
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des suppressions suspectes</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>Suppressions récentes</h5>
|
||||
<p>Les suppressions récentes sont détectées en analysant les changements récents du wiki OSM. Toute
|
||||
suppression de plus de 20 caractères est considérée comme potentiellement suspecte et mérite une
|
||||
vérification.</p>
|
||||
|
||||
<h5>Différences de contenu</h5>
|
||||
<p>Les différences de contenu sont identifiées lorsque la version française d'une page wiki contient
|
||||
significativement moins de mots que la version anglaise (plus de 30% de différence).</p>
|
||||
<p>Cela peut indiquer :</p>
|
||||
<ul>
|
||||
<li>Une traduction incomplète</li>
|
||||
<li>Des sections manquantes dans la version française</li>
|
||||
<li>Des mises à jour importantes dans la version anglaise qui n'ont pas été reportées en français
|
||||
</li>
|
||||
</ul>
|
||||
<p>Ces pages sont des candidates prioritaires pour une mise à jour de la traduction française.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
266
templates/admin/wiki_tag_proposals.html.twig
Normal file
266
templates/admin/wiki_tag_proposals.html.twig
Normal file
|
@ -0,0 +1,266 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Propositions de tags OSM{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.vote-bar {
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.vote-approve {
|
||||
background-color: #28a745;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-abstain {
|
||||
background-color: #ffc107;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-oppose {
|
||||
background-color: #dc3545;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-count {
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.voter-badge {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.voter-approve {
|
||||
background-color: rgba(40, 167, 69, 0.2);
|
||||
border: 1px solid rgba(40, 167, 69, 0.4);
|
||||
}
|
||||
|
||||
.voter-abstain {
|
||||
background-color: rgba(255, 193, 7, 0.2);
|
||||
border: 1px solid rgba(255, 193, 7, 0.4);
|
||||
}
|
||||
|
||||
.voter-oppose {
|
||||
background-color: rgba(220, 53, 69, 0.2);
|
||||
border: 1px solid rgba(220, 53, 69, 0.4);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Propositions de tags OSM</h1>
|
||||
<p class="lead">Liste des propositions de tags OpenStreetMap actuellement en cours de vote ou récemment modifiées.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Propositions en cours de vote -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>Propositions en cours de vote</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set voting_proposals = proposals|filter(p => p.type == 'voting') %}
|
||||
{% if voting_proposals|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Proposition</th>
|
||||
<th>Proposé par</th>
|
||||
<th>Votes</th>
|
||||
<th>Statut</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for proposal in voting_proposals %}
|
||||
<tr>
|
||||
<td><strong>{{ proposal.feature }}</strong></td>
|
||||
<td>
|
||||
{% if proposal.proposer %}
|
||||
<a href="https://wiki.openstreetmap.org/wiki/User:{{ proposal.proposer }}" target="_blank">
|
||||
{{ proposal.proposer }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-muted">Non spécifié</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if proposal.votes is defined and proposal.total_votes > 0 %}
|
||||
<div>
|
||||
<strong>{{ proposal.total_votes }} votes</strong>
|
||||
</div>
|
||||
<div class="vote-bar">
|
||||
{% if proposal.approve_percentage > 0 %}
|
||||
<div class="vote-approve" style="width: {{ proposal.approve_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.approve.count }} ({{ proposal.approve_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.abstain_percentage > 0 %}
|
||||
<div class="vote-abstain" style="width: {{ proposal.abstain_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.abstain.count }} ({{ proposal.abstain_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.oppose_percentage > 0 %}
|
||||
<div class="vote-oppose" style="width: {{ proposal.oppose_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.oppose.count }} ({{ proposal.oppose_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#voters{{ loop.index }}" aria-expanded="false" aria-controls="voters{{ loop.index }}">
|
||||
Voir les votants
|
||||
</button>
|
||||
<div class="collapse mt-2" id="voters{{ loop.index }}">
|
||||
{% if proposal.votes.approve.users|length > 0 %}
|
||||
<div class="mb-1">
|
||||
<strong>Approbations:</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.approve.users %}
|
||||
<span class="voter-badge voter-approve">{{ user.username }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.abstain.users|length > 0 %}
|
||||
<div class="mb-1">
|
||||
<strong>Abstentions:</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.abstain.users %}
|
||||
<span class="voter-badge voter-abstain">{{ user.username }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.oppose.users|length > 0 %}
|
||||
<div>
|
||||
<strong>Oppositions:</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.oppose.users %}
|
||||
<span class="voter-badge voter-oppose">{{ user.username }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Aucun vote</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-primary">En vote</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ proposal.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune proposition en cours de vote n'a été trouvée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Propositions récemment modifiées -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h2>Propositions récemment modifiées</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set recent_proposals = proposals|filter(p => p.type == 'recent') %}
|
||||
{% if recent_proposals|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Proposition</th>
|
||||
<th>Dernière modification</th>
|
||||
<th>Modifié par</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for proposal in recent_proposals %}
|
||||
<tr>
|
||||
<td><strong>{{ proposal.feature }}</strong></td>
|
||||
<td>{{ proposal.description }}</td>
|
||||
<td>{{ proposal.proposer }}</td>
|
||||
<td>
|
||||
<a href="{{ proposal.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune proposition récemment modifiée n'a été trouvée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des propositions de tags</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Les propositions de tags sont un processus communautaire pour introduire de nouveaux tags ou modifier des tags existants dans OpenStreetMap.</p>
|
||||
<p>Le processus typique comprend les étapes suivantes :</p>
|
||||
<ol>
|
||||
<li><strong>Brouillon</strong> : La proposition initiale est rédigée et discutée.</li>
|
||||
<li><strong>RFC (Request for Comments)</strong> : La proposition est ouverte aux commentaires de la communauté.</li>
|
||||
<li><strong>Vote</strong> : La proposition est soumise au vote de la communauté.</li>
|
||||
<li><strong>Approbation ou rejet</strong> : Selon les résultats du vote, la proposition est approuvée ou rejetée.</li>
|
||||
</ol>
|
||||
<p>Vous pouvez participer à ce processus en commentant les propositions ou en votant lorsqu'elles sont en phase de vote.</p>
|
||||
<div class="d-grid gap-2 col-md-6 mx-auto mt-3">
|
||||
<a href="https://wiki.openstreetmap.org/wiki/Proposed_features" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir toutes les propositions sur le wiki OSM
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
139
templates/base.html.twig
Normal file
139
templates/base.html.twig
Normal file
|
@ -0,0 +1,139 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
|
||||
|
||||
<link rel="icon" type="image/png" href="{{ asset('logo-osm.png') }}">
|
||||
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
|
||||
{% block stylesheets %}
|
||||
{{ encore_entry_link_tags('app') }}
|
||||
<link href='{{ asset('js/mapbox/mapbox-gl.css') }}' rel='stylesheet' />
|
||||
<!-- CSS Bootstrap -->
|
||||
<link rel="stylesheet" href="{{ asset('css/bootstrap-icons.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('js/bootstrap/bootstrap-icons.min.css') }}">
|
||||
<link href="{{ asset('js/bootstrap/bootstrap.min.css') }}" rel="stylesheet">
|
||||
<!-- CSS personnalisé -->
|
||||
<link rel="stylesheet" href="{{ asset('css/main.css') }}">
|
||||
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<header class="main-header">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-12">
|
||||
<a href="{{ path('app_public_index') }}" class="d-flex align-items-center">
|
||||
<h1 class="mb-0 mt-2">
|
||||
<img src="{{ asset('logo-osm.png') }}" alt="Logo OSM" class="me-2" style="width: 30px; height: 30px;">
|
||||
Qualiwiki OpenStreetMap</h1>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% for label, messages in app.flashes %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ label }} is-{{ label }} alert-dismissible fade show mt-3" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
{% include 'public/nav.html.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
<main class="body-landing">
|
||||
{% block body %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="main-footer">
|
||||
<div class="container">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
{% include 'public/nav.html.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="mb-2">OpenStreetMap Mon Commerce</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-12">
|
||||
<p class="mb-2">
|
||||
Licence AGPLv3+, fait par
|
||||
<a href="https://mastodon.cipherbliss.com/@tykayn">Tykayn</a> de
|
||||
<a href="https://www.cipherbliss.com">CipherBliss EI</a>,
|
||||
membre de la fédération des professionels d'OpenStreetMap
|
||||
</p>
|
||||
<p class="mb-2">
|
||||
<a href="https://www.openstreetmap.org/copyright">OpenStreetMap France</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-12">
|
||||
|
||||
<div id="userChangesHistory"></div>
|
||||
</div>
|
||||
<div class="col-md-4 col-12">
|
||||
<div id="qr-share" class="mb-12">
|
||||
partagez cette page :
|
||||
<br>
|
||||
<div id="qrcode"></div>
|
||||
</div>
|
||||
<div class="col-md-4 col-12">
|
||||
|
||||
<p class="mb-0">
|
||||
<a href="https://www.openstreetmap.org/copyright">Sources du logiciel</a>
|
||||
</p>
|
||||
<p class="mb-2">
|
||||
Sources des données : <a href="https://www.openstreetmap.org/">OpenStreetMap</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="mb-2">
|
||||
<a href="https://forum.openstreetmap.fr/t/osm-mon-commerce/34403/11" class="btn btn-outline-info ms-auto suggestion-float-btn" target="_blank" rel="noopener">
|
||||
<i class="bi bi-chat-dots"></i> Faire une suggestion
|
||||
</a>
|
||||
|
||||
<a href="https://osm-commerces.cipherbliss.com/api/v1/stats_geojson" target="_blank">Documentation de l'API (GeoJSON)</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{% block javascripts %}
|
||||
{{ encore_entry_script_tags('app') }}
|
||||
<script src="{{ asset('js/bootstrap/bootstrap.bundle.min.js') }}"></script>
|
||||
<script src='{{ asset('js/maplibre/maplibre-gl.js') }}'></script>
|
||||
|
||||
<!-- Script pour le tri automatique des tableaux -->
|
||||
{# <script src="{{ asset('js/bootstrap/Sortable.min.js') }}"></script> #}
|
||||
<script src="{{ asset('js/qrcode/qrcode.min.js') }}"></script>
|
||||
<script>
|
||||
new QRCode(document.getElementById('qrcode'), {
|
||||
text: window.location.href,
|
||||
width: 100,
|
||||
height: 100,
|
||||
colorDark : '#000000',
|
||||
colorLight : '#ffffff',
|
||||
correctLevel : QRCode.CorrectLevel.H
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block completion_progress %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
51
templates/base_embed.html.twig
Normal file
51
templates/base_embed.html.twig
Normal file
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
|
||||
|
||||
<link rel="icon" type="image/png" href="{{ asset('logo-osm.png') }}">
|
||||
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
|
||||
{% block stylesheets %}
|
||||
{{ encore_entry_link_tags('app') }}
|
||||
<link href='{{ asset('js/mapbox/mapbox-gl.css') }}' rel='stylesheet' />
|
||||
<!-- CSS Bootstrap -->
|
||||
<link rel="stylesheet" href="{{ asset('css/bootstrap-icons.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('js/bootstrap/bootstrap-icons.min.css') }}">
|
||||
<link href="{{ asset('js/bootstrap/bootstrap.min.css') }}" rel="stylesheet">
|
||||
<!-- CSS personnalisé -->
|
||||
<link rel="stylesheet" href="{{ asset('css/main.css') }}">
|
||||
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% for label, messages in app.flashes %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ label }} is-{{ label }} alert-dismissible fade show mt-3" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
<main class="body-landing">
|
||||
{% block body %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="main-footer">
|
||||
{% include 'public/nav.html.twig' %}
|
||||
</footer>
|
||||
|
||||
{% block javascripts %}
|
||||
{{ encore_entry_script_tags('app') }}
|
||||
<script src="{{ asset('js/bootstrap/bootstrap.bundle.min.js') }}"></script>
|
||||
<script src='{{ asset('js/maplibre/maplibre-gl.js') }}'></script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block completion_progress %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
57
templates/public/_wiki_navigation.html.twig
Normal file
57
templates/public/_wiki_navigation.html.twig
Normal file
|
@ -0,0 +1,57 @@
|
|||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ path('app_admin_wiki') }}">Wiki OSM</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#wikiNavbar" aria-controls="wikiNavbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="wikiNavbar">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki' %}active{% endif %}" href="{{ path('app_admin_wiki') }}">
|
||||
<i class="bi bi-list-ul"></i> Liste des pages
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_random_suggestion' %}active{% endif %}" href="{{ path('app_admin_wiki_random_suggestion') }}">
|
||||
<i class="bi bi-shuffle"></i> Suggestion aléatoire
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_tag_proposals' %}active{% endif %}" href="{{ path('app_admin_wiki_tag_proposals') }}">
|
||||
<i class="bi bi-tag"></i> Propositions de tags
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_archived_proposals' %}active{% endif %}" href="{{ path('app_admin_wiki_archived_proposals') }}">
|
||||
<i class="bi bi-archive"></i> Propositions archivées
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_suspicious_deletions' %}active{% endif %}" href="{{ path('app_admin_wiki_suspicious_deletions') }}">
|
||||
<i class="bi bi-exclamation-triangle"></i> Suppressions suspectes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_missing_translations' %}active{% endif %}" href="{{ path('app_admin_wiki_missing_translations') }}">
|
||||
<i class="bi bi-translate"></i> Pages sans traduction
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_pages_unavailable_in_french' %}active{% endif %}" href="{{ path('app_admin_wiki_pages_unavailable_in_french') }}">
|
||||
<i class="bi bi-globe"></i> Pages à traduire en français
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_osm_fr_groups' %}active{% endif %}" href="{{ path('app_admin_wiki_osm_fr_groups') }}">
|
||||
<i class="bi bi-people"></i> Groupes OSM-FR
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if app.request.get('_route') == 'app_admin_wiki_recent_changes' %}active{% endif %}" href="{{ path('app_admin_wiki_recent_changes') }}">
|
||||
<i class="bi bi-clock-history"></i> Changements récents
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
17
templates/public/nav.html.twig
Normal file
17
templates/public/nav.html.twig
Normal file
|
@ -0,0 +1,17 @@
|
|||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded shadow-sm">
|
||||
<div class="container-fluid">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="{{ path('app_public_index') }}">
|
||||
<i class="bi bi-house-fill"></i>
|
||||
{{ 'accueil'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
506
templates/public/wiki.html.twig
Normal file
506
templates/public/wiki.html.twig
Normal file
|
@ -0,0 +1,506 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki OSM{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki OpenStreetMap</h1>
|
||||
<p class="lead">Outil de qualité des des pages wiki OpenStreetMap en français et en anglais pour les clés OSM
|
||||
les plus utilisées.
|
||||
<a href="https://forum.openstreetmap.fr/t/fabriquer-un-outil-de-qualite-pour-le-wiki-osm/36814">
|
||||
Venez discuter QualiWiki sur le forum
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Liste des pages wiki</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th rowspan="2">Image</th>
|
||||
<th rowspan="2">Clé</th>
|
||||
<th colspan="4" class="text-center">Différences FR vs EN</th>
|
||||
<th rowspan="2" class="text-center">Score de<br>décrépitude</th>
|
||||
<th rowspan="2" class="text-center">Liens</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-center">Sections</th>
|
||||
<th class="text-center">Mots</th>
|
||||
<th class="text-center">Liens</th>
|
||||
<th class="text-center">Images</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, languages in wiki_pages %}
|
||||
{% if languages['en'] is defined and languages['fr'] is defined %}
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
<img src="{{ languages['en'].description_img_url }}" alt="image"
|
||||
style="height: 2rem;">
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ key }}</strong>
|
||||
</td>
|
||||
|
||||
{% set diff = page_differences[key] %}
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.section_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.section_diff_formatted }}</span>
|
||||
{% elseif diff.section_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.section_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.word_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.word_diff_formatted }}</span>
|
||||
{% elseif diff.word_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.word_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.link_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.link_diff_formatted }}</span>
|
||||
{% elseif diff.link_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.link_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if diff.media_diff > 0 %}
|
||||
<span class="badge bg-success">{{ diff.media_diff_formatted }}</span>
|
||||
{% elseif diff.media_diff < 0 %}
|
||||
<span class="badge bg-danger">{{ diff.media_diff_formatted }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% set score = languages['en'].staleness_score|default(0) %}
|
||||
{% if score > 50 %}
|
||||
<span class="badge bg-danger">{{ score }}</span>
|
||||
{% elseif score > 20 %}
|
||||
<span class="badge bg-warning text-dark">{{ score }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">{{ score }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ languages['en'].url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Version anglaise">
|
||||
<i class="bi bi-translate"></i> EN
|
||||
</a>
|
||||
<a href="{{ languages['fr'].url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-translate"></i> FR
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_compare', {'key': key}) }}"
|
||||
class="btn btn-sm btn-outline-secondary" title="Comparer les versions">
|
||||
<i class="bi bi-arrows-angle-expand"></i> Comparer
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if missing_translations|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h2>Pages manquantes en français ({{ missing_translations|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki ont une version anglaise mais pas de traduction française.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Clé</th>
|
||||
<th>Sections</th>
|
||||
<th>Mots</th>
|
||||
<th>Liens</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, page in missing_translations %}
|
||||
<tr>
|
||||
<td><strong>{{ key }}</strong></td>
|
||||
<td>{{ page.sections }}</td>
|
||||
<td>{{ page.word_count }}</td>
|
||||
<td>{{ page.link_count }}</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version anglaise">
|
||||
<i class="bi bi-flag-fill"></i> EN
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_create_french', {'key': key}) }}"
|
||||
class="btn btn-sm btn-success"
|
||||
title="Créer une traduction française">
|
||||
<i class="bi bi-plus-circle"></i> Traduire
|
||||
</a>
|
||||
{# <a href="{{ path('app_admin_wiki_compare', {'key': key}) }}" #}
|
||||
{# class="btn btn-sm btn-outline-secondary" #}
|
||||
{# title="Voir les détails et comparer"> #}
|
||||
{# <i class="bi bi-arrows-angle-expand"></i> Comparer #}
|
||||
{# </a> #}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if specific_pages is defined and specific_pages|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>Pages spécifiques ({{ specific_pages|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki sont des pages spécifiques qui ont été sélectionnées pour une comparaison
|
||||
particulière.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Raison</th>
|
||||
<th>Score de décrépitude</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in specific_pages %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if page.en_page.description_img_url is defined and page.en_page.description_img_url %}
|
||||
<div class="me-3">
|
||||
<img src="{{ page.en_page.description_img_url }}"
|
||||
alt="{{ page.key }}"
|
||||
style="max-width: 80px; max-height: 60px; object-fit: contain;">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<strong>{{ page.key }}</strong>
|
||||
<span class="badge bg-primary">Spécifique</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ page.reason }}
|
||||
</td>
|
||||
<td>
|
||||
{% if page.staleness_score is defined %}
|
||||
<div class="progress" style="height: 20px;">
|
||||
{% set score_class = page.staleness_score > 70 ? 'bg-danger' : (page.staleness_score > 40 ? 'bg-warning' : 'bg-success') %}
|
||||
<div class="progress-bar {{ score_class }}" role="progressbar"
|
||||
style="width: {{ page.staleness_score }}%;"
|
||||
aria-valuenow="{{ page.staleness_score }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
{{ page.staleness_score }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Non disponible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.en_page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Version anglaise">
|
||||
<i class="bi bi-translate"></i> EN
|
||||
</a>
|
||||
{% if page.fr_page %}
|
||||
<a href="{{ page.fr_page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-translate"></i> FR
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_compare', {'key': page.key}) }}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
title="Comparer les versions">
|
||||
<i class="bi bi-arrows-angle-expand"></i> Comparer
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ path('app_admin_wiki_create_french', {'key': page.key}) }}"
|
||||
class="btn btn-sm btn-success"
|
||||
title="Créer une traduction française">
|
||||
<i class="bi bi-plus-circle"></i> Traduire
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if pages_unavailable_in_english|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-info text-dark">
|
||||
<h2>Pages françaises non disponibles en Anglais ({{ pages_unavailable_in_english|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki ont une version française mais pas de traduction anglaise.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Score de décrépitude</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in pages_unavailable_in_english %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if page.description_img_url is defined and page.description_img_url %}
|
||||
<div class="me-3">
|
||||
<img src="{{ page.description_img_url }}" alt="{{ page.title }}"
|
||||
style="max-width: 80px; max-height: 60px; object-fit: contain;">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<strong>{{ page.title }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{% if page.outdatedness_score is defined %}
|
||||
<div class="progress" style="height: 20px;">
|
||||
{% set score_class = page.outdatedness_score > 70 ? 'bg-danger' : (page.outdatedness_score > 40 ? 'bg-warning' : 'bg-success') %}
|
||||
<div class="progress-bar {{ score_class }}" role="progressbar"
|
||||
style="width: {{ page.outdatedness_score }}%;"
|
||||
aria-valuenow="{{ page.outdatedness_score }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
{{ page.outdatedness_score }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Non disponible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-flag-fill"></i> FR
|
||||
</a>
|
||||
{% set en_url = page.url|replace({'FR:': ''}) %}
|
||||
<a href="{{ en_url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
title="Créer une traduction anglaise">
|
||||
<i class="bi bi-translate"></i> créer EN
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if newly_created_pages is defined and newly_created_pages|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2>Pages françaises récemment créées ({{ newly_created_pages|length }})</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages wiki françaises ont été récemment créées et étaient auparavant dans la liste des pages
|
||||
non disponibles en français.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Version anglaise</th>
|
||||
<th>Date de création</th>
|
||||
<th>Créée par</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in newly_created_pages %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<strong>{{ page.title }}</strong>
|
||||
<span class="badge bg-success">Nouvelle</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ page.en_title }}
|
||||
</td>
|
||||
<td>
|
||||
{{ page.created_at }}
|
||||
</td>
|
||||
<td>
|
||||
{{ page.created_by }}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-flag-fill"></i> FR
|
||||
</a>
|
||||
<a href="{{ page.en_url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Version anglaise">
|
||||
<i class="bi bi-flag-fill"></i> EN
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
le score de fraîcheur prend en compte d'avantage la différence entre le nombre de mots que l'ancienneté de
|
||||
modification.
|
||||
On compte aussi le nombre de sections et de liens.
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Graphe de décrépitude</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="decrepitudeChart" height="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Collect data from the table
|
||||
const labels = [];
|
||||
const scores = [];
|
||||
const colors = [];
|
||||
|
||||
{% for key, languages in wiki_pages %}
|
||||
{% if languages['en'] is defined and languages['fr'] is defined %}
|
||||
labels.push("{{ key }}");
|
||||
{% set score = languages['en'].staleness_score|default(0) %}
|
||||
scores.push({{ score }});
|
||||
|
||||
// Set color based on score
|
||||
{% if score > 50 %}
|
||||
colors.push('rgba(220, 53, 69, 0.7)'); // danger
|
||||
{% elseif score > 20 %}
|
||||
colors.push('rgba(255, 193, 7, 0.7)'); // warning
|
||||
{% else %}
|
||||
colors.push('rgba(25, 135, 84, 0.7)'); // success
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
// Sort data by score (descending)
|
||||
const indices = Array.from(Array(scores.length).keys())
|
||||
.sort((a, b) => scores[b] - scores[a]);
|
||||
|
||||
const sortedLabels = indices.map(i => labels[i]);
|
||||
const sortedScores = indices.map(i => scores[i]);
|
||||
const sortedColors = indices.map(i => colors[i]);
|
||||
|
||||
// Limit to top 20 pages for readability
|
||||
const displayLimit = 20;
|
||||
const displayLabels = sortedLabels.slice(0, displayLimit);
|
||||
const displayScores = sortedScores.slice(0, displayLimit);
|
||||
const displayColors = sortedColors.slice(0, displayLimit);
|
||||
|
||||
// Create the chart
|
||||
const ctx = document.getElementById('decrepitudeChart').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: displayLabels,
|
||||
datasets: [{
|
||||
label: 'Score de décrépitude',
|
||||
data: displayScores,
|
||||
backgroundColor: displayColors,
|
||||
borderColor: displayColors.map(c => c.replace('0.7', '1')),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
return `Score: ${context.raw}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Score de décrépitude (0-100)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
701
templates/public/wiki_archived_proposals.html.twig
Normal file
701
templates/public/wiki_archived_proposals.html.twig
Normal file
|
@ -0,0 +1,701 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Propositions archivées OSM{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.vote-bar {
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.vote-approve {
|
||||
background-color: #28a745;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-abstain {
|
||||
background-color: #ffc107;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-oppose {
|
||||
background-color: #dc3545;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-count {
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.proposal-card {
|
||||
margin-bottom: 1.5rem;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.proposal-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.proposal-metadata {
|
||||
font-size: 0.85rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.voter-badge {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.voter-approve {
|
||||
background-color: rgba(40, 167, 69, 0.2);
|
||||
border: 1px solid rgba(40, 167, 69, 0.4);
|
||||
}
|
||||
|
||||
.voter-abstain {
|
||||
background-color: rgba(255, 193, 7, 0.2);
|
||||
border: 1px solid rgba(255, 193, 7, 0.4);
|
||||
}
|
||||
|
||||
.voter-oppose {
|
||||
background-color: rgba(220, 53, 69, 0.2);
|
||||
border: 1px solid rgba(220, 53, 69, 0.4);
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.stats-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.stats-value {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stats-label {
|
||||
font-size: 0.9rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.top-voters-table th, .top-voters-table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.vote-duration {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #e9ecef;
|
||||
margin-top: 5px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
font-style: italic;
|
||||
color: #495057;
|
||||
background-color: #f8f9fa;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 3px solid #dee2e6;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Propositions archivées OpenStreetMap</h1>
|
||||
<p class="lead">Analyse des votes sur les propositions archivées du wiki OSM</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
<form class="d-inline-flex align-items-center" method="get" action="{{ path('app_admin_wiki_archived_proposals') }}">
|
||||
<div class="me-2">
|
||||
<label for="limit" class="me-2">Limiter à:</label>
|
||||
<select name="limit" id="limit" class="form-select form-select-sm d-inline-block" style="width: auto;">
|
||||
<option value="">Toutes les propositions</option>
|
||||
<option value="10" {% if limit == 10 %}selected{% endif %}>10 propositions</option>
|
||||
<option value="20" {% if limit == 20 %}selected{% endif %}>20 propositions</option>
|
||||
<option value="50" {% if limit == 50 %}selected{% endif %}>50 propositions</option>
|
||||
<option value="100" {% if limit == 100 %}selected{% endif %}>100 propositions</option>
|
||||
{% if limit and limit not in [10, 20, 50, 100] %}
|
||||
<option value="{{ limit }}" selected>{{ limit }} propositions</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</div>
|
||||
<input type="hidden" name="refresh" value="1">
|
||||
<button type="submit" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-arrow-clockwise"></i> Rafraîchir les données
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% if limit %}
|
||||
<div class="mt-2">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-info-circle"></i> Les données sont limitées à {{ limit }} propositions.
|
||||
<a href="{{ path('app_admin_wiki_archived_proposals') }}">Voir toutes les propositions</a>
|
||||
</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if statistics %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Statistiques globales</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.total_proposals }}</div>
|
||||
<div class="stats-label">Propositions analysées</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.total_votes }}</div>
|
||||
<div class="stats-label">Votes au total</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.avg_votes_per_proposal }}</div>
|
||||
<div class="stats-label">Votes par proposition (moyenne)</div>
|
||||
<div class="small text-muted">Excluant les propositions sans votes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.unique_voters }}</div>
|
||||
<div class="stats-label">Votants uniques</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.median_votes_per_proposal }}</div>
|
||||
<div class="stats-label">Votes par proposition (médiane)</div>
|
||||
<div class="small text-muted">Excluant les propositions sans votes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.std_dev_votes_per_proposal }}</div>
|
||||
<div class="stats-label">Votes par proposition (écart type)</div>
|
||||
<div class="small text-muted">Excluant les propositions sans votes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if statistics.avg_vote_duration_days is defined %}
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-3 col-sm-6 mb-3">
|
||||
<div class="card stats-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="stats-value">{{ statistics.avg_vote_duration_days }}</div>
|
||||
<div class="stats-label">Durée moyenne des votes (jours)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if statistics.status_distribution is defined and statistics.status_distribution|length > 0 %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<h4>Répartition par statut</h4>
|
||||
<div class="chart-container">
|
||||
<canvas id="statusChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4>Détail des statuts</h4>
|
||||
<table class="table table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Statut</th>
|
||||
<th>Nombre</th>
|
||||
<th>Pourcentage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for status, count in statistics.status_distribution %}
|
||||
<tr>
|
||||
<td>{{ status }}</td>
|
||||
<td>{{ count }}</td>
|
||||
<td>{{ (count / statistics.total_proposals * 100)|round(1) }}%</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<h4>Répartition des années des propositions</h4>
|
||||
<div class="chart-container">
|
||||
<canvas id="yearDistributionChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Contributeurs les plus actifs</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover top-voters-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Total votes</th>
|
||||
<th>Approbations</th>
|
||||
<th>Abstentions</th>
|
||||
<th>Oppositions</th>
|
||||
<th>Répartition</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for voter in statistics.top_voters %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>
|
||||
<a href="https://wiki.openstreetmap.org/wiki/User:{{ voter.username }}" target="_blank">
|
||||
{{ voter.username }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ voter.total }}</td>
|
||||
<td>{{ voter.approve }}</td>
|
||||
<td>{{ voter.abstain }}</td>
|
||||
<td>{{ voter.oppose }}</td>
|
||||
<td>
|
||||
<div class="vote-bar">
|
||||
{% if voter.approve > 0 %}
|
||||
<div class="vote-approve" style="width: {{ (voter.approve / voter.total * 100)|round }}%">
|
||||
<span class="vote-count">{{ (voter.approve / voter.total * 100)|round }}%</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if voter.abstain > 0 %}
|
||||
<div class="vote-abstain" style="width: {{ (voter.abstain / voter.total * 100)|round }}%">
|
||||
<span class="vote-count">{{ (voter.abstain / voter.total * 100)|round }}%</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if voter.oppose > 0 %}
|
||||
<div class="vote-oppose" style="width: {{ (voter.oppose / voter.total * 100)|round }}%">
|
||||
<span class="vote-count">{{ (voter.oppose / voter.total * 100)|round }}%</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Liste des propositions archivées</h2>
|
||||
<div>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm filter-btn active" data-filter="all">Toutes</button>
|
||||
<button type="button" class="btn btn-outline-success btn-sm filter-btn" data-filter="approved">Approuvées</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm filter-btn" data-filter="rejected">Rejetées</button>
|
||||
<button type="button" class="btn btn-outline-warning btn-sm filter-btn" data-filter="neutral">Neutres</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row" id="proposals-container">
|
||||
{% for proposal in proposals %}
|
||||
{% set total_votes = proposal.votes.approve.count + proposal.votes.oppose.count + proposal.votes.abstain.count %}
|
||||
{% set is_approved = proposal.votes.approve.count > proposal.votes.oppose.count %}
|
||||
{% set is_rejected = proposal.votes.approve.count < proposal.votes.oppose.count %}
|
||||
{% set is_neutral = proposal.votes.approve.count == proposal.votes.oppose.count %}
|
||||
|
||||
<div class="col-md-6 mb-4 proposal-item {% if is_approved %}approved{% elseif is_rejected %}rejected{% else %}neutral{% endif %}">
|
||||
<div class="card proposal-card h-100 {% if is_approved %}border-success{% elseif is_rejected %}border-danger{% else %}border-warning{% endif %}">
|
||||
<div class="card-header {% if is_approved %}bg-success text-white{% elseif is_rejected %}bg-danger text-white{% else %}bg-warning{% endif %}">
|
||||
<h5 class="card-title mb-0">
|
||||
<a href="{{ proposal.url }}" target="_blank" class="text-white">
|
||||
{{ proposal.title }}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="proposal-metadata mb-3">
|
||||
{% if proposal.proposer %}
|
||||
<div><strong>Proposé par :</strong> {{ proposal.proposer }}</div>
|
||||
{% endif %}
|
||||
{% if proposal.last_modified %}
|
||||
<div><strong>Dernière modification :</strong> {{ proposal.last_modified }}</div>
|
||||
{% endif %}
|
||||
<div><strong>Sections :</strong> {{ proposal.section_count }}</div>
|
||||
<div><strong>Liens :</strong> {{ proposal.link_count }}</div>
|
||||
<div><strong>Mots :</strong> {{ proposal.word_count }}</div>
|
||||
{% if proposal.votes.duration_days is defined %}
|
||||
<div class="mt-2">
|
||||
<span class="vote-duration">
|
||||
<i class="bi bi-calendar-range"></i>
|
||||
<strong>Durée du vote :</strong> {{ proposal.votes.duration_days }} jours
|
||||
({{ proposal.votes.first_vote }} → {{ proposal.votes.last_vote }})
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if total_votes > 0 %}
|
||||
<h6>Résultats des votes ({{ total_votes }} votes)</h6>
|
||||
<div class="vote-bar">
|
||||
{% if proposal.votes.approve.count > 0 %}
|
||||
<div class="vote-approve" style="width: {{ proposal.approve_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.approve.count }} ({{ proposal.approve_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.votes.abstain.count > 0 %}
|
||||
<div class="vote-abstain" style="width: {{ proposal.abstain_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.abstain.count }} ({{ proposal.abstain_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.votes.oppose.count > 0 %}
|
||||
<div class="vote-oppose" style="width: {{ proposal.oppose_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.oppose.count }} ({{ proposal.oppose_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="accordion" id="votersAccordion{{ loop.index }}">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="headingVoters{{ loop.index }}">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseVoters{{ loop.index }}" aria-expanded="false"
|
||||
aria-controls="collapseVoters{{ loop.index }}">
|
||||
Voir les votants
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseVoters{{ loop.index }}" class="accordion-collapse collapse"
|
||||
aria-labelledby="headingVoters{{ loop.index }}"
|
||||
data-bs-parent="#votersAccordion{{ loop.index }}">
|
||||
<div class="accordion-body">
|
||||
{% if proposal.votes.approve.users|length > 0 %}
|
||||
<div class="mb-2">
|
||||
<strong>Approbations ({{ proposal.votes.approve.count }}):</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.approve.users %}
|
||||
<div class="mb-2">
|
||||
<span class="voter-badge voter-approve">{{ user.username }}</span>
|
||||
{% if user.comment is defined and user.comment is not empty %}
|
||||
<div class="comment-text">{{ user.comment }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.abstain.users|length > 0 %}
|
||||
<div class="mb-2">
|
||||
<strong>Abstentions ({{ proposal.votes.abstain.count }}):</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.abstain.users %}
|
||||
<div class="mb-2">
|
||||
<span class="voter-badge voter-abstain">{{ user.username }}</span>
|
||||
{% if user.comment is defined and user.comment is not empty %}
|
||||
<div class="comment-text">{{ user.comment }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.oppose.users|length > 0 %}
|
||||
<div>
|
||||
<strong>Oppositions ({{ proposal.votes.oppose.count }}):</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.oppose.users %}
|
||||
<div class="mb-2">
|
||||
<span class="voter-badge voter-oppose">{{ user.username }}</span>
|
||||
{% if user.comment is defined and user.comment is not empty %}
|
||||
<div class="comment-text">{{ user.comment }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-secondary">
|
||||
Aucun vote trouvé pour cette proposition.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{{ proposal.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="bi bi-exclamation-triangle"></i> Aucune proposition archivée n'a été trouvée.
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Filtering functionality
|
||||
const filterButtons = document.querySelectorAll('.filter-btn');
|
||||
const proposalItems = document.querySelectorAll('.proposal-item');
|
||||
|
||||
filterButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active class from all buttons
|
||||
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked button
|
||||
this.classList.add('active');
|
||||
|
||||
const filter = this.getAttribute('data-filter');
|
||||
|
||||
// Show/hide proposals based on filter
|
||||
proposalItems.forEach(item => {
|
||||
if (filter === 'all') {
|
||||
item.style.display = 'block';
|
||||
} else if (filter === 'approved' && item.classList.contains('approved')) {
|
||||
item.style.display = 'block';
|
||||
} else if (filter === 'rejected' && item.classList.contains('rejected')) {
|
||||
item.style.display = 'block';
|
||||
} else if (filter === 'neutral' && item.classList.contains('neutral')) {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize status distribution chart if it exists
|
||||
const statusChartCanvas = document.getElementById('statusChart');
|
||||
if (statusChartCanvas) {
|
||||
// Get status distribution data from the template
|
||||
const statusDistribution = {{ statistics.status_distribution|json_encode|raw }};
|
||||
|
||||
if (statusDistribution && Object.keys(statusDistribution).length > 0) {
|
||||
const labels = Object.keys(statusDistribution);
|
||||
const data = Object.values(statusDistribution);
|
||||
|
||||
// Generate colors for each status
|
||||
const backgroundColors = [
|
||||
'#28a745', // Approved - green
|
||||
'#dc3545', // Rejected - red
|
||||
'#ffc107', // Voting/Proposed - yellow
|
||||
'#6c757d', // Abandoned/Inactive - gray
|
||||
'#17a2b8', // Other statuses - blue
|
||||
'#6610f2', // Other statuses - purple
|
||||
'#fd7e14', // Other statuses - orange
|
||||
'#20c997', // Other statuses - teal
|
||||
'#e83e8c' // Other statuses - pink
|
||||
];
|
||||
|
||||
// Create the chart
|
||||
new Chart(statusChartCanvas, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: data,
|
||||
backgroundColor: backgroundColors.slice(0, labels.length),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'right',
|
||||
labels: {
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
const label = context.label || '';
|
||||
const value = context.raw || 0;
|
||||
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||
const percentage = Math.round((value / total) * 100);
|
||||
return `${label}: ${value} (${percentage}%)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize year distribution chart
|
||||
const yearChartCanvas = document.getElementById('yearDistributionChart');
|
||||
if (yearChartCanvas) {
|
||||
// Get proposals data from the template
|
||||
const proposals = {{ proposals|json_encode|raw }};
|
||||
|
||||
if (proposals && proposals.length > 0) {
|
||||
// Extract years from last_modified dates
|
||||
const yearCounts = {};
|
||||
|
||||
proposals.forEach(proposal => {
|
||||
if (proposal.last_modified) {
|
||||
// Extract year from the date string (format: "DD Month YYYY")
|
||||
const yearMatch = proposal.last_modified.match(/\d{4}$/);
|
||||
if (yearMatch) {
|
||||
const year = yearMatch[0];
|
||||
yearCounts[year] = (yearCounts[year] || 0) + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sort years chronologically
|
||||
const sortedYears = Object.keys(yearCounts).sort();
|
||||
const counts = sortedYears.map(year => yearCounts[year]);
|
||||
|
||||
// Generate a color gradient for the bars
|
||||
const colors = sortedYears.map((year, index) => {
|
||||
// Create a gradient from blue to green
|
||||
const ratio = index / (sortedYears.length - 1 || 1);
|
||||
return `rgba(${Math.round(33 + (20 * ratio))}, ${Math.round(150 + (50 * ratio))}, ${Math.round(243 - (100 * ratio))}, 0.7)`;
|
||||
});
|
||||
|
||||
// Create the chart
|
||||
new Chart(yearChartCanvas, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: sortedYears,
|
||||
datasets: [{
|
||||
label: 'Nombre de propositions',
|
||||
data: counts,
|
||||
backgroundColor: colors,
|
||||
borderColor: colors.map(color => color.replace('0.7', '1')),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
precision: 0
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Nombre de propositions'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Année'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
title: function(context) {
|
||||
return `Année ${context[0].label}`;
|
||||
},
|
||||
label: function(context) {
|
||||
const count = context.raw;
|
||||
return count > 1 ? `${count} propositions` : `${count} proposition`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
1030
templates/public/wiki_compare.html.twig
Normal file
1030
templates/public/wiki_compare.html.twig
Normal file
File diff suppressed because it is too large
Load diff
115
templates/public/wiki_create_french.html.twig
Normal file
115
templates/public/wiki_create_french.html.twig
Normal file
|
@ -0,0 +1,115 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Créer une traduction française pour {{ key }}{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.iframe-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: calc(100vh - 200px);
|
||||
min-height: 600px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.iframe-container iframe {
|
||||
flex: 1;
|
||||
border: 1px solid #dee2e6;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator {
|
||||
width: 10px;
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator::after {
|
||||
content: "⟺";
|
||||
font-size: 20px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.iframe-header {
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-bottom: none;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.iframe-container {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.iframe-container iframe {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.iframe-container .iframe-separator::after {
|
||||
content: "⟻⟼";
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container-fluid mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Créer une traduction française pour "{{ key }}"</h1>
|
||||
<p class="lead">Utilisez cette page pour traduire la page wiki en français. La page anglaise est affichée à gauche pour référence, et le formulaire d'édition de la page française est à droite.</p>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Conseils pour la traduction :
|
||||
<ul>
|
||||
<li>Conservez la structure de la page anglaise (sections, sous-sections, etc.)</li>
|
||||
<li>Traduisez le contenu en français en adaptant les exemples au contexte français si nécessaire</li>
|
||||
<li>N'hésitez pas à consulter d'autres pages françaises pour vous inspirer du style et de la terminologie</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="iframe-header">
|
||||
<i class="bi bi-flag-fill"></i> Version anglaise (référence)
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="iframe-header">
|
||||
<i class="bi bi-translate"></i> <a href="{{ french_edit_url }}">Création de la version française</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="iframe-container">
|
||||
<iframe src="{{ english_url }}" title="Version anglaise"></iframe>
|
||||
<div class="iframe-separator"></div>
|
||||
<iframe src="{{ french_edit_url }}" title="Édition française"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
<a href="{{ english_url }}" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Ouvrir la version anglaise dans un nouvel onglet
|
||||
</a>
|
||||
<a href="{{ french_edit_url }}" target="_blank" class="btn btn-outline-info">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Ouvrir l'éditeur français dans un nouvel onglet
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
83
templates/public/wiki_missing_translations.html.twig
Normal file
83
templates/public/wiki_missing_translations.html.twig
Normal file
|
@ -0,0 +1,83 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki françaises sans traduction anglaise{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki françaises sans traduction anglaise</h1>
|
||||
<p class="lead">Liste des pages françaises du wiki OSM qui n'ont pas de traduction en anglais.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Pages françaises uniquement</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if untranslated_pages|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Clé</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in untranslated_pages %}
|
||||
<tr>
|
||||
<td><strong>{{ page.title }}</strong></td>
|
||||
<td>{{ page.key }}</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank" class="btn btn-sm btn-outline-info" title="Version française">
|
||||
<i class="bi bi-translate"></i> FR
|
||||
</a>
|
||||
<a href="https://wiki.openstreetmap.org/wiki/{{ page.key }}" target="_blank" class="btn btn-sm btn-success" title="Créer la version anglaise">
|
||||
<i class="bi bi-plus-circle"></i> Créer EN
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune page française sans traduction anglaise n'a été trouvée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des pages sans traduction anglaise</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages sont des contenus originaux créés en français qui n'ont pas encore été traduits en anglais.</p>
|
||||
<p>Bien que la langue principale du wiki OSM soit l'anglais, il est parfois utile de créer d'abord du contenu dans sa langue maternelle, puis de le traduire.</p>
|
||||
<p>Contribuer à la traduction de ces pages en anglais permet de :</p>
|
||||
<ul>
|
||||
<li>Partager les connaissances avec la communauté internationale</li>
|
||||
<li>Améliorer la visibilité des contributions françaises</li>
|
||||
<li>Faciliter la collaboration entre contributeurs de différentes langues</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
238
templates/public/wiki_osm_fr_groups.html.twig
Normal file
238
templates/public/wiki_osm_fr_groups.html.twig
Normal file
|
@ -0,0 +1,238 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Groupes OSM-FR{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Groupes OSM-FR</h1>
|
||||
<p class="lead">Liste des groupes de travail et des groupes locaux d'OpenStreetMap France.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Carte uMap des groupes locaux -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>
|
||||
<a href="{{ umap_url }}">
|
||||
|
||||
Carte des groupes locaux
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="ratio ratio-16x9">
|
||||
<iframe src="{{ umap_url }}" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a href="{{ umap_url }}" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir la carte en plein écran
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Groupes de travail -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2>Groupes de travail</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if working_groups|length > 0 %}
|
||||
<div class="accordion" id="workingGroupsAccordion">
|
||||
{% for category, groups in working_groups %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading{{ loop.index }}">
|
||||
<button class="accordion-button {% if not loop.first %}collapsed{% endif %}"
|
||||
type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapse{{ loop.index }}"
|
||||
aria-expanded="{{ loop.first ? 'true' : 'false' }}"
|
||||
aria-controls="collapse{{ loop.index }}">
|
||||
{{ category }} ({{ groups|length }})
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse{{ loop.index }}"
|
||||
class="accordion-collapse collapse {% if loop.first %}show{% endif %}"
|
||||
aria-labelledby="heading{{ loop.index }}" data-bs-parent="#workingGroupsAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="list-group">
|
||||
{% for group in groups %}
|
||||
<a href="{{ group.url }}" target="_blank"
|
||||
class="list-group-item list-group-item-action">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">{{ group.name }}</h5>
|
||||
</div>
|
||||
{% if group.description %}
|
||||
<p class="mb-1">{{ group.description }}</p>
|
||||
{% endif %}
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki
|
||||
</small>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucun groupe de travail n'a été trouvé.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Groupes locaux -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h2>Groupes locaux</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if local_groups|length > 0 %}
|
||||
<!-- Filtres -->
|
||||
<div class="mb-4">
|
||||
<div class="btn-group" role="group" aria-label="Filtres">
|
||||
<button type="button" class="btn btn-outline-primary active filter-btn" data-filter="all">Tous</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="wiki">Wiki</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="framacalc">Framacalc</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="has-wiki">Avec page wiki</button>
|
||||
<button type="button" class="btn btn-outline-primary filter-btn" data-filter="no-wiki">Sans page wiki</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
{% for group in local_groups %}
|
||||
{% set source = group.source|default('wiki') %}
|
||||
{% set has_wiki = group.has_wiki_page|default(true) %}
|
||||
{% set filter_classes = source ~ ' ' ~ (has_wiki ? 'has-wiki' : 'no-wiki') %}
|
||||
|
||||
<div class="col group-item {{ filter_classes }}">
|
||||
<div class="card h-100 {% if source == 'framacalc' and not has_wiki %}border-danger{% endif %}">
|
||||
{% if source == 'framacalc' %}
|
||||
<div class="card-header bg-light">
|
||||
<span class="badge bg-secondary">Framacalc</span>
|
||||
{% if has_wiki %}
|
||||
<span class="badge bg-success">Page wiki</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">Pas de page wiki</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ group.name }}</h5>
|
||||
{% if group.description %}
|
||||
<p class="card-text">{{ group.description }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if source == 'framacalc' and group.contact %}
|
||||
<p class="card-text"><small class="text-muted">Contact: {{ group.contact }}</small></p>
|
||||
{% endif %}
|
||||
|
||||
{% if source == 'framacalc' and group.website %}
|
||||
<p class="card-text">
|
||||
<a href="{{ group.website }}" target="_blank" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-globe"></i> Site web
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
{% if source == 'wiki' or has_wiki %}
|
||||
<a href="{{ group.url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="https://wiki.openstreetmap.org/wiki/Special:Search?search={{ group.name|url_encode }}"
|
||||
target="_blank" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-search"></i> Rechercher sur le wiki
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- JavaScript pour les filtres -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const filterButtons = document.querySelectorAll('.filter-btn');
|
||||
const groupItems = document.querySelectorAll('.group-item');
|
||||
|
||||
filterButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active class from all buttons
|
||||
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked button
|
||||
this.classList.add('active');
|
||||
|
||||
const filter = this.getAttribute('data-filter');
|
||||
|
||||
// Show/hide items based on filter
|
||||
groupItems.forEach(item => {
|
||||
if (filter === 'all') {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
if (item.classList.contains(filter)) {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucun groupe local n'a été trouvé.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des groupes OSM-FR</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>Groupes de travail</h5>
|
||||
<p>Les groupes de travail sont des équipes thématiques qui se concentrent sur des aspects spécifiques
|
||||
d'OpenStreetMap en France. Ils permettent de coordonner les efforts sur des sujets particuliers
|
||||
comme l'import de données, la cartographie des transports, etc.</p>
|
||||
|
||||
<h5>Groupes locaux</h5>
|
||||
<p>Les groupes locaux sont des communautés géographiques de contributeurs OpenStreetMap. Ils organisent
|
||||
des rencontres, des ateliers de cartographie et d'autres événements pour promouvoir OSM dans leur
|
||||
région.</p>
|
||||
|
||||
<div class="d-grid gap-2 col-md-6 mx-auto mt-3">
|
||||
<a href="https://wiki.openstreetmap.org/wiki/France/OSM-FR/Groupes_de_travail" target="_blank"
|
||||
class="btn btn-outline-success">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir tous les groupes de travail sur le wiki
|
||||
</a>
|
||||
<a href="https://wiki.openstreetmap.org/wiki/France/OSM-FR#Groupes_locaux" target="_blank"
|
||||
class="btn btn-outline-info">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir tous les groupes locaux sur le wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
215
templates/public/wiki_pages_unavailable_in_french.html.twig
Normal file
215
templates/public/wiki_pages_unavailable_in_french.html.twig
Normal file
|
@ -0,0 +1,215 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki non disponibles en français{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki non disponibles en français</h1>
|
||||
<p class="lead">Liste des pages du wiki OSM qui n'ont pas de traduction française, groupées par langue d'origine.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>Statistiques</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Total des pages</h5>
|
||||
<p class="card-text display-4">{{ all_pages|length }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-success mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Pages en anglais</h5>
|
||||
<p class="card-text display-4">{{ grouped_pages['En']|default([])|length }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-info mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Langues différentes</h5>
|
||||
<p class="card-text display-4">{{ grouped_pages|length }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pages groupées par langue -->
|
||||
<div class="accordion" id="languageAccordion">
|
||||
{% for lang_prefix, pages in grouped_pages %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading{{ lang_prefix }}">
|
||||
<button class="accordion-button {% if lang_prefix != 'En' %}collapsed{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ lang_prefix }}" aria-expanded="{{ lang_prefix == 'En' ? 'true' : 'false' }}" aria-controls="collapse{{ lang_prefix }}">
|
||||
{% if lang_prefix == 'En' %}
|
||||
<strong class="text-success">Pages en anglais ({{ pages|length }})</strong>
|
||||
{% elseif lang_prefix == 'Other' %}
|
||||
<strong>Autres pages ({{ pages|length }})</strong>
|
||||
{% else %}
|
||||
<strong>Pages en {{ lang_prefix }} ({{ pages|length }})</strong>
|
||||
{% endif %}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse{{ lang_prefix }}" class="accordion-collapse collapse {% if lang_prefix == 'En' %}show{% endif %}" aria-labelledby="heading{{ lang_prefix }}" data-bs-parent="#languageAccordion">
|
||||
<div class="accordion-body">
|
||||
{% if lang_prefix == 'En' %}
|
||||
<div class="mb-3">
|
||||
<button id="copyEnglishTitlesBtn" class="btn btn-outline-primary">
|
||||
<i class="bi bi-clipboard"></i> Copier les titres au format MediaWiki
|
||||
</button>
|
||||
<span id="copyStatus" class="ms-2 text-success" style="display: none;">
|
||||
<i class="bi bi-check-circle"></i> Copié !
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Score de décrépitude</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for page in pages %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if page.description_img_url is defined and page.description_img_url %}
|
||||
<div class="me-3">
|
||||
<img src="{{ page.description_img_url }}" alt="{{ page.title }}"
|
||||
style="max-width: 80px; max-height: 60px; object-fit: contain;">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<strong>{{ page.title }}</strong>
|
||||
{% if page.is_english %}
|
||||
<span class="badge bg-success">Priorité</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{% if page.outdatedness_score is defined %}
|
||||
<div class="progress" style="height: 20px;">
|
||||
{% set score_class = page.outdatedness_score > 70 ? 'bg-danger' : (page.outdatedness_score > 40 ? 'bg-warning' : 'bg-success') %}
|
||||
<div class="progress-bar {{ score_class }}" role="progressbar"
|
||||
style="width: {{ page.outdatedness_score }}%;"
|
||||
aria-valuenow="{{ page.outdatedness_score }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
{{ page.outdatedness_score }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Non disponible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ page.url }}" target="_blank" class="btn btn-sm btn-outline-primary" title="Voir la page originale">
|
||||
<i class="bi bi-eye"></i> Voir
|
||||
</a>
|
||||
{% set fr_url = page.url|replace({'/wiki/': '/wiki/FR:'}) %}
|
||||
<a href="{{ fr_url }}" target="_blank" class="btn btn-sm btn-success" title="Créer la traduction française">
|
||||
<i class="bi bi-plus-circle"></i> Traduire
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="card mt-4 mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des pages non disponibles en français</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ces pages sont des contenus du wiki OpenStreetMap qui n'ont pas encore été traduits en français.</p>
|
||||
<p>Contribuer à la traduction de ces pages permet de :</p>
|
||||
<ul>
|
||||
<li>Rendre la documentation OSM plus accessible aux contributeurs francophones</li>
|
||||
<li>Améliorer la qualité des contributions en français</li>
|
||||
<li>Faciliter l'apprentissage et l'utilisation d'OpenStreetMap pour les nouveaux utilisateurs</li>
|
||||
</ul>
|
||||
<p><strong>Priorité aux pages anglaises :</strong> Les pages commençant par "En:" sont prioritaires car l'anglais est la langue principale du wiki OSM.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const copyButton = document.getElementById('copyEnglishTitlesBtn');
|
||||
const copyStatus = document.getElementById('copyStatus');
|
||||
|
||||
if (copyButton) {
|
||||
copyButton.addEventListener('click', function() {
|
||||
// Get all English page titles from the table
|
||||
const englishSection = document.getElementById('collapseEn');
|
||||
const titleElements = englishSection.querySelectorAll('tbody tr td:first-child strong');
|
||||
|
||||
// Format titles in MediaWiki format
|
||||
let mediawikiText = '';
|
||||
const rows = englishSection.querySelectorAll('tbody tr');
|
||||
|
||||
rows.forEach(function(row) {
|
||||
const title = row.querySelector('td:first-child strong').textContent.trim();
|
||||
const imgElement = row.querySelector('td:first-child img');
|
||||
|
||||
if (imgElement) {
|
||||
const imgSrc = imgElement.getAttribute('src');
|
||||
mediawikiText += '* [[' + title + ']] - Image: ' + imgSrc + '\n';
|
||||
} else {
|
||||
mediawikiText += '* [[' + title + ']]\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Copy to clipboard
|
||||
navigator.clipboard.writeText(mediawikiText).then(function() {
|
||||
// Show success message
|
||||
copyStatus.style.display = 'inline';
|
||||
|
||||
// Hide success message after 3 seconds
|
||||
setTimeout(function() {
|
||||
copyStatus.style.display = 'none';
|
||||
}, 3000);
|
||||
}).catch(function(err) {
|
||||
console.error('Erreur lors de la copie: ', err);
|
||||
alert('Erreur lors de la copie dans le presse-papier. Veuillez réessayer.');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
124
templates/public/wiki_random_suggestion.html.twig
Normal file
124
templates/public/wiki_random_suggestion.html.twig
Normal file
|
@ -0,0 +1,124 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Suggestion de page Wiki à améliorer{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Suggestion de page Wiki à améliorer</h1>
|
||||
<p class="lead">Voici une page wiki qui a besoin d'être améliorée.</p>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>{{ page.key }}</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<h3>Raisons d'amélioration</h3>
|
||||
<p>{{ page.reason }}</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h3>Version anglaise</h3>
|
||||
<p class="mb-0">
|
||||
<small>Dernière modification: {{ page.en_page.last_modified }}</small>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group mb-3">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Sections
|
||||
<span class="badge bg-primary rounded-pill">{{ page.en_page.sections }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Mots
|
||||
<span class="badge bg-primary rounded-pill">{{ page.en_page.word_count|default(0) }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Liens
|
||||
<span class="badge bg-primary rounded-pill">{{ page.en_page.link_count|default(0) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{{ page.en_page.url }}" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir la page anglaise
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h3>Version française</h3>
|
||||
{% if page.fr_page %}
|
||||
<p class="mb-0">
|
||||
<small>Dernière modification: {{ page.fr_page.last_modified }}</small>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="mb-0">
|
||||
<small>Page non existante</small>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if page.fr_page %}
|
||||
<ul class="list-group mb-3">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Sections
|
||||
<span class="badge bg-info rounded-pill">{{ page.fr_page.sections }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Mots
|
||||
<span class="badge bg-info rounded-pill">{{ page.fr_page.word_count|default(0) }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Liens
|
||||
<span class="badge bg-info rounded-pill">{{ page.fr_page.link_count|default(0) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{{ page.fr_page.url }}" target="_blank" class="btn btn-outline-info">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir la page française
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<p><i class="bi bi-exclamation-triangle"></i> <strong>La page wiki pour la clé
|
||||
"{{ page.key }}" n'existe pas en français.</strong></p>
|
||||
<p>Vous pouvez contribuer en créant cette page sur le wiki OpenStreetMap.</p>
|
||||
</div>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?title=FR:Key:{{ page.key }}&action=edit"
|
||||
target="_blank" class="btn btn-success">
|
||||
<i class="bi bi-plus-circle"></i> Créer la page française
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-grid gap-2">
|
||||
<a href="{{ path('app_admin_wiki_compare', {'key': page.key}) }}" class="btn btn-primary">
|
||||
<i class="bi bi-arrows-angle-expand"></i> Voir la comparaison détaillée
|
||||
</a>
|
||||
<a href="{{ path('app_admin_wiki_random_suggestion') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-shuffle"></i> Autre suggestion aléatoire
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
156
templates/public/wiki_recent_changes.html.twig
Normal file
156
templates/public/wiki_recent_changes.html.twig
Normal file
|
@ -0,0 +1,156 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Changements récents Wiki OSM{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Changements récents Wiki OpenStreetMap</h1>
|
||||
<p class="lead">Liste des changements récents dans l'espace de noms français du wiki OpenStreetMap.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if team_members|length > 0 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2>Équipe Wiki OSM FR</h2>
|
||||
<p class="mb-0">Contributeurs classés par nombre de modifications</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
{% for member in team_members %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="{{ member.user_url }}" target="_blank" class="text-decoration-none">
|
||||
<span class="fw-bold">{{ member.username }}</span>
|
||||
</a>
|
||||
<span class="badge bg-primary ms-2">{{ member.contributions }}</span>
|
||||
<div class="ms-2 small">
|
||||
<span class="text-success" title="Caractères ajoutés">+{{ member.chars_added }}</span>
|
||||
{% if member.chars_changed > 0 %}
|
||||
<span class="text-warning" title="Caractères modifiés">~{{ member.chars_changed }}</span>
|
||||
{% endif %}
|
||||
{% if member.chars_deleted > 0 %}
|
||||
<span class="text-danger" title="Caractères supprimés">-{{ member.chars_deleted }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>Changements récents</h2>
|
||||
<p class="mb-0">
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?hidebots=1&hidepreviousrevisions=1&hidecategorization=1&hideWikibase=1&hidelog=1&hidenewuserlog=1&namespace=202&limit=500&days=30&enhanced=1&title=Special:RecentChanges&urlversion=2"
|
||||
target="_blank" class="text-white">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir sur le wiki OSM
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if recent_changes|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Date</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Commentaire</th>
|
||||
<th>Taille</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for change in recent_changes %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
<a href="{{ change.page_url }}" target="_blank" class="text-decoration-none">
|
||||
{{ change.page_name }}
|
||||
</a>
|
||||
</strong>
|
||||
</td>
|
||||
<td>{{ change.timestamp }}</td>
|
||||
<td>
|
||||
{% if change.user_url %}
|
||||
<a href="{{ change.user_url }}" target="_blank" class="text-decoration-none">
|
||||
{{ change.user }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ change.user }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ change.comment }}
|
||||
{% if change.added_text or change.removed_text %}
|
||||
<div class="mt-1 small">
|
||||
{% if change.added_text %}
|
||||
<span class="text-success">{{ change.added_text }}</span>
|
||||
{% endif %}
|
||||
{% if change.removed_text %}
|
||||
<span class="text-danger">{{ change.removed_text }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if change.change_size starts with '+' or change.change_size > 0 %}
|
||||
<span class="text-success">{{ change.change_size }}</span>
|
||||
{% elseif change.change_size starts with '−' or change.change_size < 0 %}
|
||||
<span class="text-danger">{{ change.change_size }}</span>
|
||||
{% else %}
|
||||
{{ change.change_size }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ change.page_url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir
|
||||
</a>
|
||||
{% if change.diff_url %}
|
||||
<a href="{{ change.diff_url }}" target="_blank" class="btn btn-sm btn-outline-secondary mt-1">
|
||||
<i class="bi bi-file-diff"></i> Diff
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucun changement récent n'a été trouvé.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des changements récents</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Cette page affiche les changements récents dans l'espace de noms français (FR:) du wiki OpenStreetMap.</p>
|
||||
<p>Ces informations sont utiles pour suivre les traductions manquantes et les mises à jour des pages wiki.</p>
|
||||
<p>Les données sont mises à jour automatiquement toutes les heures.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
121
templates/public/wiki_suspicious_deletions.html.twig
Normal file
121
templates/public/wiki_suspicious_deletions.html.twig
Normal file
|
@ -0,0 +1,121 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Pages Wiki avec suppressions suspectes{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Pages Wiki avec suppressions suspectes</h1>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
Cette page présente deux types de suppressions suspectes :
|
||||
<ul>
|
||||
<li><strong>Suppressions récentes</strong> : Détectées en temps réel dans les changements récents du
|
||||
wiki (suppressions > 20 caractères)
|
||||
</li>
|
||||
<li><strong>Différences de contenu</strong> : Pages françaises contenant significativement moins de mots
|
||||
que leurs équivalents anglais
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Suppressions récentes -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h2>Suppressions récentes suspectes</h2>
|
||||
{% if last_updated %}
|
||||
<small>Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if recent_deletions is defined and recent_deletions|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Suppression</th>
|
||||
<th>Date</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Commentaire</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for deletion in recent_deletions %}
|
||||
<tr>
|
||||
<td><strong>{{ deletion.page_title }}</strong></td>
|
||||
<td>
|
||||
<span class="badge bg-danger">{{ deletion.deletion_size }} caractères</span>
|
||||
</td>
|
||||
<td>{{ deletion.timestamp }}</td>
|
||||
<td>{{ deletion.user }}</td>
|
||||
<td>{{ deletion.comment }}</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ deletion.page_url }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" title="Voir la page">
|
||||
<i class="bi bi-eye"></i> Voir
|
||||
</a>
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?title={{ deletion.page_title|url_encode }}&action=history"
|
||||
target="_blank" class="btn btn-sm btn-outline-secondary"
|
||||
title="Historique">
|
||||
<i class="bi bi-clock-history"></i> Historique
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a href="https://wiki.openstreetmap.org/w/index.php?hidebots=1&hidenewpages=1&hidecategorization=1&hideWikibase=1&hidelog=1&hidenewuserlog=1&namespace=202&limit=250&days=30&enhanced=1&title=Special:RecentChanges&urlversion=2"
|
||||
target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-arrow-right-circle"></i> Voir tous les changements récents sur le wiki
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune suppression suspecte récente n'a été détectée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Différences de contenu -->
|
||||
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des suppressions suspectes</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>Suppressions récentes</h5>
|
||||
<p>Les suppressions récentes sont détectées en analysant les changements récents du wiki OSM. Toute
|
||||
suppression de plus de 20 caractères est considérée comme potentiellement suspecte et mérite une
|
||||
vérification.</p>
|
||||
|
||||
<h5>Différences de contenu</h5>
|
||||
<p>Les différences de contenu sont identifiées lorsque la version française d'une page wiki contient
|
||||
significativement moins de mots que la version anglaise (plus de 30% de différence).</p>
|
||||
<p>Cela peut indiquer :</p>
|
||||
<ul>
|
||||
<li>Une traduction incomplète</li>
|
||||
<li>Des sections manquantes dans la version française</li>
|
||||
<li>Des mises à jour importantes dans la version anglaise qui n'ont pas été reportées en français
|
||||
</li>
|
||||
</ul>
|
||||
<p>Ces pages sont des candidates prioritaires pour une mise à jour de la traduction française.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
266
templates/public/wiki_tag_proposals.html.twig
Normal file
266
templates/public/wiki_tag_proposals.html.twig
Normal file
|
@ -0,0 +1,266 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Propositions de tags OSM{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.vote-bar {
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.vote-approve {
|
||||
background-color: #28a745;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-abstain {
|
||||
background-color: #ffc107;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-oppose {
|
||||
background-color: #dc3545;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vote-count {
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.voter-badge {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.voter-approve {
|
||||
background-color: rgba(40, 167, 69, 0.2);
|
||||
border: 1px solid rgba(40, 167, 69, 0.4);
|
||||
}
|
||||
|
||||
.voter-abstain {
|
||||
background-color: rgba(255, 193, 7, 0.2);
|
||||
border: 1px solid rgba(255, 193, 7, 0.4);
|
||||
}
|
||||
|
||||
.voter-oppose {
|
||||
background-color: rgba(220, 53, 69, 0.2);
|
||||
border: 1px solid rgba(220, 53, 69, 0.4);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container mt-4">
|
||||
{% include 'admin/_wiki_navigation.html.twig' %}
|
||||
|
||||
<h1>Propositions de tags OSM</h1>
|
||||
<p class="lead">Liste des propositions de tags OpenStreetMap actuellement en cours de vote ou récemment modifiées.</p>
|
||||
|
||||
{% if last_updated %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> Dernière mise à jour : {{ last_updated|date('d/m/Y H:i') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Propositions en cours de vote -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h2>Propositions en cours de vote</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set voting_proposals = proposals|filter(p => p.type == 'voting') %}
|
||||
{% if voting_proposals|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Proposition</th>
|
||||
<th>Proposé par</th>
|
||||
<th>Votes</th>
|
||||
<th>Statut</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for proposal in voting_proposals %}
|
||||
<tr>
|
||||
<td><strong>{{ proposal.feature }}</strong></td>
|
||||
<td>
|
||||
{% if proposal.proposer %}
|
||||
<a href="https://wiki.openstreetmap.org/wiki/User:{{ proposal.proposer }}" target="_blank">
|
||||
{{ proposal.proposer }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-muted">Non spécifié</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if proposal.votes is defined and proposal.total_votes > 0 %}
|
||||
<div>
|
||||
<strong>{{ proposal.total_votes }} votes</strong>
|
||||
</div>
|
||||
<div class="vote-bar">
|
||||
{% if proposal.approve_percentage > 0 %}
|
||||
<div class="vote-approve" style="width: {{ proposal.approve_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.approve.count }} ({{ proposal.approve_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.abstain_percentage > 0 %}
|
||||
<div class="vote-abstain" style="width: {{ proposal.abstain_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.abstain.count }} ({{ proposal.abstain_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if proposal.oppose_percentage > 0 %}
|
||||
<div class="vote-oppose" style="width: {{ proposal.oppose_percentage }}%">
|
||||
<span class="vote-count">{{ proposal.votes.oppose.count }} ({{ proposal.oppose_percentage }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#voters{{ loop.index }}" aria-expanded="false" aria-controls="voters{{ loop.index }}">
|
||||
Voir les votants
|
||||
</button>
|
||||
<div class="collapse mt-2" id="voters{{ loop.index }}">
|
||||
{% if proposal.votes.approve.users|length > 0 %}
|
||||
<div class="mb-1">
|
||||
<strong>Approbations:</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.approve.users %}
|
||||
<span class="voter-badge voter-approve">{{ user.username }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.abstain.users|length > 0 %}
|
||||
<div class="mb-1">
|
||||
<strong>Abstentions:</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.abstain.users %}
|
||||
<span class="voter-badge voter-abstain">{{ user.username }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if proposal.votes.oppose.users|length > 0 %}
|
||||
<div>
|
||||
<strong>Oppositions:</strong>
|
||||
<div>
|
||||
{% for user in proposal.votes.oppose.users %}
|
||||
<span class="voter-badge voter-oppose">{{ user.username }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">Aucun vote</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-primary">En vote</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ proposal.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune proposition en cours de vote n'a été trouvée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Propositions récemment modifiées -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h2>Propositions récemment modifiées</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set recent_proposals = proposals|filter(p => p.type == 'recent') %}
|
||||
{% if recent_proposals|length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Proposition</th>
|
||||
<th>Dernière modification</th>
|
||||
<th>Modifié par</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for proposal in recent_proposals %}
|
||||
<tr>
|
||||
<td><strong>{{ proposal.feature }}</strong></td>
|
||||
<td>{{ proposal.description }}</td>
|
||||
<td>{{ proposal.proposer }}</td>
|
||||
<td>
|
||||
<a href="{{ proposal.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<p><i class="bi bi-info-circle"></i> Aucune proposition récemment modifiée n'a été trouvée.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2>À propos des propositions de tags</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Les propositions de tags sont un processus communautaire pour introduire de nouveaux tags ou modifier des tags existants dans OpenStreetMap.</p>
|
||||
<p>Le processus typique comprend les étapes suivantes :</p>
|
||||
<ol>
|
||||
<li><strong>Brouillon</strong> : La proposition initiale est rédigée et discutée.</li>
|
||||
<li><strong>RFC (Request for Comments)</strong> : La proposition est ouverte aux commentaires de la communauté.</li>
|
||||
<li><strong>Vote</strong> : La proposition est soumise au vote de la communauté.</li>
|
||||
<li><strong>Approbation ou rejet</strong> : Selon les résultats du vote, la proposition est approuvée ou rejetée.</li>
|
||||
</ol>
|
||||
<p>Vous pouvez participer à ce processus en commentant les propositions ou en votant lorsqu'elles sont en phase de vote.</p>
|
||||
<div class="d-grid gap-2 col-md-6 mx-auto mt-3">
|
||||
<a href="https://wiki.openstreetmap.org/wiki/Proposed_features" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Voir toutes les propositions sur le wiki OSM
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ path('app_admin_wiki') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Retour à la liste des pages wiki
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue