_formalize_delivery_docs.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. # -*- coding: utf-8 -*-
  2. """正式交付稿:去掉临时/注释性文字,另存新版本。"""
  3. from __future__ import annotations
  4. import re
  5. import shutil
  6. import sys
  7. import tempfile
  8. from pathlib import Path
  9. from docx import Document
  10. HERE = Path(__file__).resolve().parent
  11. sys.path.insert(0, str(HERE))
  12. from _add_req_func_codes import set_paragraph_text # noqa: E402
  13. from _adjust_front_matter import remove_paragraph # noqa: E402
  14. from _unify_ab_delivery_vnext import ( # noqa: E402
  15. BASE,
  16. append_version_row,
  17. dest_name,
  18. ensure_version_page,
  19. find_p,
  20. iter_paras,
  21. open_via_temp,
  22. update_cover_version,
  23. version_table_after,
  24. )
  25. from _gen_delivery_sys_docs import ( # noqa: E402
  26. write_module_dds,
  27. write_module_um,
  28. write_sys_bbp,
  29. write_sys_brd,
  30. write_sys_dds,
  31. write_sys_um,
  32. )
  33. from _gen_s9_diag_impr_int_delivery import write_bbp, write_brd # noqa: E402
  34. from _gen_s9_modules_data import MODULES # noqa: E402
  35. NOTE_OLD = "统一系统名与版本记录"
  36. NOTE_NEW = "统一系统名称与文档版式,并修订范围与用词。"
  37. DROP_RE = re.compile(
  38. r"更新域|按 F9|按F9|如目录未显示|请在 Word 中右键|"
  39. r"不再使用|统一系统名与版本记录|"
  40. r"AidopDemoShell|AllowAnonymous|仅占位|规划占位|"
  41. r"本批禁用占位|写动作禁用占位|禁用占位|"
  42. r"占位与演示边界|演示基线|数据待接入|"
  43. r"实现现状(对照实际系统)|完成度:|"
  44. r"MVP 不得伪称|列为后续|缺口(|本轮不单独立册"
  45. )
  46. # 现行正式稿已手改、禁止再从上一号重出覆盖。
  47. # S3 用户操作手册 V1.2:Release 手换「工单物料齐套上线看板」截图并调整图高。
  48. SKIP_JOBS = {
  49. ("S3", "S3供应协同模块用户操作手册-V1.1.docx"),
  50. }
  51. JOBS = [
  52. ("S0", "S0-运营建模-业务需求描述-V0.7.docx", "V0.7", "V0.8"),
  53. ("S0", "S0-运营建模-业务蓝图设计方案-V0.7.docx", "V0.7", "V0.8"),
  54. ("S0", "S0运营建模模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  55. ("S0", "S0-运营建模-用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  56. ("S1", "S1产销协同业务需求描述-V0.3.docx", "V0.3", "V0.4"),
  57. ("S1", "S1产销协同模块蓝图设计方案-V2.1.docx", "V2.1", "V2.2"),
  58. ("S1", "S1产销协同模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  59. ("S1", "S1产销协同模块用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  60. ("S2", "S2制造协同业务需求描述-V0.3.docx", "V0.3", "V0.4"),
  61. ("S2", "S2制造协同模块蓝图设计方案-V0.3.docx", "V0.3", "V0.4"),
  62. ("S2", "S2制造协同模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  63. ("S2", "S2制造协同模块用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  64. ("S3", "S3供应协同业务需求描述-V0.3.docx", "V0.3", "V0.4"),
  65. ("S3", "S3供应协同模块蓝图设计方案-V0.3.docx", "V0.3", "V0.4"),
  66. ("S3", "S3供应协同模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  67. ("S3", "S3供应协同模块用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  68. ("S4", "S4采购执行业务需求描述-V0.3.docx", "V0.3", "V0.4"),
  69. ("S4", "S4采购执行模块蓝图设计方案-V0.3.docx", "V0.3", "V0.4"),
  70. ("S4", "S4采购执行模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  71. ("S4", "S4采购执行模块用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  72. ("S5", "S5-物料仓储-业务需求描述-V1.1.docx", "V1.1", "V1.2"),
  73. ("S5", "S5-物料仓储-业务蓝图设计方案-V0.2.docx", "V0.2", "V0.3"),
  74. ("S5", "S5物料仓储模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  75. ("S5", "S5-物料仓储-用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  76. ("S6", "S6-生产执行-业务需求描述-V1.1.docx", "V1.1", "V1.2"),
  77. ("S6", "S6-生产执行-业务蓝图设计方案-V0.2.docx", "V0.2", "V0.3"),
  78. ("S6", "S6生产执行模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  79. ("S6", "S6-生产执行-用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  80. ("S7", "S7-成品仓储-业务需求描述-V1.1.docx", "V1.1", "V1.2"),
  81. ("S7", "S7-成品仓储-业务蓝图设计方案-V0.2.docx", "V0.2", "V0.3"),
  82. ("S7", "S7成品仓储模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  83. ("S7", "S7-成品仓储-用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  84. ("S8", "S8-异常监控-业务需求描述-V1.1.docx", "V1.1", "V1.2"),
  85. ("S8", "S8-异常监控-业务蓝图设计方案-V0.2.docx", "V0.2", "V0.3"),
  86. ("S8", "S8异常监控模块详细设计说明书-V1.1.docx", "V1.1", "V1.2"),
  87. ("S8", "S8-异常监控-用户操作手册-V1.1.docx", "V1.1", "V1.2"),
  88. ]
  89. def drop_or_rewrite(doc: Document) -> int:
  90. n = 0
  91. for p in list(doc.paragraphs):
  92. t = p.text.strip()
  93. if not t:
  94. continue
  95. if "临时加产" in t:
  96. continue
  97. if NOTE_OLD in t:
  98. set_paragraph_text(p, t.replace(t, NOTE_NEW) if t.startswith("统一系统名") else NOTE_NEW)
  99. n += 1
  100. continue
  101. if DROP_RE.search(t):
  102. remove_paragraph(p)
  103. n += 1
  104. for table in doc.tables:
  105. for row in table.rows:
  106. for cell in row.cells:
  107. for p in cell.paragraphs:
  108. raw = p.text
  109. if NOTE_OLD in raw or "不再使用「数据中台」" in raw:
  110. set_paragraph_text(p, NOTE_NEW)
  111. n += 1
  112. elif DROP_RE.search(raw) and "版本" not in raw[:6]:
  113. if raw.strip() in {"文档编号", "版本", "密级", "编制单位", "编制日期"}:
  114. continue
  115. if re.match(r"^V\d", raw.strip()):
  116. continue
  117. set_paragraph_text(p, NOTE_NEW if "变更" in "".join(c.text for c in row.cells) else "")
  118. n += 1
  119. return n
  120. def process_sxx(module: str, src_name: str, old_ver: str, new_ver: str) -> str:
  121. src = BASE / module / src_name
  122. if not src.exists():
  123. return f"MISSING {src}"
  124. dest = BASE / module / dest_name(src_name, new_ver)
  125. doc, _ = open_via_temp(src)
  126. n = drop_or_rewrite(doc)
  127. update_cover_version(doc, old_ver, new_ver)
  128. heading = find_p(doc, "版本记录")
  129. if heading is not None:
  130. tbl = version_table_after(heading)
  131. if tbl is not None:
  132. append_version_row(tbl, new_ver)
  133. for row in tbl.rows:
  134. for cell in row.cells:
  135. if NOTE_OLD in cell.text:
  136. set_paragraph_text(cell.paragraphs[0], NOTE_NEW)
  137. else:
  138. ensure_version_page(doc, old_ver, new_ver)
  139. else:
  140. ensure_version_page(doc, old_ver, new_ver)
  141. tmp = Path(tempfile.gettempdir()) / "aidop_unify_ab" / f"formal_{dest.stem[:40]}.docx"
  142. tmp.parent.mkdir(parents=True, exist_ok=True)
  143. doc.save(str(tmp))
  144. shutil.copy2(tmp, dest)
  145. return f"OK {dest.name} drop={n} {dest.stat().st_size // 1024}KB"
  146. def write_generated() -> list[str]:
  147. import _gen_delivery_sys_docs as sysgen
  148. msgs = []
  149. orig = sysgen.save_doc
  150. def save_ver(doc, dest):
  151. dest = Path(dest)
  152. orig(doc, dest.with_name(f"{dest.stem}-V0.4{dest.suffix}"))
  153. sysgen.save_doc = save_ver
  154. try:
  155. write_sys_brd()
  156. write_sys_bbp()
  157. write_sys_dds()
  158. write_sys_um()
  159. for key in ("运营诊断", "运营改善", "ChatBI", "系统集成"):
  160. write_module_dds(key, MODULES[key])
  161. write_module_um(key, MODULES[key])
  162. finally:
  163. sysgen.save_doc = orig
  164. msgs.append("SYS+DDS/UM V0.4 written")
  165. for key in ("S9", "运营诊断", "运营改善", "ChatBI", "系统集成"):
  166. mod = MODULES[key]
  167. folder = BASE / mod["folder"]
  168. write_brd(mod, folder / f"{Path(mod['brd_name']).stem}-V0.4.docx")
  169. write_bbp(mod, folder / f"{Path(mod['bbp_name']).stem}-V0.4.docx")
  170. msgs.append(f"{key} BRD/BBP V0.4")
  171. return msgs
  172. def main() -> None:
  173. lines = []
  174. for module, src_name, old_ver, new_ver in JOBS:
  175. if (module, src_name) in SKIP_JOBS:
  176. dest = dest_name(src_name, new_ver)
  177. msg = f"SKIP {module} {src_name} -> {dest} (hand-maintained)"
  178. print(msg, flush=True)
  179. lines.append(msg)
  180. continue
  181. print(f"formalize {module} {src_name}", flush=True)
  182. try:
  183. msg = process_sxx(module, src_name, old_ver, new_ver)
  184. except Exception as exc:
  185. msg = f"FAIL {module} {src_name}: {exc}"
  186. print(msg, flush=True)
  187. lines.append(msg)
  188. print("regenerate topic docs", flush=True)
  189. lines.extend(write_generated())
  190. print("\n".join(lines), flush=True)
  191. if __name__ == "__main__":
  192. main()