| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- # coding:utf8
- import re
- from typing import Dict, Any
- from BiddingKG.dl.template_extract.abstract_template import AbstractTemplate, table2list
- import os
- import json
- class PurchaseTransactionTemplate(AbstractTemplate):
- """
- 采购成交信息提取模板(适配物料采购表格场景)
- 适配包含中标供应商、物料明细的表格提取
- """
- def __init__(self):
- super().__init__(
- template_id=os.path.abspath(__file__),
- priority=3
- )
- self.header = None # 物料采购表格表头
- self.data = None # 物料采购表格数据
- def check_call_timing(self, preprocessed_data: Dict[str, Any]) -> bool:
- """检查是否符合模板调用条件"""
- if "表格列表" not in preprocessed_data:
- return False
- tables = preprocessed_data["表格列表"]
- if len(tables) < 1:
- return False
- try:
- # 验证物料采购表格(唯一表格)
- purchase_table = tables[0]
- if len(purchase_table) < 2: # 至少包含表头+1行数据
- return False
- # 物料采购表头正则匹配(按实际位置泛化)
- purchase_header_patterns = [
- r'序号', # 位置0
- r'中标供应商|成交供应商|中标单位|成交单位', # 位置1
- r'物料编码|物资编码|材料编码|货品编码', # 位置2
- r'物料描述|物资描述|材料名称|货品名称|产品名称', # 位置3
- r'规格型号|规格|型号|技术参数', # 位置4
- r'计量单位|单位|数量单位', # 位置5
- r'采购数量|计划数量|需求数量', # 位置6
- r'中标数量|成交数量|供货数量', # 位置7
- r'成交单价|中标单价|单价|报价', # 位置8
- r'到货日期|交货日期|交付日期|到货时间', # 位置9
- r'交货地点|到货地点|交付地址|交货地址' # 位置10
- ]
- # 校验每个表头位置的匹配度
- for i, pattern in enumerate(purchase_header_patterns):
- if i >= len(purchase_table[0]): # 兼容表头长度不足的情况
- return False
- if not re.fullmatch(pattern, purchase_table[0][i], re.IGNORECASE):
- # print(f'物料采购表头未验证: 位置{i} 预期[{pattern}] 实际[{purchase_table[0][i]}]')
- return False
- # 赋值表格数据
- self.header = purchase_table[0]
- self.data = purchase_table[1:]
- return True
- except Exception as e:
- # print(f"表格验证错误: {e}")
- return False
- def extract(self, preprocessed_data: Dict[str, Any]) -> Dict[str, Any]:
- """提取表格中的中标信息、产品信息"""
- extract_result = {
- "项目编号": "",
- "项目名称": "",
- "招标信息": [],
- "中标信息": [],
- "候选人信息": [],
- "产品信息": []
- }
- # 提取中标信息(物料采购表格)
- for row in self.data:
- if len(row) < 11: # 确保行数据长度匹配表头
- continue
- # 按表头位置解析字段
- seq = row[0]
- supplier_name = row[1]
- material_code = row[2]
- product_name = row[3]
- spec = row[4]
- unit = row[5]
- purchase_quantity = row[6]
- bid_quantity = row[7]
- unit_price = row[8]
- arrival_date = row[9]
- delivery_address = row[10]
- # 中标信息必须包含中标人
- if not supplier_name:
- continue
- # 补充单价单位(元)
- price_unit = "元"
- if re.search(r'[万亿美欧日]?元', self.header[8], re.IGNORECASE):
- price_unit = re.search(r'[万亿美欧日]?元', self.header[8], re.IGNORECASE).group(0)
- if unit_price and not re.search('[万亿美欧日]?元', unit_price):
- unit_price += price_unit
- # 组装中标信息(过滤空值)
- bid_info = {
- "标的": product_name,
- "中标人": supplier_name,
- "中标价": unit_price,
- # "地址": delivery_address,
- "服务时间": arrival_date # 到货日期作为服务/交付时间
- }
- # 过滤空值字段
- bid_info = {k: v for k, v in bid_info.items() if v}
- extract_result["中标信息"].append(bid_info)
- # 提取产品信息(物料采购表格)
- product_list = []
- for row in self.data:
- if len(row) < 11:
- continue
- # 按表头位置解析字段
- seq = row[0]
- supplier_name = row[1]
- material_code = row[2]
- product_name = row[3]
- spec = row[4]
- unit = row[5]
- purchase_quantity = row[6]
- bid_quantity = row[7]
- unit_price = row[8]
- arrival_date = row[9]
- delivery_address = row[10]
- # 补充单价单位
- price_unit = "元"
- if re.search(r'[万亿美欧日]?元', self.header[8], re.IGNORECASE):
- price_unit = re.search(r'[万亿美欧日]?元', self.header[8], re.IGNORECASE).group(0)
- if unit_price and not re.search('[万亿美欧日]?元', unit_price):
- unit_price += price_unit
- # 组装产品信息
- product_info = {
- "产品": product_name,
- "规格": spec,
- "数量": bid_quantity, # 中标数量作为实际供货数量
- "单位": unit,
- "单价": unit_price
- }
- # 产品信息必须包含产品及至少一个其他要素
- other_fields = [product_info["规格"], product_info["数量"],
- product_info["单位"], product_info["单价"]]
- if product_info["产品"] and any(other_fields):
- # 过滤空值字段
- product_info = {k: v for k, v in product_info.items() if v}
- product_list.append(product_info)
- extract_result["产品信息"] = product_list
- # 招标信息校验:无招标人/预算则置空
- extract_result["招标信息"] = []
- # 候选人信息:无候选人及排名则置空
- extract_result["候选人信息"] = []
- # 整体过滤空值字段(顶层)
- extract_result = {k: v for k, v in extract_result.items() if v or v == []}
- return extract_result
- # 使用示例
- if __name__ == "__main__":
- # 读取并解析JSON文件
- with open("data.json", "r", encoding="utf8") as f:
- preprocessed_data = json.load(f)
- # 初始化模板并提取信息
- template = PurchaseTransactionTemplate()
- if template.check_call_timing(preprocessed_data):
- result = template.extract(preprocessed_data)
- print("提取结果:", json.dumps(result, ensure_ascii=False, indent=4))
- else:
- print("当前数据不满足模板调用条件")
|