仪表盘展示最新运行快照
This commit is contained in:
+19
-3
@@ -98,14 +98,25 @@ def latest_report(db: Session = Depends(get_db)) -> Optional[Report]:
|
|||||||
def dashboard(db: Session = Depends(get_db)) -> DashboardRead:
|
def dashboard(db: Session = Depends(get_db)) -> DashboardRead:
|
||||||
# 单个仪表盘接口返回运行、信号、日报和主题,前端不需要理解内部调度细节。
|
# 单个仪表盘接口返回运行、信号、日报和主题,前端不需要理解内部调度细节。
|
||||||
runs = list(db.scalars(select(AgentRun).order_by(AgentRun.id.desc()).limit(10)).all())
|
runs = list(db.scalars(select(AgentRun).order_by(AgentRun.id.desc()).limit(10)).all())
|
||||||
|
latest_completed_run = db.scalar(
|
||||||
|
select(AgentRun)
|
||||||
|
.where(AgentRun.status == "completed")
|
||||||
|
.order_by(AgentRun.id.desc())
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
latest_run_id = latest_completed_run.id if latest_completed_run is not None else None
|
||||||
news_signals = list(
|
news_signals = list(
|
||||||
db.scalars(
|
db.scalars(
|
||||||
_visible_signals_query(source_type="news").order_by(Signal.id.desc()).limit(80)
|
_visible_signals_query(source_type="news", run_id=latest_run_id)
|
||||||
|
.order_by(Signal.id.desc())
|
||||||
|
.limit(80)
|
||||||
).all()
|
).all()
|
||||||
)
|
)
|
||||||
github_signals = list(
|
github_signals = list(
|
||||||
db.scalars(
|
db.scalars(
|
||||||
_visible_signals_query(source_type="github").order_by(Signal.id.desc()).limit(80)
|
_visible_signals_query(source_type="github", run_id=latest_run_id)
|
||||||
|
.order_by(Signal.id.desc())
|
||||||
|
.limit(80)
|
||||||
).all()
|
).all()
|
||||||
)
|
)
|
||||||
report = db.scalar(select(Report).order_by(Report.id.desc()).limit(1))
|
report = db.scalar(select(Report).order_by(Report.id.desc()).limit(1))
|
||||||
@@ -144,9 +155,14 @@ def list_topics(db: Session = Depends(get_db)) -> list[Topic]:
|
|||||||
return list(db.scalars(select(Topic).order_by(Topic.id.asc())).all())
|
return list(db.scalars(select(Topic).order_by(Topic.id.asc())).all())
|
||||||
|
|
||||||
|
|
||||||
def _visible_signals_query(source_type: Optional[Literal["news", "github"]] = None):
|
def _visible_signals_query(
|
||||||
|
source_type: Optional[Literal["news", "github"]] = None,
|
||||||
|
run_id: Optional[int] = None,
|
||||||
|
):
|
||||||
# 运行中的信号和完成后的信号走同一张表,查询函数保留统一排序入口。
|
# 运行中的信号和完成后的信号走同一张表,查询函数保留统一排序入口。
|
||||||
query = select(Signal).join(AgentRun, Signal.run_id == AgentRun.id)
|
query = select(Signal).join(AgentRun, Signal.run_id == AgentRun.id)
|
||||||
if source_type is not None:
|
if source_type is not None:
|
||||||
query = query.where(Signal.source_type == source_type)
|
query = query.where(Signal.source_type == source_type)
|
||||||
|
if run_id is not None:
|
||||||
|
query = query.where(Signal.run_id == run_id)
|
||||||
return query
|
return query
|
||||||
|
|||||||
@@ -191,3 +191,71 @@ def test_dashboard_returns_news_and_github_signals_separately(monkeypatch) -> No
|
|||||||
assert [item["source_type"] for item in dashboard["github_signals"]] == ["github"]
|
assert [item["source_type"] for item in dashboard["github_signals"]] == ["github"]
|
||||||
assert github_response.status_code == 200
|
assert github_response.status_code == 200
|
||||||
assert [item["title"] for item in github_response.json()] == ["example/agent-kit"]
|
assert [item["title"] for item in github_response.json()] == ["example/agent-kit"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_dashboard_uses_latest_completed_run_signals(monkeypatch) -> None:
|
||||||
|
# 情报页展示最新完成运行的快照,历史运行只保留在记录和明细接口里。
|
||||||
|
monkeypatch.setenv("SIGNALSCOUT_ENV", "test")
|
||||||
|
get_settings.cache_clear()
|
||||||
|
engine = create_engine(
|
||||||
|
"sqlite:///:memory:",
|
||||||
|
connect_args={"check_same_thread": False},
|
||||||
|
poolclass=StaticPool,
|
||||||
|
future=True,
|
||||||
|
)
|
||||||
|
TestingSessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True)
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
with TestingSessionLocal() as session:
|
||||||
|
old_run = AgentRun(status="completed", started_at=datetime(2026, 7, 8, 8, 0), request_payload={})
|
||||||
|
new_run = AgentRun(status="completed", started_at=datetime(2026, 7, 8, 9, 0), request_payload={})
|
||||||
|
session.add_all([old_run, new_run])
|
||||||
|
session.flush()
|
||||||
|
session.add_all(
|
||||||
|
[
|
||||||
|
Signal(
|
||||||
|
run_id=old_run.id,
|
||||||
|
source_type="news",
|
||||||
|
topic="AI 新闻",
|
||||||
|
title="Old signal",
|
||||||
|
summary="旧运行信号。",
|
||||||
|
source_url="https://example.com/old",
|
||||||
|
source_url_hash="old-hash",
|
||||||
|
source_name="Example",
|
||||||
|
published_at=datetime(2026, 7, 8, 8, 0),
|
||||||
|
importance=3,
|
||||||
|
entities=[],
|
||||||
|
),
|
||||||
|
Signal(
|
||||||
|
run_id=new_run.id,
|
||||||
|
source_type="news",
|
||||||
|
topic="AI 新闻",
|
||||||
|
title="New signal",
|
||||||
|
summary="新运行信号。",
|
||||||
|
source_url="https://example.com/new",
|
||||||
|
source_url_hash="new-hash",
|
||||||
|
source_name="Example",
|
||||||
|
published_at=datetime(2026, 7, 8, 9, 0),
|
||||||
|
importance=4,
|
||||||
|
entities=[],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
def override_get_db():
|
||||||
|
# 仪表盘快照测试使用内存库构造多运行场景。
|
||||||
|
db = TestingSessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
app.dependency_overrides[get_db] = override_get_db
|
||||||
|
with TestClient(app) as client:
|
||||||
|
response = client.get("/dashboard")
|
||||||
|
get_settings.cache_clear()
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert [item["title"] for item in response.json()["news_signals"]] == ["New signal"]
|
||||||
|
|||||||
Reference in New Issue
Block a user