fix(cma): my-dashboard KPI period caliber (monthly period priority) + as-of/stale marks
my_dashboard picked the row with max calculated_at, so batch backfills made stale periods win (revenue/net-profit showed 2026-07, rebate rate 2026-04). Now prefer the latest monthly period, fall back to calculated_at. Frontend adds data-as-of label and stale tag; not deployed yet (push gated by another pending commit).
This commit is contained in:
@@ -565,6 +565,35 @@ def predict_kpis(db: Session = Depends(get_db), entity_id: int = Depends(get_ent
|
||||
|
||||
# ── 个人工作台 ──────────────────────────────
|
||||
|
||||
# KPIValue 取数口径(2026-09-18 工作台「我的KPI」期间缺口修正)
|
||||
# 原实现(my_dashboard 内联)按 calculated_at 倒序取「最近写入」的一条:批量补录/复采会把
|
||||
# **旧期间**的行写得更晚 → 卡片显示过期期间。实测 entity1:F_REVENUE/F_NET_PROFIT 的
|
||||
# 2026-07 行写入时刻(2026-09-14 17:01:22, id=2128/2130)晚于 2026-08 行(17:00:30, id=2125/2127)
|
||||
# → 取到 7 月;C_REBATE_RATE 1–4 月补录行 calculated_at 并列(2026-08-24 17:17:38) → 取到 4 月。
|
||||
# 新口径:优先取「月期间 YYYY-MM」中最新的一期(月期是采集/录入的常规粒度);若该 KPI 只有
|
||||
# H1/H2/FY 等汇总期行,则回退到原按 calculated_at 的取法(不改变无月期 KPI 的行为)。
|
||||
# 注意:**不能**只按 period 字典序排 —— '2026-H1'/'2026-H2' 字典序大于 '2026-08',会把
|
||||
# 季度汇总行/半年度预估值(forecast)当成"最新"。
|
||||
_MONTH_PERIOD_REGEX = r"^[0-9]{4}-(0[1-9]|1[0-2])$"
|
||||
|
||||
|
||||
def latest_kpi_value(db: Session, kpi_id: int) -> Optional[KPIValue]:
|
||||
"""该 KPI 的最新可用值:月期间优先,无月期间行时回退 calculated_at 倒序。"""
|
||||
monthly = (
|
||||
db.query(KPIValue)
|
||||
.filter(KPIValue.kpi_id == kpi_id, KPIValue.period.op("REGEXP")(_MONTH_PERIOD_REGEX))
|
||||
.order_by(KPIValue.period.desc(), KPIValue.calculated_at.desc(), KPIValue.id.desc())
|
||||
.first()
|
||||
)
|
||||
if monthly is not None:
|
||||
return monthly
|
||||
return (
|
||||
db.query(KPIValue)
|
||||
.filter(KPIValue.kpi_id == kpi_id)
|
||||
.order_by(KPIValue.calculated_at.desc(), KPIValue.id.desc())
|
||||
.first()
|
||||
)
|
||||
|
||||
|
||||
@router.get("/my-dashboard")
|
||||
def my_dashboard(
|
||||
@@ -613,9 +642,7 @@ def my_dashboard(
|
||||
|
||||
kpi_list = []
|
||||
for k in all_kpis:
|
||||
latest_v = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == k.id
|
||||
).order_by(KPIValue.calculated_at.desc()).first()
|
||||
latest_v = latest_kpi_value(db, k.id)
|
||||
|
||||
actual = latest_v.actual_value if latest_v else None
|
||||
target = kpi_target_by_frequency(k)
|
||||
|
||||
Reference in New Issue
Block a user