audit_template_sources.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. """审计模板:每个表头字段是否都在「填写说明 → 字段依据」中有来源标注。"""
  2. from __future__ import annotations
  3. import re
  4. import sys
  5. from pathlib import Path
  6. import openpyxl
  7. sys.stdout.reconfigure(encoding="utf-8")
  8. TPL = Path(r"E:\CODE\knowledge_agent\data\templates")
  9. def norm(h: str) -> str:
  10. s = h.replace("*", "")
  11. for x in ("(选填)", "(必填)", "(工号)", "(非续签留空)"):
  12. s = s.replace(x, "")
  13. return re.sub(r"\s+", "", s)
  14. def main() -> None:
  15. for f in sorted(TPL.glob("*.xlsx")):
  16. if f.name.startswith("~$"):
  17. continue # Excel 临时锁文件
  18. wb = openpyxl.load_workbook(f, read_only=True, data_only=True)
  19. data = wb[wb.sheetnames[0]]
  20. headers = [c.value for c in data[1] if c.value]
  21. note = wb["填写说明"]
  22. start = None
  23. for r in range(1, 200):
  24. if note.cell(row=r, column=1).value == "【字段依据】":
  25. start = r
  26. break
  27. entries: list[str] = []
  28. if start:
  29. r = start + 2
  30. while note.cell(row=r, column=1).value:
  31. entries.append(str(note.cell(row=r, column=1).value))
  32. r += 1
  33. missing = []
  34. for h in headers:
  35. hn = norm(h)
  36. if re.fullmatch(r"\d+日", hn):
  37. continue # 1日..31日 由「1-31日」条目覆盖
  38. covered = False
  39. for e in entries:
  40. toks = [t for t in re.split(r"[/、,,]", norm(e)) if t]
  41. for tk in toks:
  42. if hn == tk or (len(tk) >= 2 and (tk in hn or hn in tk)):
  43. covered = True
  44. break
  45. if covered:
  46. break
  47. if not covered:
  48. missing.append(h)
  49. print(f"=== {f.name}: {len(headers)} 列 / 依据 {len(entries)} 条 / 未覆盖 {len(missing)}")
  50. for m in missing:
  51. print(f" ✗ {m}")
  52. wb.close()
  53. if __name__ == "__main__":
  54. main()