284 lines
9.5 KiB
HTML
284 lines
9.5 KiB
HTML
{% extends 'base.html' %}
|
||
|
||
{% block title %}Аналитика на Докладите - Официален Портал{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="page-header">
|
||
<h1>Аналитика на Докладите</h1>
|
||
<p class="lead">Подробна статистика и аналитика за докладите за измами</p>
|
||
</div>
|
||
|
||
<!-- Overall Statistics -->
|
||
<div class="gov-card">
|
||
<div class="gov-card-header">
|
||
<h2>Обща Статистика</h2>
|
||
</div>
|
||
<div class="gov-card-body">
|
||
<div class="stats-grid">
|
||
<div class="stat-card">
|
||
<h3>{{ total_reports|default:0 }}</h3>
|
||
<p>Общо Доклади</p>
|
||
</div>
|
||
<div class="stat-card stat-pending">
|
||
<h3>{{ pending_reports|default:0 }}</h3>
|
||
<p>В Очакване</p>
|
||
</div>
|
||
<div class="stat-card stat-verified">
|
||
<h3>{{ verified_reports|default:0 }}</h3>
|
||
<p>Потвърдени</p>
|
||
</div>
|
||
<div class="stat-card stat-rejected">
|
||
<h3>{{ rejected_reports|default:0 }}</h3>
|
||
<p>Отхвърлени</p>
|
||
</div>
|
||
<div class="stat-card stat-review">
|
||
<h3>{{ under_review_reports|default:0 }}</h3>
|
||
<p>В Преглед</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Time-based Statistics -->
|
||
<div class="gov-card">
|
||
<div class="gov-card-header">
|
||
<h2>Статистика по Период</h2>
|
||
</div>
|
||
<div class="gov-card-body">
|
||
<div class="stats-grid">
|
||
<div class="stat-card">
|
||
<h3>{{ reports_last_7_days|default:0 }}</h3>
|
||
<p>Последните 7 Дни</p>
|
||
</div>
|
||
<div class="stat-card">
|
||
<h3>{{ reports_last_30_days|default:0 }}</h3>
|
||
<p>Последните 30 Дни</p>
|
||
</div>
|
||
<div class="stat-card">
|
||
<h3>{{ reports_last_90_days|default:0 }}</h3>
|
||
<p>Последните 90 Дни</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Scam Type Distribution -->
|
||
{% if scam_types %}
|
||
<div class="gov-card">
|
||
<div class="gov-card-header">
|
||
<h2>Разпределение по Видове Измами</h2>
|
||
</div>
|
||
<div class="gov-card-body">
|
||
<div class="scam-types-grid">
|
||
{% for type in scam_types %}
|
||
<div class="scam-type-card">
|
||
<div class="scam-type-header">
|
||
<span class="badge badge-{{ type.scam_type }}" style="font-size: 0.9rem; padding: 0.5rem 1rem;">
|
||
{{ type.display_name }}
|
||
</span>
|
||
<div class="scam-type-count">
|
||
<span class="count-number">{{ type.count }}</span>
|
||
<span class="count-label">доклад{{ type.count|pluralize:"а,а" }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="scam-type-bar">
|
||
<div class="scam-type-bar-fill" style="width: {{ type.percentage }}%"></div>
|
||
</div>
|
||
<div class="scam-type-percentage">
|
||
{{ type.percentage }}% от общия брой
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Moderation Statistics -->
|
||
<div class="gov-card">
|
||
<div class="gov-card-header">
|
||
<h2>Статистика на Модерация</h2>
|
||
</div>
|
||
<div class="gov-card-body">
|
||
<div class="stats-grid">
|
||
<div class="stat-card">
|
||
<h3>{{ total_moderations|default:0 }}</h3>
|
||
<p>Общо Модерации</p>
|
||
</div>
|
||
<div class="stat-card stat-verified">
|
||
<h3>{{ approvals|default:0 }}</h3>
|
||
<p>Одобрени</p>
|
||
</div>
|
||
<div class="stat-card stat-rejected">
|
||
<h3>{{ rejections|default:0 }}</h3>
|
||
<p>Отхвърлени</p>
|
||
</div>
|
||
</div>
|
||
{% if avg_moderation_time_hours %}
|
||
<div style="margin-top: 2rem; padding-top: 2rem; border-top: 1px solid #e0e0e0;">
|
||
<h3>Време за Модерация</h3>
|
||
<div class="stats-grid" style="margin-top: 1rem;">
|
||
<div class="stat-card">
|
||
<h3>{{ avg_moderation_time_hours }}ч</h3>
|
||
<p>Средно Време</p>
|
||
</div>
|
||
<div class="stat-card">
|
||
<h3>{{ min_moderation_time_hours }}ч</h3>
|
||
<p>Минимално Време</p>
|
||
</div>
|
||
<div class="stat-card">
|
||
<h3>{{ max_moderation_time_hours }}ч</h3>
|
||
<p>Максимално Време</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Top Reporters -->
|
||
{% if top_reporters %}
|
||
<div class="gov-card">
|
||
<div class="gov-card-header">
|
||
<h2>Най-Активни Докладващи</h2>
|
||
</div>
|
||
<div class="gov-card-body">
|
||
<div class="table-responsive">
|
||
<table class="gov-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Позиция</th>
|
||
<th>Потребител</th>
|
||
<th>Брой Доклади</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for reporter in top_reporters %}
|
||
<tr>
|
||
<td>{{ forloop.counter }}</td>
|
||
<td>{{ reporter.reporter__username|default:"Анонимен" }}</td>
|
||
<td><strong>{{ reporter.report_count }}</strong></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Daily Reports Chart -->
|
||
{% if daily_reports %}
|
||
<div class="gov-card">
|
||
<div class="gov-card-header">
|
||
<h2>Дневна Статистика (Последните 30 Дни)</h2>
|
||
</div>
|
||
<div class="gov-card-body">
|
||
<div class="chart-container">
|
||
<canvas id="dailyReportsChart" style="max-height: 400px;"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Status Over Time -->
|
||
{% if status_over_time %}
|
||
<div class="gov-card">
|
||
<div class="gov-card-header">
|
||
<h2>Статуси по Време (Последните 7 Дни)</h2>
|
||
</div>
|
||
<div class="gov-card-body">
|
||
<div class="chart-container">
|
||
<canvas id="statusChart" style="max-height: 400px;"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div style="margin-top: 2rem;">
|
||
<a href="{% url 'analytics:dashboard' %}" class="btn btn-secondary">Обратно към Таблото</a>
|
||
</div>
|
||
|
||
{% if daily_reports %}
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
||
<script>
|
||
// Daily Reports Chart
|
||
const dailyCtx = document.getElementById('dailyReportsChart');
|
||
if (dailyCtx) {
|
||
const dailyData = {{ daily_reports }};
|
||
new Chart(dailyCtx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: dailyData.map(d => new Date(d.date).toLocaleDateString('bg-BG')),
|
||
datasets: [{
|
||
label: 'Доклади',
|
||
data: dailyData.map(d => d.count),
|
||
borderColor: '#1e3a8a',
|
||
backgroundColor: 'rgba(30, 58, 138, 0.1)',
|
||
tension: 0.4
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: true,
|
||
plugins: {
|
||
legend: {
|
||
display: true
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
beginAtZero: true
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// Status Over Time Chart
|
||
const statusCtx = document.getElementById('statusChart');
|
||
if (statusCtx) {
|
||
const statusData = {{ status_over_time }};
|
||
new Chart(statusCtx, {
|
||
type: 'bar',
|
||
data: {
|
||
labels: statusData.map(d => new Date(d.date).toLocaleDateString('bg-BG')),
|
||
datasets: [
|
||
{
|
||
label: 'В Очакване',
|
||
data: statusData.map(d => d.pending),
|
||
backgroundColor: '#f59e0b'
|
||
},
|
||
{
|
||
label: 'Потвърдени',
|
||
data: statusData.map(d => d.verified),
|
||
backgroundColor: '#10b981'
|
||
},
|
||
{
|
||
label: 'Отхвърлени',
|
||
data: statusData.map(d => d.rejected),
|
||
backgroundColor: '#ef4444'
|
||
}
|
||
]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: true,
|
||
plugins: {
|
||
legend: {
|
||
display: true
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
beginAtZero: true,
|
||
stacked: false
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
{% endif %}
|
||
{% endblock %}
|