fix(P0-02): 积分榜改为追加快照(append-only) + available_at cutoff
去掉 uq_standings_league_season_team,改为 (league, season, team, available_at) 唯一; 每次采集 INSERT 新行(available_at=now),ON CONFLICT DO NOTHING,不覆盖旧行。 standings_slice(before):DISTINCT ON (team_id) WHERE available_at<=cutoff ORDER available_at DESC;before=None → cutoff=now()。 公开 list_standings 取每队最新可用快照(子查询 max available_at)。 迁移 0023 + 切片/路由/docs 同步;测试 test_p0_standings_cutoff(5/5)。 284 测试全绿。
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""D - 联赛排名切片: 积分榜位置与实力差距(standings)。"""
|
||||
"""D - 联赛排名切片: 积分榜快照(支持 cutoff 的历史还原,standings)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -7,6 +7,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from src.db.base import AsyncSessionLocal
|
||||
from src.db.models import League, Standing
|
||||
from src.llm.slices.common import MatchHeader, SliceResult
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -16,24 +17,33 @@ if TYPE_CHECKING:
|
||||
async def standings_slice(header: MatchHeader, *, before=None, db: AsyncSession | None = None) -> SliceResult:
|
||||
"""D - 联赛排名切片: 两队积分榜位置、积分、近期走势(form)、分区,评估整体实力差距。
|
||||
|
||||
before 参数保留与其他切片一致的签名(积分榜是最新快照,无历史版本,不受 cutoff 影响)。
|
||||
P0-02: 支持 cutoff(before)。取 available_at<=cutoff 的每队最新快照
|
||||
(DISTINCT ON team_id ORDER available_at DESC);before=None 时 cutoff=now()。
|
||||
回测时可还原历史时刻榜单,不再只是"最新快照、忽略 cutoff"。
|
||||
|
||||
db: 可选共享 session(见 context_builder 模块 docstring)。
|
||||
|
||||
语义区分:
|
||||
- 两队都有积分榜行 → has_data=True(明确的排名信息)
|
||||
- 任一队缺失 → has_data=False(升班马/杯赛无榜,信息不完整时明确声明)
|
||||
"""
|
||||
from src.db.models import League, Standing
|
||||
# P0-02: before=None → cutoff=now()(取最新可用快照)
|
||||
if before is None:
|
||||
from datetime import datetime, timezone
|
||||
before = datetime.now(timezone.utc)
|
||||
|
||||
if db is not None:
|
||||
league = (await db.execute(select(League).where(League.id == header.league_id))).scalar_one_or_none()
|
||||
# P0-02: DISTINCT ON (team_id) 取 available_at<=cutoff 的最新快照
|
||||
rows = (
|
||||
(
|
||||
await db.execute(
|
||||
select(Standing)
|
||||
.options(selectinload(Standing.team))
|
||||
.where(Standing.league_id == header.league_id)
|
||||
.order_by(Standing.position.asc())
|
||||
.where(Standing.available_at <= before)
|
||||
.distinct(Standing.team_id)
|
||||
.order_by(Standing.team_id, Standing.available_at.desc())
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
|
||||
Reference in New Issue
Block a user