diff --git a/srv/tesm/app.py b/srv/tesm/app.py index 47bb846..0391bbe 100644 --- a/srv/tesm/app.py +++ b/srv/tesm/app.py @@ -7727,13 +7727,29 @@ def activity_log(): "SELECT id, ts, username, action, target, details FROM audit_log ORDER BY id DESC LIMIT ?", (AUDIT_LOG_PAGE_SIZE,), ).fetchall() - total_count = conn.execute("SELECT COUNT(*) AS n FROM audit_log").fetchone()["n"] + # Gesamtzahlen je Kategorie (nicht nur der geladenen Zeilen) für die + # "X / Y geladen"-Anzeige in den Statistik-Kacheln -- eine einzige + # aggregierte Abfrage statt die komplette Tabelle einzulesen. Die + # LIKE-Muster bilden exakt dieselbe Kategorisierung wie category_of() + # in _audit_log_macros.html ab (kind = action.split(".")[-1]). + totals_row = conn.execute( + "SELECT COUNT(*) AS total, " + "SUM(CASE WHEN action LIKE '%.create' OR action LIKE '%.upload' OR action LIKE '%.mkdir' THEN 1 ELSE 0 END) AS create_n, " + "SUM(CASE WHEN action LIKE '%.delete' THEN 1 ELSE 0 END) AS delete_n " + "FROM audit_log" + ).fetchone() conn.close() + total_count = totals_row["total"] or 0 + total_create = totals_row["create_n"] or 0 + total_delete = totals_row["delete_n"] or 0 + total_edit = total_count - total_create - total_delete + oldest_loaded_id = entries[-1]["id"] if entries else None has_more = len(entries) >= AUDIT_LOG_PAGE_SIZE and total_count > len(entries) return render_template( "activity_log.html", entries=entries, total_count=total_count, + total_create=total_create, total_edit=total_edit, total_delete=total_delete, oldest_loaded_id=oldest_loaded_id, has_more=has_more, ) diff --git a/srv/tesm/templates/activity_log.html b/srv/tesm/templates/activity_log.html index 5efcad2..b4e9275 100644 --- a/srv/tesm/templates/activity_log.html +++ b/srv/tesm/templates/activity_log.html @@ -18,22 +18,24 @@ {% else %}{% set ns.edit = ns.edit + 1 %}{% endif %} {% endfor %} +{% macro fmt_count(loaded, total) %}{% if loaded < total %}{{ loaded }} / {{ total }}{% else %}{{ loaded }}{% endif %}{% endmacro %} +
Alle
-
{{ entries|length }}
+
{{ fmt_count(entries|length, total_count) }}
Hinzufügen
-
{{ ns.create }}
+
{{ fmt_count(ns.create, total_create) }}
Änderungen
-
{{ ns.edit }}
+
{{ fmt_count(ns.edit, total_edit) }}
Löschungen
-
{{ ns.delete }}
+
{{ fmt_count(ns.delete, total_delete) }}
@@ -112,16 +114,23 @@ document.addEventListener("DOMContentLoaded", function () { /* ---- Nachladen älterer Einträge (Mehr laden / Alle laden) ---- */ const totalCount = {{ total_count }}; + const totalCreate = {{ total_create }}; + const totalEdit = {{ total_edit }}; + const totalDelete = {{ total_delete }}; const entryCountLabel = document.getElementById("auditEntryCount"); + function fmtCount(loaded, total) { + return loaded < total ? loaded + " / " + total : String(loaded); + } + function recomputeStats() { const rows = Array.from(document.querySelectorAll("#auditTable tbody tr[data-category]")); const counts = { create: 0, edit: 0, delete: 0 }; rows.forEach(function (r) { counts[r.dataset.category] = (counts[r.dataset.category] || 0) + 1; }); - document.getElementById("statAll").textContent = rows.length; - document.getElementById("statCreate").textContent = counts.create; - document.getElementById("statEdit").textContent = counts.edit; - document.getElementById("statDelete").textContent = counts.delete; + document.getElementById("statAll").textContent = fmtCount(rows.length, totalCount); + document.getElementById("statCreate").textContent = fmtCount(counts.create, totalCreate); + document.getElementById("statEdit").textContent = fmtCount(counts.edit, totalEdit); + document.getElementById("statDelete").textContent = fmtCount(counts.delete, totalDelete); if (entryCountLabel) { entryCountLabel.textContent = rows.length < totalCount ? rows.length + " von " + totalCount + " Einträgen geladen"