arborist-viz/arborist_viz/views/dashboards.py
russell@unturf.com 0cd96ab1d7
Initial commit: arborist VIZ — Merkle command center
Pyramid web app for inspecting arborist Merkle trees over sharded SQLite:
document roots, inclusion proofs, an interactive 3D Merkle lattice
(six.js / WebGL with graceful fallback), audit-chain linkage, and a live
SSE event stream.

Generated artifacts (env/, vendored static/six/ bundles, demo databases)
are gitignored and rebuilt via the Makefile (venv, six, init-db, serve).
2026-05-28 09:22:59 -04:00

244 lines
9.2 KiB
Python

"""Saved-dashboard metadata endpoints.
Phase 1: backed by SQLAlchemy. The three canned configs (Audit
Operations, Claim Graveyard, Merkle Root Proof) are upserted on first
access so the UI always has a starter catalog.
"""
from __future__ import annotations
import json
from pyramid.httpexceptions import HTTPNotFound
from pyramid.response import Response
from pyramid.view import view_config
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from ..models import Base, Dashboard
_engine = None
_Session = None
def _session_factory(settings):
global _engine, _Session
if _engine is None:
_engine = create_engine(settings["sqlalchemy.url"], future=True)
Base.metadata.create_all(_engine)
_Session = sessionmaker(bind=_engine, expire_on_commit=False, future=True)
return _Session
# Canned starter catalog. Upserted on first access so the demo always
# has something to display even before fox saves their own layouts.
CANNED: dict[str, dict] = {
"default": {
"id": "default",
"title": "Audit Operations View",
"description": "Operator default — recent audit chain, live claim status, root explorer.",
"version": "dashboard-v1",
"layout": [
{"panel_id": "root", "x": 0, "y": 0, "w": 4, "h": 4, "dock": "left"},
{"panel_id": "audit", "x": 4, "y": 0, "w": 8, "h": 4, "dock": "main"},
{"panel_id": "claim", "x": 0, "y": 4, "w": 6, "h": 4},
{"panel_id": "run", "x": 6, "y": 4, "w": 6, "h": 4},
],
"widgets": [
{
"widget_id": "root",
"type": "root-explorer",
"title": "Root Explorer",
"data_source_id": "shards",
"query": {"select": ["root_hash", "leaf_count", "ingest_ts"], "from": "documents", "limit": 50},
"view": {"renderer": "table", "options": {}},
"source_roots": [],
},
{
"widget_id": "audit",
"type": "audit-timeline",
"title": "Audit Timeline",
"data_source_id": "shards",
"query": {"select": ["event_hash", "event_type", "ts"], "from": "audit_events", "limit": 200},
"view": {"renderer": "timeline", "options": {}},
"source_roots": [],
},
{
"widget_id": "claim",
"type": "claim-warrant",
"title": "Claim Warrants",
"data_source_id": "shards",
"query": {"select": ["claim_id", "warrant", "status"], "from": "claims", "limit": 100},
"view": {"renderer": "table", "options": {}},
"source_roots": [],
},
{
"widget_id": "run",
"type": "run-dag",
"title": "Latest Ingest Run",
"data_source_id": "shards",
"query": {"select": ["run_id", "stage", "status"], "from": "runs", "limit": 1},
"view": {"renderer": "dag", "options": {}},
"source_roots": [],
},
],
"data_sources": [
{"id": "shards", "kind": "arborist_shard", "locator": "default", "read_only": True}
],
"global_filters": [],
"interactions": [],
},
"graveyard": {
"id": "graveyard",
"title": "Claim Graveyard View",
"description": "Negative memory — failed/stale/quarantined claims plus their falsifiers (#000059 projection).",
"version": "dashboard-v1",
"layout": [{"panel_id": "gv", "x": 0, "y": 0, "w": 12, "h": 8, "dock": "main"}],
"widgets": [
{
"widget_id": "gv",
"type": "claim-graveyard",
"title": "Claim Graveyard",
"data_source_id": "shards",
"query": {"select": ["failed_claim_hash", "failure_class", "created_at"], "from": "graveyard", "limit": 500},
"view": {"renderer": "table", "options": {}},
"source_roots": [],
}
],
"data_sources": [{"id": "shards", "kind": "arborist_shard", "locator": "default", "read_only": True}],
},
"proof": {
"id": "proof",
"title": "Merkle Root Proof View",
"description": "Single-root deep dive — inclusion proofs, leaves, audit linkage, 3D lattice.",
"version": "dashboard-v1",
"layout": [
{"panel_id": "root", "x": 0, "y": 0, "w": 4, "h": 3},
{"panel_id": "lattice", "x": 4, "y": 0, "w": 8, "h": 6},
{"panel_id": "proof", "x": 0, "y": 3, "w": 4, "h": 3},
],
"widgets": [
{
"widget_id": "root",
"type": "root-explorer",
"title": "Root",
"data_source_id": "shards",
"query": {"select": ["*"], "from": "documents", "limit": 1},
"view": {"renderer": "card", "options": {}},
"source_roots": [],
},
{
"widget_id": "lattice",
"type": "merkle-lattice-3d",
"title": "3D Merkle Lattice",
"data_source_id": "shards",
"query": {"select": ["layers"], "from": "merkle_tree", "limit": 1},
"view": {"renderer": "three-d", "options": {"animate_proof_path": True}},
"source_roots": [],
},
{
"widget_id": "proof",
"type": "merkle-proof",
"title": "Inclusion Proof",
"data_source_id": "shards",
"query": {"select": ["proof_path"], "from": "proofs", "limit": 1},
"view": {"renderer": "card", "options": {}},
"source_roots": [],
},
],
"data_sources": [{"id": "shards", "kind": "arborist_shard", "locator": "default", "read_only": True}],
},
}
def _upsert_canned(session_factory):
"""Insert the canned catalog if it isn't already there."""
Session = session_factory
with Session() as s:
for cfg in CANNED.values():
existing = s.get(Dashboard, cfg["id"])
if existing is None:
s.add(Dashboard(
id=cfg["id"], title=cfg["title"], description=cfg.get("description"),
version=cfg.get("version", "dashboard-v1"), config_json=cfg,
))
s.commit()
def _json(body, status=200) -> Response:
return Response(json_body=body, status=status)
@view_config(route_name="api_dashboards", request_method="GET")
def list_dashboards(request):
Session = _session_factory(request.registry.settings)
_upsert_canned(Session)
with Session() as s:
rows = s.query(Dashboard).order_by(Dashboard.id).all()
return _json({"dashboards": [r.config_json for r in rows]})
@view_config(route_name="api_dashboards", request_method="POST")
def create_dashboard(request):
try:
body = request.json_body
except Exception:
return _json({"error": "invalid JSON"}, status=400)
if not body.get("id") or not body.get("title"):
return _json({"error": "id and title required"}, status=400)
Session = _session_factory(request.registry.settings)
with Session() as s:
if s.get(Dashboard, body["id"]):
return _json({"error": "exists", "id": body["id"]}, status=409)
d = Dashboard(
id=body["id"], title=body["title"], description=body.get("description"),
version=body.get("version", "dashboard-v1"), config_json=body,
)
s.add(d)
s.commit()
return _json({"created": body})
@view_config(route_name="api_dashboard", request_method="GET")
def get_dashboard(request):
dashboard_id = request.matchdict["dashboard_id"]
Session = _session_factory(request.registry.settings)
_upsert_canned(Session)
with Session() as s:
d = s.get(Dashboard, dashboard_id)
if d is None:
raise HTTPNotFound(json_body={"error": "unknown dashboard", "id": dashboard_id})
return _json(d.config_json)
@view_config(route_name="api_dashboard", request_method="PUT")
def update_dashboard(request):
dashboard_id = request.matchdict["dashboard_id"]
try:
body = request.json_body
except Exception:
return _json({"error": "invalid JSON"}, status=400)
Session = _session_factory(request.registry.settings)
with Session() as s:
d = s.get(Dashboard, dashboard_id)
if d is None:
raise HTTPNotFound(json_body={"error": "unknown dashboard", "id": dashboard_id})
d.title = body.get("title", d.title)
d.description = body.get("description", d.description)
d.config_json = body
s.commit()
return _json({"updated": dashboard_id, "config": body})
@view_config(route_name="api_dashboard", request_method="DELETE")
def delete_dashboard(request):
dashboard_id = request.matchdict["dashboard_id"]
Session = _session_factory(request.registry.settings)
with Session() as s:
d = s.get(Dashboard, dashboard_id)
if d is None:
raise HTTPNotFound(json_body={"error": "unknown dashboard", "id": dashboard_id})
s.delete(d)
s.commit()
return _json({"deleted": dashboard_id})