_bid_check_final.py 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. # -*- coding: utf-8 -*-
  2. """合并成册后的自检:目录条目、页码连续性、并入完整性、占位项清单。"""
  3. import io
  4. import os
  5. import re
  6. import sys
  7. import fitz
  8. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
  9. HERE = os.path.dirname(os.path.abspath(__file__))
  10. PDF = os.path.join(HERE, "_bid_preview",
  11. "响应文件(完整版)-2026年河南产互联制造业数据智能运营平台研发项目.pdf")
  12. d = fitz.open(PDF)
  13. pages = [d[i].get_text() for i in range(d.page_count)]
  14. print(f"总页数 {d.page_count}")
  15. # ---------------- 目录:条目、层级、页码
  16. toc_pages = [i for i, t in enumerate(pages[:8]) if "目 录" in t or "目 录" in t]
  17. toc_txt = "".join(pages[i] for i in range(toc_pages[0], toc_pages[0] + 4)) \
  18. if toc_pages else ""
  19. ENTRY = re.compile(r"^(.*?)\s\.{3,}\s*(\d+)\s*$", re.M)
  20. entries = ENTRY.findall(toc_txt)
  21. print(f"目录页 P{toc_pages[0] + 1} 起,抓到条目 {len(entries)} 条")
  22. bad = [e for e in entries if re.search(r"(四、\s*四、|(三)\s*(三))", e[0])]
  23. print(" 重复编号:", bad or "无")
  24. nums = [int(n) for _, n in entries]
  25. print(" 页码是否单调不减:", all(b >= a for a, b in zip(nums, nums[1:])))
  26. print(" 目录首条 / 末条:", entries[0][0].strip()[:30] if entries else "-",
  27. "/", entries[-1][0].strip()[:30] if entries else "-")
  28. # ---------------- 页脚页码连续性:每页末尾的独立数字
  29. foot = []
  30. for i, t in enumerate(pages):
  31. m = re.match(r"\s*(\d+)\s", t) # 页码在页面文本流最前(页脚被前置)
  32. foot.append(int(m.group(1)) if m else None)
  33. miss = [i + 1 for i, v in enumerate(foot) if v is None]
  34. jump = [(i + 1, foot[i], foot[i + 1]) for i in range(len(foot) - 1)
  35. if foot[i] is not None and foot[i + 1] is not None
  36. and foot[i + 1] != foot[i] + 1]
  37. print(f"\n页脚页码:缺失 {len(miss)} 页 {miss[:8]} 断号 {len(jump)} 处 {jump[:6]}")
  38. # ---------------- 并入完整性:各部分标志性文字
  39. MARKS = {
  40. "增值税专票承诺书": "关于能够提供增值税专用发票的承诺书",
  41. "不组联合体承诺书": "关于不组成联合体及中选后不转包的承诺书",
  42. "资格及信用承诺书": "关于资格条件及信用状况的承诺书",
  43. "软著一览表": "计算机软件著作权登记证书一览表",
  44. "项目团队表": "项目团队岗位职责与人员配备表",
  45. "4 项目需求理解": "4、项目需求理解及综合分析",
  46. "5 系统功能展示": "5、系统功能展示",
  47. "6 系统建设方案": "6、系统建设方案",
  48. "7 智能诊断": "7、智能诊断与分析方案",
  49. "8 UAT 测试": "8、UAT 测试与质量保障",
  50. "9 质量与工期": "9、质量与工期保障",
  51. "10 应急支撑": "10、应急支撑方案",
  52. "11 售后服务": "11、售后服务方案",
  53. "12 项目培训": "12、项目培训方案",
  54. }
  55. print("\n并入完整性:")
  56. for label, key in MARKS.items():
  57. hit = next((i + 1 for i, t in enumerate(pages)
  58. if key.replace(" ", "") in t.replace(" ", "").replace("\n", "")),
  59. None)
  60. print(f" {'○' if hit else '×'} {label:<18} {('P' + str(hit)) if hit else '未找到'}")
  61. # ---------------- 残留占位与标记
  62. print("\n残留检查:")
  63. for tok, name in (("@@MERGE", "合并标记"), ("【待填", "待填占位"),
  64. ("【待补", "待补占位")):
  65. hits = [i + 1 for i, t in enumerate(pages) if tok in t]
  66. print(f" {name}:{len(hits)} 页 {hits[:12]}")