From a1102952e932753103ccb14ff13b4c865d949a27 Mon Sep 17 00:00:00 2001 From: Jordan Wages Date: Sat, 19 Jul 2025 00:16:11 -0500 Subject: [PATCH] Add global stats generation --- scripts/generate_reports.py | 30 ++++++++++++++++++++++++++++++ templates/index.html | 22 ++++++++++++++++++++++ tests/test_reports.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/scripts/generate_reports.py b/scripts/generate_reports.py index e7c42cb..b92a54c 100644 --- a/scripts/generate_reports.py +++ b/scripts/generate_reports.py @@ -62,6 +62,35 @@ def _render_snippet(report: Dict, out_dir: Path) -> None: snippet_path.write_text(template.render(report=report)) +def _write_stats() -> None: + """Query basic dataset stats and write them to ``output/global/stats.json``.""" + conn = sqlite3.connect(DB_PATH) + cur = conn.cursor() + + cur.execute("SELECT COUNT(*) FROM logs") + total_logs = cur.fetchone()[0] or 0 + + cur.execute("SELECT MIN(time), MAX(time) FROM logs") + row = cur.fetchone() or (None, None) + start_date = row[0] or "" + end_date = row[1] or "" + + cur.execute("SELECT COUNT(DISTINCT host) FROM logs") + unique_domains = cur.fetchone()[0] or 0 + + conn.close() + + stats = { + "total_logs": total_logs, + "start_date": start_date, + "end_date": end_date, + "unique_domains": unique_domains, + } + + out_path = OUTPUT_DIR / "global" / "stats.json" + _save_json(out_path, stats) + + def _bucket_expr(interval: str) -> str: """Return the SQLite strftime expression for the given interval.""" fmt = INTERVAL_FORMATS.get(interval) @@ -199,6 +228,7 @@ def _generate_global() -> None: report_list.append(entry) _save_json(out_dir / "reports.json", report_list) + _write_stats() typer.echo("Generated global reports") diff --git a/templates/index.html b/templates/index.html index 08b5732..6294602 100644 --- a/templates/index.html +++ b/templates/index.html @@ -32,6 +32,12 @@ +
+

Overview

+

Total logs: -

+

Date range: - to -

+

Unique domains: -

+
@@ -42,6 +48,10 @@ const intervalSelect = document.getElementById('interval-select'); const domainSelect = document.getElementById('domain-select'); const container = document.getElementById('reports-container'); + const totalElem = document.getElementById('stat-total'); + const startElem = document.getElementById('stat-start'); + const endElem = document.getElementById('stat-end'); + const domainsElem = document.getElementById('stat-domains'); let currentInterval = intervalSelect.value; let currentDomain = domainSelect.value; @@ -97,6 +107,17 @@ }); } + function loadStats() { + fetch('global/stats.json') + .then(r => r.json()) + .then(stats => { + totalElem.textContent = stats.total_logs; + startElem.textContent = stats.start_date; + endElem.textContent = stats.end_date; + domainsElem.textContent = stats.unique_domains; + }); + } + function loadReports() { let path = currentInterval; if (currentDomain) { @@ -128,6 +149,7 @@ }); loadReports(); + loadStats(); feather.replace(); diff --git a/tests/test_reports.py b/tests/test_reports.py index d8259f2..8b4bad1 100644 --- a/tests/test_reports.py +++ b/tests/test_reports.py @@ -189,3 +189,31 @@ def test_global_reports_once(tmp_path, sample_reports, monkeypatch): assert global_snippet.exists() assert not (tmp_path / "output" / "hourly" / "domain_totals.html").exists() + +def test_global_stats_file(tmp_path, sample_reports, monkeypatch): + db_path = tmp_path / "database" / "ngxstat.db" + setup_db(db_path) + + monkeypatch.setattr(gr, "DB_PATH", db_path) + monkeypatch.setattr(gr, "OUTPUT_DIR", tmp_path / "output") + monkeypatch.setattr(gr, "REPORT_CONFIG", sample_reports) + monkeypatch.setattr( + gr, "TEMPLATE_DIR", Path(__file__).resolve().parents[1] / "templates" + ) + + gr._generate_global() + + stats_path = tmp_path / "output" / "global" / "stats.json" + assert stats_path.exists() + stats = json.loads(stats_path.read_text()) + assert set(stats.keys()) == { + "total_logs", + "start_date", + "end_date", + "unique_domains", + } + assert stats["total_logs"] == 2 + assert stats["start_date"] == "2024-01-01 10:00:00" + assert stats["end_date"] == "2024-01-01 10:05:00" + assert stats["unique_domains"] == 1 +