Merge pull request #25 from wagesj45/codex/implement-report-generation-and-rendering
Add global stats reporting
This commit is contained in:
commit
0bf67f6107
3 changed files with 80 additions and 0 deletions
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,12 @@
|
|||
<span class="icon is-small is-left"><i data-feather="server"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="overview" class="box mb-5">
|
||||
<h2 class="subtitle">Overview</h2>
|
||||
<p>Total logs: <span id="stat-total">-</span></p>
|
||||
<p>Date range: <span id="stat-start">-</span> to <span id="stat-end">-</span></p>
|
||||
<p>Unique domains: <span id="stat-domains">-</span></p>
|
||||
</div>
|
||||
<div id="reports-container"></div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
||||
|
@ -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();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue