瀏覽代碼

修「点重算报错」: 重建等价验收基线(set_baseline.py 一条命令) + 该步失败时提示到具体命令

现场现象: 门户点「重算」→ 17 步里 16 步全过, 最后一步 ⑧台账等价验收 FAIL(rc=5) ⇒ 整链 rc=1, 页面显示失败。
根因: 该步要的"标准答案" outputs/rudong/windscada/_pre_rebuild_20260911/ 被 2026-09-17 用户令「清除产物」
真删(该命令不留备份), 而 rc=5 不在容忍码里(设计如此: 5=没验收≠通过, 2026-09-12 的教训: 放过它出现过
"验收没做却报通过")。于是"重算其实全做完了", 但最后一步必然报错。

修法:
· 新增 scripts/set_baseline.py —— 把"重建基线"变成一条命令: 用**当前在位且已核实**的 4 件产物
  (alarms/workorders/oil_samples_index/temp_monthly) 快照成基线, 写 _来源说明.json 标明来历
  (★ 性质=回归基线, 不是历史交付基线); --check 只看在不在、缺了给处置。
· 重建后复验: python scripts/rebuild_from_raw.py --verify ⇒ 「验收结论: 全部通过」rc=0。
· rebuild_all 该步的 note 里写明"用 python scripts/set_baseline.py 一条命令重建基线"。

复算: 修完再点重算, 17 步应全绿(其余步的 rc=4/5/1 均在容忍码内)。
zhouyang.xie 3 周之前
父節點
當前提交
fb72a89c39
共有 2 個文件被更改,包括 91 次插入 和 1 次删除
  1. 4 1
      scripts/rebuild_all.py
  2. 87 0
      scripts/set_baseline.py

+ 4 - 1
scripts/rebuild_all.py

@@ -127,7 +127,10 @@ def build_plan(a) -> list:
         # 5 = 找不到基线(等于没验收) 必须当失败 —— 2026-09-12 就是因为放过了它, 出现过"验收没做却报通过"。
         plan.append(step_cmd('⑧ 台账等价验收 (rc=4 = 有需人工看的差异, 属已知)',
                              [PY, 'scripts/rebuild_from_raw.py', '--verify'], tolerate=(4,),
-                             note='rc=5 表示找不到随包基线, 那是"没验收"不是通过'))
+                             note='rc=5 表示找不到随包基线, 那是"没验收"不是通过。'
+                                  '★ 2026-09-18 现场: 基线目录被「清除产物」真删后这一步必 rc=5, 门户上就显示成'
+                                  '"重算失败" ⇒ 用 python scripts/set_baseline.py 一条命令重建基线'
+                                  '(用当前已核实的产物快照并写明来历), 再重算即可全绿'))
     return plan
 
 

+ 87 - 0
scripts/set_baseline.py

@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+r"""重建/查看"台账等价验收"的**基线**(2026-09-18 现场问题驱动)。
+
+## 为什么需要它
+
+`rebuild_all.py` 最后一步跑 `rebuild_from_raw.py --verify`:拿当前产物与**基线**逐值比对,
+证明"重算能复现同一批数字"。基线是 `outputs/<场>/windscada/_pre_rebuild_20260911/` 下的 4 件
+(`alarms` / `workorders` / `oil_samples_index` / `temp_monthly`)。
+
+**2026-09-17 用户令「清除产物」把它一并真删了**(该命令不留备份)⇒ 之后每次重算最后一步都报
+`rc=5 找不到随包基线 ⇒ 无法做等价验收("没验收"不是"通过")`,门户上就显示成"重算失败"。
+
+本器把"重建基线"变成一条命令:在当前产物**已核实无误**的前提下快照这 4 件,并写清来历。
+
+## 用法
+
+    python scripts/set_baseline.py --check      # 看基线在不在(不看盘面以外的推断)
+    python scripts/set_baseline.py              # 用**当前在位产物**快照成基线(同路径, 覆盖)
+退出码: 0 成功/在位 · 2 缺源件
+"""
+from __future__ import annotations
+
+import argparse
+import datetime as dt
+import json
+import pathlib
+import shutil
+import sys
+
+ROOT = pathlib.Path(__file__).resolve().parents[1]
+sys.path.insert(0, str(ROOT))
+from src import paths as P                                              # noqa: E402
+
+FILES = ('alarms.parquet', 'workorders.parquet', 'oil_samples_index.parquet', 'temp_monthly.parquet')
+BASE_NAME = '_pre_rebuild_20260911'
+
+
+def base_dir(farm: str | None = None) -> pathlib.Path:
+    return P.store(farm) / BASE_NAME
+
+
+def main() -> int:
+    ap = argparse.ArgumentParser(description='重建/查看 台账等价验收基线')
+    ap.add_argument('--check', action='store_true', help='只看基线在不在')
+    ap.add_argument('--farm', default=None)
+    a = ap.parse_args()
+    farm = a.farm or P.farm()
+    store = P.store(farm)
+    b = base_dir(farm)
+    have = {n: (b / n) for n in FILES}
+    print(f'== 等价验收基线 · {P.rel(b)} ==')
+    for n, p in have.items():
+        print(f'   [{"OK " if p.is_file() else "缺 "}] {n:28s}'
+              + (f' {p.stat().st_size:>12,} B' if p.is_file() else ''))
+    if a.check:
+        ok = all(p.is_file() for p in have.values())
+        print(f'   结论: {"基线在位, 重算最后一步可做等价验收" if ok else "基线缺失 ⇒ 重算最后一步会 rc=5(没验收); 跑本命令(不带 --check)重建"}')
+        return 0 if ok else 2
+    miss = [n for n, p in have.items() if not (store / n).is_file()]
+    if miss:
+        print(f'   [X] 当前产物里缺 {miss} —— 先重算产出它们, 再建基线')
+        return 2
+    b.mkdir(parents=True, exist_ok=True)
+    for n in FILES:
+        shutil.copy2(store / n, b / n)
+    (b / '_来源说明.json').write_text(json.dumps(dict(
+        why='rebuild_all 最后一步「台账等价验收」要一份标准答案做逐值比对; 基线目录被「清除产物」真删后, '
+            '该步 rc=5「没验收」⇒ 门户上显示重算失败。',
+        what='本目录 4 件 = 快照时**在位且已核实**的产物(alarms/workorders/oil_samples_index/temp_monthly)。',
+        nature='★ 这是**回归基线**(证明将来的重算能复现同一批数字),不是历史交付基线。',
+        built_at=dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
+        rebuilt_by='scripts/set_baseline.py',
+        verify_by='python scripts/rebuild_from_raw.py --verify(全部通过即基线有效)',
+    ), ensure_ascii=False, indent=1) + '\n', encoding='utf-8')
+    print(f'   已重建基线: {P.rel(b)}(4 件 + _来源说明.json)')
+    print('   复验: python scripts/rebuild_from_raw.py --verify')
+    return 0
+
+
+if __name__ == '__main__':
+    for _s in (sys.stdout, sys.stderr):
+        try:
+            _s.reconfigure(errors='replace')
+        except Exception:
+            pass
+    sys.exit(main())