Files
iAOP/web/cockpit/scripts/build_plans.py
T

125 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""驾驶舱前端渲染计划编译脚本(issue #131 / PRD 5.5「⑤ 配置化驾驶舱」)。
把行业模板的布局 / 告警面板 **YAML 资产**编译成前端可直接 ``fetch`` 的
**RenderPlan JSON**,落到 ``web/cockpit/plans/``:
- ``plans/ti-cl4.json`` ← ``templates/ti-cl4/dashboard/cockpit.ti.yaml``
- ``plans/resin.json`` ← ``templates/resin/dashboard/cockpit.resin.yaml``
- ``plans/alarm_panel.ti.json``← ``templates/ti-cl4/dashboard/alarm_panel.ti.yaml``
切换行业模板 = 前端加载另一份 plan JSON,**前端代码零改动**(PRD 5.5 验收口径)。
编译在开发期离线完成,前端页面保持纯静态(无构建链、无后端依赖)。
本脚本是唯一依赖 PyYAML 的环节(开发期依赖,不进运行时);渲染逻辑全部复用
``core/cockpit``(#50 布局校验 / #51 渲染计划 / #52 告警面板 props),
保证前端看到的 JSON 与后端 Schema 严格一致。
用法(仓库根目录下):
python web/cockpit/scripts/build_plans.py
退出码:0 = 全部编译成功;1 = 存在失败项。
"""
from __future__ import annotations
import json
import os
import sys
import yaml # 开发期依赖(PyYAML),仅本脚本使用;前端运行时不依赖
# 仓库根目录加入 sys.path,以便 `from core.cockpit import ...`
_REPO_ROOT = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
)
sys.path.insert(0, _REPO_ROOT)
from core.cockpit import ( # noqa: E402
load_alarm_config,
load_layout,
plan_to_dict,
render_alarm_panel_props,
render_layout,
)
# 布局资产 → plan JSON 文件名
_LAYOUTS = {
"ti-cl4": "templates/ti-cl4/dashboard/cockpit.ti.yaml",
"resin": "templates/resin/dashboard/cockpit.resin.yaml",
}
# 告警面板配置资产 → plan JSON 文件名
_ALARM_PANELS = {
"alarm_panel.ti": "templates/ti-cl4/dashboard/alarm_panel.ti.yaml",
}
_OUT_DIR = os.path.join(_REPO_ROOT, "web", "cockpit", "plans")
def _load_yaml(path: str) -> dict:
with open(path, encoding="utf-8") as f:
return yaml.safe_load(f)
def _write_json(name: str, data: dict) -> str:
os.makedirs(_OUT_DIR, exist_ok=True)
out_path = os.path.join(_OUT_DIR, name + ".json")
with open(out_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return out_path
def _enrich_process_view_stages(plan_dict: dict, layout_data: dict) -> None:
"""把布局资产里 process_view 的 ``stages``(四状态工艺语义)注入 plan JSON。
#51 ``_build_props`` 只透传 ``src``/``states``,四状态清单(氯化→精制→
还原→蒸馏)留在布局资产里;前端 ProcessView 组件需要它来渲染流程节点,
因此在编译期按 widget 顺序补进 ``props.stages``(specs 与源 widgets 同序)。
"""
src_widgets = layout_data.get("widgets") or []
specs = plan_dict.get("widgets") or []
for spec, raw in zip(specs, src_widgets):
if spec.get("type") == "process_view" and isinstance(raw, dict):
stages = raw.get("stages")
if stages:
spec.setdefault("props", {})["stages"] = stages
def main() -> int:
failures = 0
# 1) 布局资产 → RenderPlan JSON(#50 校验 + #51 渲染计划)
for name, rel in _LAYOUTS.items():
try:
data = _load_yaml(os.path.join(_REPO_ROOT, rel))
plan = render_layout(load_layout(data))
plan_dict = plan_to_dict(plan)
_enrich_process_view_stages(plan_dict, data)
out = _write_json(name, plan_dict)
print(f"[OK] {rel} -> {os.path.relpath(out, _REPO_ROOT)} "
f"({plan.widget_count} widgets, theme={plan.theme})")
except Exception as exc: # noqa: BLE001 - 编译脚本需聚合全部失败
failures += 1
print(f"[FAIL] {rel}: {exc}")
# 2) 告警面板配置资产 → alarm_panel props JSON(#52)
for name, rel in _ALARM_PANELS.items():
try:
data = _load_yaml(os.path.join(_REPO_ROOT, rel))
props = render_alarm_panel_props(load_alarm_config(data))
out = _write_json(name, props)
print(f"[OK] {rel} -> {os.path.relpath(out, _REPO_ROOT)} "
f"(severities={sorted(props['severityStyles'])})")
except Exception as exc: # noqa: BLE001
failures += 1
print(f"[FAIL] {rel}: {exc}")
if failures:
print(f"共 {failures} 项编译失败")
return 1
print("全部渲染计划编译完成")
return 0
if __name__ == "__main__":
sys.exit(main())