operator-plane: wire research reader into standalone dashboard (sparklines)
DashboardState.bars() short-circuits to [] unless a research_con is attached; the
embedded orchestrator passed one but the standalone dashboard_api.py never built
it, so production sparklines were permanently dead. The missing wire was purely
the entrypoint (DATABASE_URL already reaches the market schema) — now it opens
open_research_db("market") read-only and passes it, degrading to None (charts
show "no research db") if it can't connect. Verified live: bars() returns real
minute rows through the standalone reader.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cb206e45cb
commit
21687c97ff
2 files changed
+51
-1
No files matched your search
+13
-1
@@ -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})")
|
||||
|
||||
@@ -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"])
|
||||
Reference in new issue
Block a user