Page views and relay joins/session-lengths appended as JSON lines. Uniques via daily-salted hash — no IPs, no cookies, no client-side code, no third parties. tools/stats_report.py prints a per-day table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
"""Traffic report from server.py --stats JSONL.
|
|
|
|
python3 tools/stats_report.py /path/to/stats.jsonl [days]
|
|
"""
|
|
import json
|
|
import sys
|
|
import time
|
|
from collections import defaultdict
|
|
|
|
path = sys.argv[1] if len(sys.argv) > 1 else 'stats.jsonl'
|
|
days_back = int(sys.argv[2]) if len(sys.argv) > 2 else 14
|
|
cutoff = time.time() - days_back * 86400
|
|
|
|
daily = defaultdict(lambda: {'views': 0, 'uniq': set(), 'hosts': 0, 'patients': 0})
|
|
sessions = []
|
|
for line in open(path, encoding='utf-8'):
|
|
try:
|
|
d = json.loads(line)
|
|
except ValueError:
|
|
continue
|
|
if d.get('t', 0) < cutoff:
|
|
continue
|
|
day = time.strftime('%Y-%m-%d', time.localtime(d['t']))
|
|
row = daily[day]
|
|
if d['e'] == 'page':
|
|
row['views'] += 1
|
|
row['uniq'].add(d.get('u'))
|
|
elif d['e'] == 'join':
|
|
row['hosts' if d.get('role') == 'admin' else 'patients'] += 1
|
|
elif d['e'] == 'session' and d.get('role') == 'admin':
|
|
sessions.append(d.get('secs', 0))
|
|
|
|
print(f"{'day':12} {'views':>6} {'uniques':>8} {'hosts':>6} {'patients':>9}")
|
|
for day in sorted(daily):
|
|
r = daily[day]
|
|
print(f"{day:12} {r['views']:>6} {len(r['uniq']):>8} {r['hosts']:>6} {r['patients']:>9}")
|
|
|
|
if sessions:
|
|
sessions.sort()
|
|
med = sessions[len(sessions) // 2]
|
|
print(f"\n{len(sessions)} hosted relay sessions · median {med // 60}m{med % 60:02d}s "
|
|
f"· longest {max(sessions) // 60}m")
|
|
else:
|
|
print('\nno completed relay sessions in range')
|