mod:кастомный шаблонный фильтр для статистики

This commit is contained in:
2026-01-21 15:50:26 +03:00
parent 310ed5440d
commit 716c25dc26
4 changed files with 87 additions and 45 deletions

View File

@@ -45,9 +45,9 @@
{# ШАПКА и главное меню #} {# ШАПКА и главное меню #}
<nav id="main-navbar" class="navbar navbar-expand-lg mb-4"> <nav id="main-navbar" class="navbar navbar-expand-lg mb-4">
<div class="container"> <div class="container p-0">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img id="logo-img" class="logo-img" src="" <img id="logo-img" class="logo-img p-0 m-0" src=""
data-src-light="{% static 'svg/logo-etpgrf-site-light.svg' %}" data-src-light="{% static 'svg/logo-etpgrf-site-light.svg' %}"
data-src-light-compact="{% static 'svg/logo-etpgrf-site-light-compact.svg' %}" data-src-light-compact="{% static 'svg/logo-etpgrf-site-light-compact.svg' %}"
data-src-dark="{% static 'svg/logo-etpgrf-site-dark.svg' %}" data-src-dark="{% static 'svg/logo-etpgrf-site-dark.svg' %}"
@@ -64,12 +64,12 @@
{# Футер #} {# Футер #}
<footer class="footer mt-auto py-2 mt-4"> <footer class="footer mt-auto py-2 mt-4">
<div class="container d-flex justify-content-between align-items-center"> <div class="container d-flex justify-content-between align-items-center">
<span class="text-muted small nowrap">&copy; Sergei Erjemin, 2025&ndash;{% now 'Y' %}.</span> <span class="text-muted small nowrap me-2">&copy; Sergei Erjemin, 2025&ndash;{% now 'Y' %}.</span>
<span class="text-muted small nowrap"><i class="bi bi-tags me-1" title="Версия библиотеки etpgrf / Версия сайта"></i>v0.1.3 / v0.1.3</span> <nobr class="text-muted small mx-2"><i class="bi bi-tags me-1" title="Версия библиотеки etpgrf / Версия сайта"></i>v0.1.3 / v0.1.3</nobr>
{# Сводная статистика (HTMX) #} {# Сводная статистика (HTMX) #}
<span class="text-muted small" hx-get="{% url 'stats_summary' %}" hx-trigger="load"> <span class="text-muted small ms-2" hx-get="{% url 'stats_summary' %}" hx-trigger="load">
... ...
</span> </span>

View File

@@ -1,12 +1,16 @@
<span class="me-3" title="Просмотров"> {% load typograph_extras %}
<i class="bi bi-eye me-1"></i>{{ views }} <nobr class="ms-3 float-end" title="Скопировано в буфер текстов/символов">
</span> <i class="bi bi-clipboard-check me-1"></i>{{ copied|humanize_num }} / {{ chars_copied|humanize_num }}
<span class="me-3 nowrap" title="На вход обработано текстов/символов"> </nobr>
<i class="bi bi-box-arrow-in-right me-1"></i>{{ processed }}/{{ chars_in }} <nobr class="ms-3 float-end" title="На выход получено символов">
</span> <i class="bi bi-box-arrow-right me-1"></i>{{ chars_out|humanize_num }}
<span class="me-3" title="На выход получено символов"> </nobr>
<i class="bi bi-box-arrow-right me-1"></i>{{ chars_out }} <nobr class="ms-3 float-end" title="На вход обработано текстов/символов">
</span> <i class="bi bi-box-arrow-in-right me-1"></i>{{ processed|humanize_num }} / {{ chars_in|humanize_num }}
<span class="nowrap" title="Скопировано в буфер текстов/символов"> </nobr>
<i class="bi bi-clipboard-check me-1"></i>{{ copied }}/{{ chars_copied }} <nobr class="ms-3 float-end" title="Просмотров">
</span> <i class="bi bi-eye me-1"></i>{{ views|humanize_num }}
</nobr>

View File

@@ -0,0 +1,45 @@
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter(name='humanize_num')
def humanize_num(value):
"""
Форматирует число с тонкими пробелами в качестве разделителя тысяч
и сокращает большие числа до M (миллионы) или k (тысячи).
Примеры:
1234 -> 1&thinsp;234
1234567 -> 1,2M
"""
try:
num = int(value)
if num > 1_000_000_000:
val = num / 1_000_000_000
suffix = "&thinsp;B"
elif num > 1_000_000:
val = num / 1_000_000
suffix = "&thinsp;M"
elif num > 1_000:
val = num / 1_000
suffix = "&thinsp;k"
else:
# Больше 1B -- форматируем с пробелами
return mark_safe(f"{num:,}".replace(",", "&thinsp;"))
# Форматируем float:
# {:,.1f} - разделитель тысяч (запятая) и 1 знак после точки
# 1234567.89 -> "1,234,567.9"
formatted = f"{val:,.2f}"
# Меняем английскую запятую (разделитель тысяч) на тонкий пробел
# Меняем английскую точку (десятичный разделитель) на запятую
# Но тут проблема: replace делает все сразу.
# "1,234.5" -> replace(",", " ") -> "1 234.5" -> replace(".", ",") -> "1 234,5"
formatted = formatted.replace(",", "&thinsp;").replace(".", ",")
return mark_safe(f"{formatted}{suffix}")
except (ValueError, TypeError):
return value

View File

@@ -23,35 +23,28 @@ def index(request):
def get_stats_summary(request): def get_stats_summary(request):
"""Возвращает сводную статистику.""" """Возвращает сводную статистику."""
# Убираем try...except для отладки try:
stats = DailyStat.objects.aggregate( stats = DailyStat.objects.aggregate(
views=Sum('index_views'), views=Sum('index_views'),
processed=Sum('process_requests'), processed=Sum('process_requests'),
copied=Sum('copy_count'), copied=Sum('copy_count'),
chars_in=Sum('chars_in'), chars_in=Sum('chars_in'),
chars_out=Sum('chars_out'), chars_out=Sum('chars_out'),
chars_copied=Sum('chars_copied') chars_copied=Sum('chars_copied')
) )
# print("Aggregated stats:", stats) # DEBUG
context = {
# Функция для форматирования чисел с сокращениями (M, k) 'views': stats['views'] or 0,
def format_large_number(num): 'processed': stats['processed'] or 0,
if num > 1_000_000: 'copied': stats['copied'] or 0,
return f"{num / 1_000_000:.3f}M".replace(".", ",") 'chars_in': stats['chars_in'] or 0,
elif num > 1_000: 'chars_out': stats['chars_out'] or 0,
return f"{num / 1_000:.2f}k".replace(".", ",") 'chars_copied': stats['chars_copied'] or 0,
return str(num) }
context = { return render(request, 'typograph/stats_summary.html', context)
'views': f"{(stats['views'] or 0):,}".replace(",", "&thinsp;"), except Exception:
'processed': f"{(stats['processed'] or 0):,}".replace(",", "&thinsp;"), return HttpResponse("...")
'copied': f"{(stats['copied'] or 0):,}".replace(",", "&thinsp;"),
'chars_in': format_large_number(stats['chars_in'] or 0),
'chars_out': format_large_number(stats['chars_out'] or 0),
'chars_copied': format_large_number(stats['chars_copied'] or 0),
}
return render(request, 'typograph/stats_summary.html', context)
@require_POST @require_POST