| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- # -*- coding: utf-8 -*-
- """Unify system name and add version record tables in S0-S4 delivery docx."""
- from __future__ import annotations
- import re
- from pathlib import Path
- from docx import Document
- from docx.enum.text import WD_BREAK
- from docx.oxml import OxmlElement
- from docx.shared import Pt
- from docx.text.paragraph import Paragraph
- BASE = Path(
- r"C:\Users\skygu\OneDrive\Projects\AIDOP\项目\项目管理\产互联项目管理\交付文档"
- )
- OLD_NAMES = (
- "Ai-DOP 智慧运营决策平台",
- "Ai-DOP智慧运营决策平台",
- )
- NEW_NAME = "Ai-DOP智慧运营管理系统"
- TODAY = "2026-06-22"
- CHANGE_NOTE = "统一系统名称为 Ai-DOP智慧运营管理系统"
- def iter_all_paragraphs(doc: Document):
- for p in doc.paragraphs:
- yield p
- for table in doc.tables:
- for row in table.rows:
- for cell in row.cells:
- for p in cell.paragraphs:
- yield p
- def replace_in_paragraph(paragraph, old: str, new: str) -> bool:
- text = paragraph.text
- if old not in text:
- return False
- if paragraph.runs:
- combined = "".join(r.text for r in paragraph.runs)
- if old not in combined:
- return False
- new_text = combined.replace(old, new)
- paragraph.runs[0].text = new_text
- for run in paragraph.runs[1:]:
- run.text = ""
- else:
- paragraph.add_run(text.replace(old, new))
- return True
- def replace_system_name(doc: Document) -> int:
- changed = 0
- for old in OLD_NAMES:
- for p in iter_all_paragraphs(doc):
- if replace_in_paragraph(p, old, NEW_NAME):
- changed += 1
- return changed
- def parse_metadata(doc: Document) -> dict[str, str]:
- meta = {"author": "智造易项目组", "created": "", "updated": "", "version": ""}
- for p in doc.paragraphs[:25]:
- t = p.text.strip()
- if t.startswith("文档作者:"):
- meta["author"] = t.split(":", 1)[1].strip()
- elif t.startswith("创建日期:"):
- meta["created"] = t.split(":", 1)[1].strip()
- elif t.startswith("更新日期:"):
- meta["updated"] = t.split(":", 1)[1].strip()
- elif t.startswith("当前版本:"):
- meta["version"] = t.split(":", 1)[1].strip()
- elif t.startswith("版本:"):
- m = re.search(r"版本:\s*(\S+)", t)
- if m:
- meta["version"] = m.group(1)
- m = re.search(r"日期:\s*([^作者]+?)(?:\s+作者:|$)", t)
- if m and not meta["updated"]:
- meta["updated"] = m.group(1).strip()
- m = re.search(r"作者:\s*(.+)$", t)
- if m:
- meta["author"] = m.group(1).strip()
- if doc.tables:
- for row in doc.tables[0].rows:
- cells = [c.text.strip() for c in row.cells]
- if len(cells) >= 2 and cells[0] == "版本" and cells[1].startswith("V"):
- meta["version"] = meta["version"] or cells[1]
- if len(cells) >= 2 and cells[0] == "编制单位":
- meta["author"] = meta["author"] or cells[1]
- if not meta["updated"]:
- meta["updated"] = meta["created"] or TODAY
- if not meta["created"]:
- meta["created"] = meta["updated"]
- if not meta["version"]:
- meta["version"] = "V1.0"
- return meta
- def find_toc_paragraph(doc: Document) -> Paragraph | None:
- for p in doc.paragraphs:
- if p.text.strip() == "目录":
- return p
- return None
- def has_system_name(doc: Document) -> bool:
- for p in doc.paragraphs[:20]:
- if NEW_NAME in p.text or any(old in p.text for old in OLD_NAMES):
- return True
- return False
- def insert_system_name_line(doc: Document) -> bool:
- if has_system_name(doc):
- return False
- anchor = None
- for p in doc.paragraphs[:20]:
- if p.text.strip().startswith("文档作者:"):
- anchor = p
- break
- if anchor is None:
- return False
- el = OxmlElement("w:p")
- anchor._element.addprevious(el)
- para = Paragraph(el, anchor._parent)
- para.add_run(NEW_NAME)
- return True
- def add_para_before(ref: Paragraph, text: str = "", *, style: str | None = None, page_break: bool = False) -> Paragraph:
- el = OxmlElement("w:p")
- ref._element.addprevious(el)
- para = Paragraph(el, ref._parent)
- if page_break:
- para.add_run().add_break(WD_BREAK.PAGE)
- if text:
- run = para.add_run(text)
- if style == "Heading 1":
- run.bold = True
- if style:
- para.style = style
- return para
- def fill_version_table(table, rows: list[tuple[str, str, str, str]]) -> None:
- for ri, row_data in enumerate(rows):
- for ci, val in enumerate(row_data):
- cell = table.rows[ri].cells[ci]
- cell.text = ""
- run = cell.paragraphs[0].add_run(val)
- run.font.size = Pt(10.5)
- run.bold = ri == 0
- def version_rows(meta: dict[str, str]) -> list[tuple[str, str, str, str]]:
- header = ("版本", "日期", "修订人", "修订说明")
- current = (meta["version"], TODAY, meta["author"], CHANGE_NOTE)
- initial_ver = "V0.1"
- if meta["version"] in {"V1.0", "V2.0"}:
- initial_ver = "V1.0" if meta["version"] != "V2.0" else "V1.0"
- initial = (initial_ver, meta["created"], meta["author"], "初稿")
- if current[0] == initial[0] and current[1] == initial[1]:
- return [header, current]
- return [header, current, initial]
- def find_version_record_table(doc: Document):
- for i, p in enumerate(doc.paragraphs):
- if p.text.strip() != "版本记录":
- continue
- body = doc.element.body
- children = list(body)
- idx = children.index(p._element)
- for el in children[idx + 1 : idx + 4]:
- if el.tag.endswith("tbl"):
- for table in doc.tables:
- if table._tbl is el:
- return table
- return None
- def upsert_version_record(doc: Document, meta: dict[str, str]) -> str:
- rows = version_rows(meta)
- existing = find_version_record_table(doc)
- if existing is not None:
- current = rows[1]
- version, date, author, note = current
- for table_row in existing.rows[1:]:
- cells = [c.text.strip() for c in table_row.cells]
- if cells and cells[0] == version and note in "".join(cells):
- return "version-table-exists"
- new_row = existing.add_row()
- for ci, val in enumerate(current):
- cell = new_row.cells[ci]
- cell.text = ""
- run = cell.paragraphs[0].add_run(val)
- run.font.size = Pt(10.5)
- return "version-table-updated"
- toc = find_toc_paragraph(doc)
- if toc is None:
- return "no-toc"
- add_para_before(toc, page_break=True)
- add_para_before(toc, "版本记录", style="Heading 1")
- table = doc.add_table(rows=len(rows), cols=4)
- tbl_el = table._tbl
- doc.element.body.remove(tbl_el)
- toc._element.addprevious(tbl_el)
- fill_version_table(table, rows)
- return "version-table-added"
- def process_file(path: Path) -> dict[str, int | str]:
- doc = Document(path)
- meta = parse_metadata(doc)
- name_changes = replace_system_name(doc)
- inserted_name = insert_system_name_line(doc)
- version_action = upsert_version_record(doc, meta)
- doc.save(path)
- return {
- "name_changes": name_changes,
- "inserted_name_line": int(inserted_name),
- "version_action": version_action,
- }
- def main() -> None:
- for mod in ["S0", "S1", "S2", "S3", "S4"]:
- mod_dir = BASE / mod
- if not mod_dir.is_dir():
- print(f"{mod}: missing directory")
- continue
- for path in sorted(mod_dir.glob("*.docx")):
- result = process_file(path)
- print(
- f"{mod}/{path.name}: "
- f"name={result['name_changes']} "
- f"insert_line={result['inserted_name_line']} "
- f"{result['version_action']}"
- )
- if __name__ == "__main__":
- main()
|