diff --git a/dashboard_api.py b/dashboard_api.py index 9a0da30..9a265cc 100644 --- a/dashboard_api.py +++ b/dashboard_api.py @@ -33,7 +33,19 @@ def main() -> None: db_path = os.environ.get("DATABASE_URL") or os.environ.get( "WATCHER_EVENT_DB_PATH", "momentum") mode = os.environ.get("TRADING_EXECUTION_MODE", "alpaca_paper") - state = DashboardState(db_path, execution_service=None, execution_mode=mode) + # Sparklines: the standalone dashboard needs the SAME research-DB reader the + # embedded (orchestrator) mode passes — without it DashboardState.bars() short- + # circuits to []. The missing wire was purely that this entrypoint never built + # one (DATABASE_URL already reaches the market schema). Read-only use; degrade + # to None (charts show "no research db") if the connection can't be opened. + research_con = None + try: + from research.multi_schema import open_research_db + research_con = open_research_db("market") + except Exception as exc: # noqa: BLE001 + print(f"[dashboard] research DB unavailable, sparklines disabled: {exc}") + state = DashboardState(db_path, execution_service=None, execution_mode=mode, + research_con=research_con) server = create_server(state, host, port) db_disp = db_path.split("@")[-1] if "@" in db_path else db_path print(f"[dashboard] serving http://{host}:{port} (event db: {db_disp}, mode: {mode})") diff --git a/tests/unit/api/test_dashboard_sparklines.py b/tests/unit/api/test_dashboard_sparklines.py new file mode 100644 index 0000000..b08f91f --- /dev/null +++ b/tests/unit/api/test_dashboard_sparklines.py @@ -0,0 +1,38 @@ +"""Standalone dashboard sparklines: the fix is a NON-None research reader +(plan 007 step 5). Without it bars() short-circuits; with one it runs the query +(an empty array on a bare DB is still a PASS).""" + +import pandas as pd + +from api.main import DashboardState + + +class _FakeResult: + def df(self): + return pd.DataFrame() + + def fetchone(self): + return [None] + + +class _FakeResearchCon: + def execute(self, *args, **kwargs): + return _FakeResult() + + +def test_bars_without_reader_reports_missing_db(): + out = DashboardState("momentum", research_con=None).bars("AAPL") + assert out["bars"] == [] + assert out.get("reason") == "no research db attached" + + +def test_bars_with_reader_runs_query(): + out = DashboardState("momentum", research_con=_FakeResearchCon()).bars("AAPL") + # the reader path ran (empty DB -> empty array, but NOT the "no db" short-circuit) + assert out["bars"] == [] + assert "reason" not in out + + +if __name__ == "__main__": # pragma: no cover + import pytest + pytest.main([__file__, "-q"])