97 lines
4.5 KiB
HTML
97 lines
4.5 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Statystyki{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="card bg-dark border-secondary mb-4">
|
|
<div class="card-header">Rozkład geograficzny</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-dark align-middle mb-0" id="geo-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:48%">Kraj</th>
|
|
<th style="width:40%">Udział</th>
|
|
<th class="text-end" style="width:12%">Liczba</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% set ns = namespace(total_geo=0) %}
|
|
{% for _, c in stats.geo_distribution.items() %}{% set ns.total_geo = ns.total_geo + (c|int) %}{%
|
|
endfor %}
|
|
{% for country, count in stats.geo_distribution.items() %}
|
|
{% set pct = ((count|int) / (ns.total_geo if ns.total_geo>0 else 1) * 100) | round(1) %}
|
|
{% set pct = 0 if pct < 0 else (100 if pct> 100 else pct) %}
|
|
<tr>
|
|
<td><span class="d-inline-block text-truncate" style="max-width:95%"
|
|
title="{{ country }}">{{ country }}</span></td>
|
|
<td>
|
|
<div class="progress bg-black border border-secondary" style="height:10px;">
|
|
<div class="progress-bar bg-primary" role="progressbar" style="width: {{ pct }}%;"
|
|
aria-valuenow="{{ pct }}" aria-valuemin="0" aria-valuemax="100"></div>
|
|
</div>
|
|
<div class="text-secondary small mt-1">{{ pct }}%</div>
|
|
</td>
|
|
<td class="text-end"><span class="badge bg-secondary">{{ count|int }}</span></td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% if stats.geo_distribution|length == 0 %}
|
|
<tr>
|
|
<td colspan="3" class="text-center text-secondary">Brak danych</td>
|
|
</tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Reasons -->
|
|
<div class="card bg-dark border-secondary mb-4">
|
|
<div class="card-header">Przyczyny banów</div>
|
|
<div class="card-body">
|
|
{% set nsr = namespace(total=0) %}
|
|
{% for _, v in stats.ban_reasons.items() %}{% set nsr.total = nsr.total + (v|int) %}{% endfor %}
|
|
|
|
{% if stats.ban_reasons|length == 0 %}
|
|
<div class="text-center text-secondary small">Brak danych</div>
|
|
{% else %}
|
|
<div class="list-group list-group-flush">
|
|
{% for reason, count in (stats.ban_reasons|dictsort(by='value', reverse=true)) %}
|
|
{% set label = reason if reason else 'Manual ban' %}
|
|
{% set pct = ((count|int) / (nsr.total if nsr.total>0 else 1) * 100) | round(1) %}
|
|
{% set pct = 0 if pct < 0 else (100 if pct> 100 else pct) %}
|
|
<div class="list-group-item bg-dark text-white border-secondary">
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<span class="text-truncate" title="{{ label }}">{{ label }}</span>
|
|
<span class="small text-secondary">{{ count|int }} ({{ pct }}%)</span>
|
|
</div>
|
|
<div class="progress bg-black border border-secondary" style="height:10px;">
|
|
<div class="progress-bar bg-primary" role="progressbar" style="width: {{ pct }}%;"
|
|
aria-valuenow="{{ pct }}" aria-valuemin="0" aria-valuemax="100"></div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
{{ super() }}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6"></script>
|
|
<script>
|
|
// dane dla wykresu z Jinja -> JS
|
|
const reasonsData = (() => {
|
|
const entries = Object.entries({{ stats.ban_reasons | tojson }});
|
|
// posortuj malejąco
|
|
entries.sort((a, b) => (b[1] || 0) - (a[1] || 0));
|
|
const labels = entries.map(([k]) => k && k.length ? k : "Manual ban");
|
|
const data = entries.map(([, v]) => v);
|
|
return { labels, data };
|
|
}) ();
|
|
</script>
|
|
<script src="{{ url_for('static', filename='js/stats.js') }}" defer></script>
|
|
{% endblock %} |