MCPcopy
hub / github.com/phuryn/claude-usage / get_dashboard_data

Function get_dashboard_data

dashboard.py:27–232  ·  view source on GitHub ↗
(db_path=DB_PATH)

Source from the content-addressed store, hash-verified

25
26
27def get_dashboard_data(db_path=DB_PATH):
28 if not db_path.exists():
29 return {"error": "Database not found. Run: python cli.py scan"}
30
31 conn = sqlite3.connect(db_path)
32 # The dashboard reads while a background scan may be committing (cmd_dashboard
33 # serves first, scans in a background thread; /api/rescan scans in-process too).
34 # Wait briefly for write locks instead of raising "database is locked".
35 conn.execute("PRAGMA busy_timeout = 5000")
36 conn.row_factory = sqlite3.Row
37 # Ensure the schema is current before querying. cmd_dashboard binds and serves
38 # *before* its background scan runs init_db, so on the first load after an
39 # upgrade a pre-existing DB may still be on the old schema — the subagent
40 # queries below reference the `agents` table and the `is_subagent`/`agent_id`
41 # columns and would raise "no such table: agents" until the scan caught up.
42 # init_db is idempotent (CREATE ... IF NOT EXISTS + additive column checks),
43 # so this is a cheap no-op once migrated.
44 init_db(conn)
45
46 # ── All models (for filter UI) ────────────────────────────────────────────
47 # GROUP BY uses the normalised expression too so NULL and '' don't end up
48 # as two separate "unknown" rows.
49 model_rows = conn.execute("""
50 SELECT COALESCE(NULLIF(model, ''), 'unknown') as model
51 FROM turns
52 GROUP BY COALESCE(NULLIF(model, ''), 'unknown')
53 ORDER BY SUM(input_tokens + output_tokens) DESC
54 """).fetchall()
55 all_models = [r["model"] for r in model_rows]
56
57 # ── Daily per-model, ALL history (client filters by range) ────────────────
58 daily_rows = conn.execute("""
59 SELECT
60 substr(timestamp, 1, 10) as day,
61 COALESCE(NULLIF(model, ''), 'unknown') as model,
62 SUM(input_tokens) as input,
63 SUM(output_tokens) as output,
64 SUM(cache_read_tokens) as cache_read,
65 SUM(cache_creation_tokens) as cache_creation,
66 COUNT(*) as turns
67 FROM turns
68 GROUP BY day, COALESCE(NULLIF(model, ''), 'unknown')
69 ORDER BY day, model
70 """).fetchall()
71
72 daily_by_model = [{
73 "day": r["day"],
74 "model": r["model"],
75 "input": r["input"] or 0,
76 "output": r["output"] or 0,
77 "cache_read": r["cache_read"] or 0,
78 "cache_creation": r["cache_creation"] or 0,
79 "turns": r["turns"] or 0,
80 } for r in daily_rows]
81
82 # ── Hourly per-day per-model (client filters by range + TZ-shifts) ────────
83 # Timestamps are ISO8601 UTC (e.g. "2026-04-08T09:30:00Z"); chars 12-13 = hour.
84 hourly_rows = conn.execute("""

Calls 1

init_dbFunction · 0.90