| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- # -*- coding: utf-8 -*-
- """Apply S0 blueprint front matter to all S0-S4 delivery docx (recursive)."""
- from __future__ import annotations
- import importlib.util
- import shutil
- import tempfile
- from pathlib import Path
- from docx import Document
- from docx.oxml.ns import qn
- from docx.text.paragraph import Paragraph
- BASE = Path(
- r"C:\Users\skygu\OneDrive\Projects\AIDOP\项目\项目管理\产互联项目管理\交付文档"
- )
- MODULES = ["S0", "S1", "S2", "S3", "S4"]
- def load_module(name: str, filename: str):
- path = Path(__file__).with_name(filename)
- spec = importlib.util.spec_from_file_location(name, path)
- mod = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(mod)
- return mod
- def iter_delivery_docx() -> list[Path]:
- files: list[Path] = []
- for module in MODULES:
- mod_dir = BASE / module
- if not mod_dir.is_dir():
- continue
- for path in sorted(mod_dir.rglob("*.docx")):
- if path.name.startswith("~$"):
- continue
- files.append(path)
- return files
- def has_toc(doc: Document) -> bool:
- return any(p.text.strip() == "目录" for p in doc.paragraphs)
- def has_page_break(p: Paragraph) -> bool:
- return bool(p._element.findall(".//" + qn("w:br")))
- def front_matter_status(doc: Document) -> str:
- if not has_toc(doc):
- return "skip-no-toc"
- has_control = any(p.text.strip() == "文档控制" for p in doc.paragraphs)
- has_change = any(p.text.strip() == "更改记录" for p in doc.paragraphs)
- has_version = any(p.text.strip() == "版本记录" for p in doc.paragraphs)
- has_old_meta = any(
- p.text.strip().startswith(prefix)
- for p in doc.paragraphs[:20]
- for prefix in ("文档作者:", "创建日期:", "当前版本:")
- )
- has_cover_table = any(
- table.rows
- and table.rows[0].cells[0].text.strip() == "文档编号"
- and len(table.rows) == 5
- for table in doc.tables
- )
- version_break = False
- toc_break = False
- for p in doc.paragraphs:
- t = p.text.strip()
- if t == "版本记录":
- version_break = has_page_break(p)
- if t == "目录":
- toc_break = has_page_break(p)
- if has_control or has_change or has_old_meta or not has_cover_table:
- return "rebuild"
- if not has_version:
- return "rebuild"
- if not version_break or not toc_break:
- return "adjust"
- return "ok"
- def infer_module_kind(path: Path) -> tuple[str, str]:
- rel = path.relative_to(BASE)
- module = rel.parts[0]
- kind = "blue" if "蓝图" in path.name else "req"
- return module, kind
- def save_with_fallback(doc: Document, path: Path) -> None:
- try:
- doc.save(path)
- except PermissionError:
- tmp = Path(tempfile.gettempdir()) / f"docfix_{path.name}"
- doc.save(tmp)
- try:
- shutil.copy2(tmp, path)
- tmp.unlink(missing_ok=True)
- except PermissionError:
- print(f" !! {path.name} 被占用,已写入: {tmp}")
- def process_file(path: Path) -> None:
- rel = path.relative_to(BASE)
- doc = Document(path)
- status = front_matter_status(doc)
- if status == "skip-no-toc":
- print(f"{rel}: skip (无目录)")
- return
- if status == "ok":
- print(f"{rel}: ok")
- return
- module, kind = infer_module_kind(path)
- adjust = load_module("adjust", "_adjust_front_matter.py")
- if status == "rebuild":
- unify = load_module("unify", "_unify_doc_format.py")
- restore = load_module("restore", "_restore_cover_format.py")
- versions = load_module("versions", "_unify_version_records.py")
- unify.unify_document(path, module, kind)
- restore.restore_file(path)
- versions.process_file(path)
- adjust.adjust_file(path)
- print(f"{rel}: rebuilt + formatted")
- return
- adjust.adjust_file(path)
- print(f"{rel}: adjusted page breaks")
- def main() -> None:
- for path in iter_delivery_docx():
- process_file(path)
- if __name__ == "__main__":
- main()
|