template3.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # coding:utf8
  2. import re
  3. from typing import Dict, Any
  4. from BiddingKG.dl.template_extract.abstract_template import AbstractTemplate, table2list
  5. import os
  6. import json
  7. class CustomTemplate(AbstractTemplate):
  8. """
  9. 招标信息提取模板(适配data.json中的表格结构)
  10. 适配包含项目信息、招标信息、中标信息的表格提取(仅从第一个表格提取)
  11. """
  12. def __init__(self):
  13. super().__init__(
  14. template_id=os.path.abspath(__file__),
  15. priority=3
  16. )
  17. self.main_table = None # 第一个表格,存储所有需提取信息
  18. def check_call_timing(self, preprocessed_data: Dict[str, Any]) -> bool:
  19. """检查是否符合模板调用条件"""
  20. if "表格列表" not in preprocessed_data:
  21. return False
  22. tables = preprocessed_data["表格列表"]
  23. if len(tables) < 1:
  24. print("表格数量异常,至少需要1个表格")
  25. return False
  26. try:
  27. self.main_table = tables[0] # 只处理第一个表格
  28. # 验证表格关键表头(按data.json实际位置泛化匹配)
  29. header_patterns = [
  30. (0, 0, r'项目编号'), # 第1行第1列
  31. (0, 2, r'项目名称|工程名称'), # 第1行第3列
  32. (1, 0, r'经办人单位|采购单位|招标人单位'), # 第2行第1列
  33. (1, 2, r'经办人|联系人'), # 第2行第3列
  34. (2, 0, r'预算金额|招标金额'), # 第3行第1列
  35. (2, 2, r'成交金额|中标金额'), # 第3行第3列
  36. (3, 0, r'成交供应商|中标人'), # 第4行第1列
  37. (5, 0, r'中标人地址|联系地址'), # 第6行第1列
  38. (5, 2, r'采购单位|招标人|采购人'), # 第6行第3列
  39. (8, 0, r'供应商联系手机|中标人电话|联系方式'), # 第9行第1列
  40. ]
  41. # 验证表头
  42. for row, col, pattern in header_patterns:
  43. if row >= len(self.main_table) or col >= len(self.main_table[row]):
  44. return False
  45. header_text = self.main_table[row][col].strip()
  46. if not re.search(pattern, header_text, re.IGNORECASE):
  47. print(f"表头不匹配: {header_text} 不符合 {pattern}")
  48. return False
  49. return True
  50. except Exception as e:
  51. print(f"表格验证错误: {e}")
  52. return False
  53. def extract(self, preprocessed_data: Dict[str, Any]) -> Dict[str, Any]:
  54. """提取表格中的各类信息"""
  55. result = {}
  56. # 提取项目基本信息
  57. result["项目编号"] = self._get_value(self.main_table, 0, 1) # 第1行第2列
  58. result["项目名称"] = self._get_value(self.main_table, 0, 3) # 第1行第4列
  59. # 提取候选人信息(无候选人信息)
  60. candidate_info_list = self._extract_candidate_info()
  61. if candidate_info_list:
  62. result["候选人信息"] = candidate_info_list
  63. else:
  64. # 无候选人信息时提取中标信息
  65. winner_info = self._extract_winner_info()
  66. if winner_info:
  67. result["中标信息"] = [winner_info]
  68. # 提取招标信息
  69. tender_info = self._extract_tender_info()
  70. if tender_info:
  71. result["招标信息"] = [tender_info]
  72. # 过滤空值字段
  73. return {k: v for k, v in result.items() if v or v == []}
  74. def _get_value(self, table: list, row: int, col: int) -> str:
  75. """获取表格指定位置的值"""
  76. if row < len(table) and col < len(table[row]):
  77. return table[row][col].strip()
  78. return ""
  79. def _format_amount(self, value: str, header_row: int, header_col: int) -> str:
  80. """格式化金额,补充单位"""
  81. if not value:
  82. return ""
  83. # 检查值中是否已包含单位
  84. if re.search(r'[万亿美欧日]?元|¥|人民币|美元|欧元', value):
  85. return value
  86. # 从表头获取单位补充
  87. header_text = self._get_value(self.main_table, header_row, header_col)
  88. if re.search(r'[万亿美欧日]?元|¥|人民币|美元|欧元', header_text):
  89. unit = re.search(r'[万亿美欧日]?元|¥|人民币|美元|欧元', header_text).group(0)
  90. return f"{value}{unit}"
  91. return value
  92. def _extract_tender_info(self) -> Dict[str, Any]:
  93. """提取招标信息(招标人相关信息)"""
  94. tender = {
  95. "标的": self._get_value(self.main_table, 0, 3), # 项目名称作为标的
  96. "招标人": self._get_value(self.main_table, 5, 3), # 采购单位(第6行第3列)
  97. "预算": self._format_amount(self._get_value(self.main_table, 2, 1), 2, 0), # 预算金额
  98. "联系人": self._get_value(self.main_table, 1, 3), # 经办人(招标人联系人)
  99. }
  100. # 必须包含招标人或预算
  101. if not tender.get("招标人") and not tender.get("预算"):
  102. return {}
  103. # 过滤空值
  104. return {k: v for k, v in tender.items() if v}
  105. def _extract_winner_info(self) -> Dict[str, Any]:
  106. """提取中标信息(中标人相关信息)"""
  107. winner = {
  108. "标的": self._get_value(self.main_table, 0, 3), # 项目名称作为标的
  109. "中标人": self._get_value(self.main_table, 3, 1), # 成交供应商(中标人)
  110. "中标价": self._format_amount(self._get_value(self.main_table, 2, 3), 2, 2), # 成交金额
  111. "电话": self._get_value(self.main_table, 8, 1), # 供应商联系手机
  112. "地址": self._get_value(self.main_table, 5, 1), # 联系地址(中标人地址)
  113. }
  114. # 必须包含中标人
  115. if not winner.get("中标人"):
  116. return {}
  117. # 过滤空值
  118. return {k: v for k, v in winner.items() if v}
  119. def _extract_candidate_info(self) -> list:
  120. """提取候选人信息(表格中无相关信息)"""
  121. return [] # 表格中无候选人及排名相关信息
  122. # 使用示例
  123. if __name__ == "__main__":
  124. with open("data.json", "r", encoding="utf8") as f:
  125. preprocessed_data = json.load(f)
  126. template = CustomTemplate()
  127. if template.check_call_timing(preprocessed_data):
  128. result = template.extract(preprocessed_data)
  129. print("提取结果:", json.dumps(result, ensure_ascii=False, indent=2))
  130. else:
  131. print("当前数据不满足模板调用条件")