pbl_blueprint/scripts/patch_selfcheck_design.py
2026-09-16 12:49:06 +08:00

129 lines
6.2 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 -*-
"""对齐 selfcheck.py F/H 组判定到设计定稿(幂等,改后 py_compile 校验,失败回滚)。
H 组:设计定稿 B4~B11 为**独立建表**(非泛化单表 pbl_subobject
核心表判定改为 B1 pbl_blueprint / B2 pbl_blueprint_version / B3 pbl_blueprint_approval
并逐表核验 B4~B11 八张子对象表均有 CREATE TABLE。
F 组load_path.py 是 RBAC 唯一声明源,必须**覆盖**超集json/*.json editable 的 3×11 条,
故一致性判定改为「json editable ⊆ load_path PATHS」不再要求双向相等
(支撑表含 lock/fork/lineage 等额外动作,天然多于 editable 三条)。
"""
import os
import shutil
import subprocess
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SC = os.path.join(ROOT, "scripts", "selfcheck.py")
src = open(SC, "r", encoding="utf-8").read()
orig = src
bak = SC + ".pbak"
shutil.copyfile(SC, bak)
# ---------------------------------------------------------------- H 组核心表
old_h = ''' need_core = ["pbl_blueprint", "pbl_blueprint_version", "pbl_subobject"]
miss = [t for t in need_core if t not in tables]
check(g, not miss, "DDL 覆盖核心表(主表/版本/泛化子对象)",
"缺失=%s 实际=%s" % (miss or "", tables))'''
new_h = ''' # 设计定稿 B1~B3 核心表B4~B11 为独立建表,非泛化单表)
need_core = ["pbl_blueprint", "pbl_blueprint_version", "pbl_blueprint_approval"]
miss = [t for t in need_core if t not in tables]
check(g, not miss, "DDL 覆盖核心表 B1~B3聚合根/版本/审批)",
"缺失=%s" % (miss or ""))
# 设计定稿 B4~B11 八张子对象表必须独立建表
need_sub = ["pbl_learner", "pbl_learning_goal", "pbl_problem",
"pbl_driving_question", "pbl_project", "pbl_role",
"pbl_mission", "pbl_artifact_def"]
miss_sub = [t for t in need_sub if t not in tables]
check(g, not miss_sub, "DDL 覆盖子对象表 B4~B118 张独立建表)",
"缺失=%s 建表总数=%d" % (miss_sub or "", len(tables)))'''
if old_h in src:
src = src.replace(old_h, new_h)
print("[FIX ] H 组核心表判定对齐设计定稿 B1~B11")
else:
print("[WARN] H 组未匹配到旧规则(可能已修)")
# ---------------------------------------------------------------- H 组承载映射
old_c = ''' uncovered = []
for t in mfiles:
if t in tables:
continue
carrier = GENERIC_CARRIER.get(t)
if carrier and carrier in tables:
continue
# 其余子对象表由泛化单表承载
if "pbl_subobject" in tables and t.startswith("pbl_blueprint_"):
continue
uncovered.append(t)
check(g, not uncovered, "models 11 表均有物理承载(独立建表或泛化单表)",
"未承载=%s 泛化映射=%s" % (uncovered or "", GENERIC_CARRIER))'''
new_c = ''' # 设计定稿models 主清单 11 表 = DDL 独立建表,逐表必须有 CREATE TABLE
uncovered = [t for t in mfiles if t not in tables]
check(g, not uncovered, "models 主清单 11 表均有独立 CREATE TABLE 承载",
"未承载=%s models=%d ddl建表=%d" % (uncovered or "", len(mfiles), len(tables)))'''
if old_c in src:
src = src.replace(old_c, new_c)
print("[FIX ] H 组承载判定改为逐表独立建表")
else:
print("[WARN] H 组承载规则未匹配(可能已修)")
# ---------------------------------------------------------------- F 组一致性
old_f = ''' only_lp = [p for p in lits if p not in impl]
only_impl = sorted(p for p in impl if p not in lits)
check(g, not only_lp and not only_impl,
"load_path.py 与实现(api*.py/json editable) RBAC 路径一致",
"load_path=%d 实现=%d 仅load_path=%s 仅实现=%s"
% (len(lits), len(impl), only_lp[:5] or "", only_impl[:5] or ""))'''
new_f = ''' # load_path.py 是 RBAC 唯一声明源,必须覆盖 json editable 的 3×11 条(超集合法:
# 支撑表含 lock/fork/lineage 等额外动作。故只判「json editable ⊆ load_path」。
missing = sorted(p for p in impl if p not in lits)
check(g, not missing,
"load_path.py 覆盖 json/*.json editable 全部路径(超集)",
"load_path=%d json_editable=%d 未覆盖=%s"
% (len(lits), len(impl), missing[:5] or ""))
# 反向load_path 中每条路径必须归属已知表(主清单 11 + 支撑表 9防孤儿路径
known = set(MAIN_TABLES_KNOWN) | set(SUPPORT_TABLES_KNOWN)
orphan = []
for p in lits:
m = re.match(r"/api/([a-z0-9_]+)/", p)
if not m or m.group(1) not in known:
orphan.append(p)
check(g, not orphan, "load_path.py 无孤儿路径(均归属已知表)",
"孤儿=%s" % (orphan[:5] or ""))'''
if old_f in src:
src = src.replace(old_f, new_f)
print("[FIX ] F 组一致性改为超集覆盖 + 孤儿路径检查")
else:
print("[WARN] F 组规则未匹配(可能已修)")
# 注入已知表名常量
if "MAIN_TABLES_KNOWN" not in src:
consts = '''
MAIN_TABLES_KNOWN = (
"pbl_blueprint", "pbl_blueprint_version", "pbl_blueprint_approval",
"pbl_learner", "pbl_learning_goal", "pbl_problem", "pbl_driving_question",
"pbl_project", "pbl_role", "pbl_mission", "pbl_artifact_def",
)
SUPPORT_TABLES_KNOWN = (
"pbl_blueprint_audit", "pbl_blueprint_edge", "pbl_blueprint_fork",
"pbl_blueprint_lock", "pbl_blueprint_node", "pbl_blueprint_offline",
"pbl_blueprint_publish", "pbl_blueprint_template", "pbl_blueprint_version_delta",
)
'''
src = src.replace("\nLINES: list[str] = []", consts + "\nLINES: list[str] = []", 1)
if src != orig:
open(SC, "w", encoding="utf-8").write(src)
r = subprocess.run([sys.executable, "-m", "py_compile", SC],
capture_output=True, text=True)
if r.returncode != 0:
shutil.move(bak, SC)
print("[ROLLBACK] selfcheck.py 编译失败已回滚: %s" % (r.stderr or "")[-400:])
sys.exit(1)
os.remove(bak)
print("[OK ] selfcheck.py 已更新并通过 py_compile")
else:
os.remove(bak)
print("[OK ] selfcheck.py 无需修改")