# dashboardbuilder.py — the Python engine behind dashboardbuilder.ado # ---------------------------------------------------------------------------- # Loaded at runtime by the ado via: python script "dashboardbuilder.py" # Uses ONLY the Python standard library (json, os) + Stata's sfi bridge. # Nothing here talks to the network; nothing needs pip. # # Two entry points, called as one-liners from the ado: # _dbb_capture() snapshot the current dataset's capture-vars to JSON # _dbb_assemble() read all snapshots + spec globals, write the HTML file # Both report failures through the Stata global DBB_PYERR (never raise), so # the ado can print friendly errors. # ---------------------------------------------------------------------------- import json import os from sfi import Data, Macro def _dbb_g(name): """read a Stata global (empty string if unset)""" try: return Macro.getGlobal(name) except Exception: return "" def _dbb_capture(): """Snapshot the current dataset's capture-vars to /panel.json. Value columns (DBB_CAPRAW) stay numeric; every other column is decoded through its value label so categories arrive as readable strings.""" try: k = _dbb_g("DBB_K") d = _dbb_g("DBB_DIR") cols = _dbb_g("DBB_CAP").split() raw = set(_dbb_g("DBB_CAPRAW").split()) colvals, labels = [], {} for c in cols: vals = Data.get(var=c, valuelabel=(c not in raw), missingval=None) colvals.append(vals) try: labels[c] = Data.getVarLabel(c) or c except Exception: labels[c] = c rows = [list(t) for t in zip(*colvals)] if colvals else [] with open(os.path.join(d, "panel%s.json" % k), "w", encoding="utf-8") as f: json.dump({"columns": cols, "labels": labels, "rows": rows}, f) Macro.setGlobal("DBB_PYERR", "") except Exception as e: Macro.setGlobal("DBB_PYERR", str(e)) def _dbb_esc(s): """escape text for direct HTML embedding""" return s.replace("&", "&").replace("<", "<").replace(">", ">") _DBB_THEMES = { "simple": { "accent": "#2563EB", "accent2": "#0F172A", "bg": "#F8FAFC", "line": "#E2E8F0", "muted": "#64748B", "ink": "#0F172A", "hdbg": "#FFFFFF", "hdink": "#0F172A", "hdborder": "1px solid #E2E8F0", "palette": ["#2563EB", "#0F172A", "#0E9488", "#B45309", "#7C3AED", "#DC2626", "#64748B"], }, "tx2036": { "accent": "#D44500", "accent2": "#1B2D55", "bg": "#F5F7FA", "line": "#DDE3EC", "muted": "#6C7A8D", "ink": "#1B2D55", "hdbg": "#1B2D55", "hdink": "#FFFFFF", "hdborder": "none", "palette": ["#D44500", "#1B2D55", "#0E9488", "#6C7A8D", "#8A4B10", "#5B4FA0", "#2B6CB0"], }, } def _dbb_assemble(): try: out = _dbb_g("DBB_OUT") d = _dbb_g("DBB_DIR") theme = _dbb_g("DBB_THEME") or "simple" th = _DBB_THEMES[theme] np = int(_dbb_g("DBB_NPANELS")) nt = int(_dbb_g("DBB_NTABS")) sel = _dbb_g("DBB_SEL") refval = _dbb_g("DBB_REFVAL") tabs = [] for i in range(1, nt + 1): tabs.append({"name": _dbb_g("DBB_TAB_NAME_%d" % i), "label": _dbb_g("DBB_TAB_LAB_%d" % i)}) panels, todo = [], [] selvalues = set() for i in range(1, np + 1): ptype = _dbb_g("DBB_P_%d_TYPE" % i) if ptype == "html": # external-HTML panel: inline the file (e.g. a sparkta2 map) as an #