zbparse/flask_app/routes/偏离表main.py

424 lines
21 KiB
Python
Raw Normal View History

2024-11-06 12:20:24 +08:00
import json
import logging
2024-11-17 16:29:02 +08:00
import re
import string
2024-11-22 16:06:57 +08:00
import time
2024-11-06 12:20:24 +08:00
2024-11-22 16:06:57 +08:00
from flask_app.general.doubao import doubao_model
2024-11-06 12:20:24 +08:00
from flask_app.general.format_change import pdf2docx, docx2pdf
2024-11-22 16:06:57 +08:00
from flask_app.general.json_utils import clean_json_string
2024-10-27 12:08:54 +08:00
from flask_app.general.通义千问long import upload_file
2024-11-22 16:06:57 +08:00
from flask_app.货物标.截取pdf货物标版 import truncate_pdf_specific_goods
from flask_app.货物标.提取采购需求main import fetch_procurement_reqs
2024-11-18 16:12:11 +08:00
from flask_app.货物标.技术参数要求提取后处理函数 import extract_matching_keys
2024-11-22 16:06:57 +08:00
from flask_app.货物标.资格审查main import combine_qualification_review
import concurrent.futures
2024-10-27 12:08:54 +08:00
2024-11-06 12:20:24 +08:00
def get_global_logger(unique_id):
if unique_id is None:
return logging.getLogger() # 获取默认的日志器
logger = logging.getLogger(unique_id)
return logger
logger = None
2024-11-22 16:06:57 +08:00
def get_nested(dic, keys, default=None):
for key in keys:
if isinstance(dic, dict):
dic = dic.get(key, default)
else:
return default
return dic
def prepare_for_zige_info(zige_review):
try:
zige_info = ""
fuhe_info = ""
zigefuhe_info = ""
# 检查是否存在"资格性和符合性审查"
if "资格性和符合性审查" in zige_review:
# 情况3只有"申请人资格要求"和"资格性和符合性审查"
if zige_review.get("申请人资格要求", {}) or zige_review.get("资格性和符合性审查", {}):
zigefuhe_info = json.dumps({
"申请人资格要求": zige_review.get("申请人资格要求", {}),
"符合性审查": zige_review.get("资格性和符合性审查", {})
}, ensure_ascii=False, indent=4)
2024-11-22 16:06:57 +08:00
else:
# 情况1和2存在分开的资格审查和符合性审查
if zige_review.get("申请人资格要求", {}) or zige_review.get("资格性审查", {}):
zige_info = json.dumps({
"申请人资格要求": zige_review.get("申请人资格要求", {}),
"资格性审查": zige_review.get("资格性审查", {})
}, ensure_ascii=False, indent=4)
2024-11-22 16:06:57 +08:00
# 检查符合性审查的键值是否为空
2024-11-22 16:06:57 +08:00
fuhe_key = "符合性审查" if "符合性审查" in zige_review else "符合性审查(以下情况不得出现)"
if zige_review.get(fuhe_key, {}):
fuhe_info = json.dumps({
"符合性审查": zige_review.get(fuhe_key, {})
}, ensure_ascii=False, indent=4)
return zige_info, fuhe_info, zigefuhe_info
2024-11-22 16:06:57 +08:00
except KeyError as e:
print(f"缺少关键字: {e}")
# 异常时直接返回空字符串
return "", "", ""
2024-11-22 16:06:57 +08:00
def extract_zige_deviation_table(zige_info, fuhe_info, zigefuhe_info):
prompt_template1 = """
任务给出一份文本根据文本提取资格性检查的具体评审标准
输出要求
1.以json格式返回结果不要输出其他内容
2.键名为"资格性检查"键值为字符串列表每个字符串为一条评审标准评审标准不分先后不要有序号标注
要求与指南
1. 评审标准是具体的内容不要返回诸如'本项目的特定资格要求:'这种标题性质且不能体现具体评审标准的内容
2. 若文本中存在相同或相似的表述仅需取其中一个作为键值中的一条即可
文本内容{full_text}
"""
prompt_template2 = """
任务给出一份文本根据文本提取符合性检查的具体评审标准
输出要求
1.以json格式返回结果不要输出其他内容
2.键名为"符合性检查"键值为字符串列表每个字符串为一条评审标准评审标准不分先后不要有序号标注
3.仔细检查你所选取的标准若发现这些标准实际上是在描述不允许出现的符合性审查情况则将外键替换为'符合性检查(以下情况不得出现)'并将这些标准写入其中
要求与指南
1. 评审标准应该是具体的内容不要返回诸如'本项目的特定符合性要求:'这种标题性质且不能体现具体评审标准的内容
2. 若文本中存在相同或相似的表述仅需取其中一个作为键值中的一条即可
输出示例1
{{
"符合性检查": [
"因素1",
"因素2",
...
]
}}
输出示例2
{{
"符合性检查(以下情况不得出现)": [
"因素1",
"因素2",
...
]
}}
文本内容{full_text}
"""
prompt_template3 = """
任务给出一份文本根据文本提取资格性检查和符合性检查的具体评审标准
输出要求
1.以json格式返回结果不要输出其他内容
2.键名为"资格性和符合性检查"键值为字符串列表每个字符串为一条评审标准评审标准不分先后不要有序号标注
要求与指南
1. 评审标准应该是具体的内容不要返回诸如'本项目的特定符合性要求:'这种标题性质且不能体现具体评审标准的内容
2. 若文本中存在相同或相似的表述仅需取其中一个作为键值中的一条即可
文本内容{full_text}
"""
def get_model_response(query):
return doubao_model(query)
result = {"资格审查": {}}
if zigefuhe_info:
# 如果zigefuhe_info非空使用prompt_template3
user_query3 = prompt_template3.format(full_text=zigefuhe_info)
model_res3 = get_model_response(user_query3)
zigefuhe_deviation = clean_json_string(model_res3)
result["资格审查"] = zigefuhe_deviation
else:
zige_deviation = {}
fuhe_deviation = {}
# 提交 zige_info 和 fuhe_info 的模型调用
2024-11-22 16:06:57 +08:00
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = {}
if zige_info != "":
user_query1 = prompt_template1.format(full_text=zige_info)
futures["zige"] = executor.submit(get_model_response, user_query1)
if fuhe_info != "":
user_query2 = prompt_template2.format(full_text=fuhe_info)
futures["fuhe"] = executor.submit(get_model_response, user_query2)
# 获取结果
for key, future in futures.items():
try:
model_res = future.result()
if key == "zige":
zige_deviation = clean_json_string(model_res)
elif key == "fuhe":
fuhe_deviation = clean_json_string(model_res)
except Exception as e:
print(f"Error processing {key}: {e}")
# 合并结果
2024-11-22 16:06:57 +08:00
result["资格审查"] = {
"资格性检查": zige_deviation.get("资格性检查", zige_deviation),
"符合性检查": fuhe_deviation.get("符合性检查", fuhe_deviation),
2024-11-22 16:06:57 +08:00
}
return result
def extract_business_deviation(procurement):
new_data = {}
counter = 1
if "服务要求" in procurement:
new_data[f"招标要求{counter}"] = procurement["服务要求"]
counter += 1
# Extract "商务要求"
if "商务要求" in procurement:
new_data[f"招标要求{counter}"] = procurement["商务要求"]
counter += 1
# Extract "其他要求"
if "其他要求" in procurement:
new_data[f"招标要求{counter}"] = procurement["其他要求"]
counter += 1
business_requirements_string = json.dumps(new_data, ensure_ascii=False, indent=4)
# print(business_requirements_string)
2024-12-03 11:50:15 +08:00
prompt_template1 = """请帮我从以下文本中摘取商务要求部分,并将信息重新组织,键名为'商务要求',键值为字符串列表,其中每个字符串为一条商务要求,保留三角▲、五角星★(若有),但是去除开头的序号(若有)。
2024-11-29 15:02:07 +08:00
#角色
你是一个专业的招投标业务专家擅长从招标文件中总结商务要求的部分并逐条列出作为编写商务要求偏离表的前置准备
#要求与指南:
1. 每条内容需要有实际的含义要求不能光有标题性质的表述如'售后服务期限(质保期)及要求'
2024-12-03 11:50:15 +08:00
2. 你的回答内容需从所给文本中整理尽量不改变原文的表达请勿擅自添加三角五角星除非以下要求与指南3.的特殊情况
3. 若输入文本中存在嵌套键值对格式且键值本身语义完整且符合'商务要求'可直接将其添加至'商务要求'的键值中若键值本身语义表达不完整可将键值对用冒号''拼接之后作为一条商务要求
4. 对于以三角或五角星开头的字符串
a. 如果该字符串仅为标题性质的表述且不具备实际商务要求的含义请根据语义关联性将其开头的三角或五角星添加到紧随其后的若干可为一内容之后形成完整的商务要求并确保整个内容连贯
默认在该字符串后面的一个字符串开头添加三角或五角星若有明确的序号或者语义表示了其后若干字符串之间的相关性那么可在这些字符串开头都添加三角或五角星作为若干商务要求
b. 如果该字符串已经包含实际的商务要求那么该内容作为一条完整的商务要求保留开头的三角或五角星
- 示例输入
```
"★ 提供高质量的售后服务,服务期限不少于两年。"
```
- 示例输出
```
"★ 提供高质量的售后服务,服务期限不少于两年。"
```
c. 无论哪种情况都需确保不遗漏任何以三角或五角星开头的重要信息
5. 若无商务要求键值为空列表[]
2024-11-29 15:02:07 +08:00
### 示例输入如下:
{{
2024-12-03 11:50:15 +08:00
"招标要求1": ["▲(1)整个平台运行运维服务,须安排人员驻场对平台进行运行维护,采用 4人轮流值班依照 7×12小时对可视化督察巡控平台进行操作确保平台稳定运行。","▲ (一) 投标人","1.投标人需要获得 ISO9001 质量管理体系认证 、ISO 14001 环境管理体系认证及 OHSAS18001 职业健康安全管理体系认证。","2.投标人具备网络运营商资格。"]
2024-11-29 15:02:07 +08:00
"招标要求2": {{
"合同履行期限": ["★交货期(工期):合同签订之日起 15个日历天内完成并通过项目验收。"],
"交货地点": ["采购人指定地点"],
"报价方式": ["1本项目报价须为固定总价包含但不限于采购、实施、调试、试运行、验收、运维等所有完成本项目相关的一切费用。","2)因投标人自身原因造成漏报、少报皆由其自行承担责任,采购人不再补偿。"],
"其他要求": ["无。"]
}}
}}
### 对应的参考输出如下:
{{
"商务要求":[
2024-12-03 11:50:15 +08:00
"▲整个平台运行运维服务,须安排人员驻场对平台进行运行维护,采用 4人轮流值班依照 7×12小时对可视化督察巡控平台进行操作确保平台稳定运行。",
"▲投标人 获得 ISO9001 质量管理体系认证 、ISO 14001 环境管理体系认证及 OHSAS18001 职业健康安全管理体系认证。",
"▲投标人具备网络运营商资格"
2024-11-29 15:02:07 +08:00
"★交货期(工期):合同签订之日起 15个日历天内完成并通过项目验收。",
"交货地点:采购人指定地点",
"本项目报价须为固定总价,包含但不限于:采购、实施、调试、试运行、验收、运维等所有完成本项目相关的一切费用。",
"因投标人自身原因造成漏报、少报皆由其自行承担责任,采购人不再补偿。"
]
}}
文本内容{full_text}
2024-12-03 11:50:15 +08:00
"""
2024-11-22 16:06:57 +08:00
user_query1 = prompt_template1.format(full_text=business_requirements_string)
model_res1 = doubao_model(user_query1)
# print(model_res)
business_req_deviation = clean_json_string(model_res1)
prompt_template2 = """以下文本是项目采购需求的商务要求部分,请你帮我从键值列表中各字符串中提取带星★或带三角▲的要求项,你的返回格式同输入文本格式,外键名为'商务要求带星',键值为字符串列表,其中每个字符串为带星★或带三角▲的要求项。
要求与指南
1. 每个星或三角要求占据一个字符串
2. 若没有带星或带三角的要求项键值为空列表[]
特殊情况处理
对于输入类似于'技术要求中带★条款项不满足的视为无效投标'这种描述带星或带三角的响应情况的它本身不是带星或带三角的要求因此不需要添加进字符串列表中仅需把本身是带或带三角的要求添加进来
### 示例输入如下:
{{
"商务要求": [
"考虑设备兼容性、项目实施、交付及售后服务",
"★交货期(工期):合同签订之日起 15个日历天内完成并通过项目验收。",
"▲本项目报价须为固定总价,包含但不限于:采购、实施、调试、试运行、验收、运维等所有完成本项目相关的一切费用。"
]
}}
### 对应的输出如下:
{{
"商务要求带星": [
"★交货期(工期):合同签订之日起 15个日历天内完成并通过项目验收。",
"▲本项目报价须为固定总价,包含但不限于:采购、实施、调试、试运行、验收、运维等所有完成本项目相关的一切费用。"
]
}}
文本内容{full_text}
"""
user_query2 = prompt_template2.format(full_text=model_res1)
model_res2 = doubao_model(user_query2)
business_star_req_deviation = clean_json_string(model_res2)
return business_req_deviation, business_star_req_deviation
def get_tech_star_deviation(tech_string):
prompt_template = """以下输入文本包含采购货物的技术参数要求或采购要求。请从每个键对应的字符串列表中提取带有星★或三角▲的要求项。返回格式应与输入文本格式相同为JSON格式每个键名保持不变键值为包含对应货物、系统或功能模块的带星或带三角要求项的字符串列表。
要求与指南
1. 如果某个货物系统或功能模块下没有带星或带三角的要求项则不返回该键值对
2. 每个带星或带三角的要求项应作为单独的字符串
3. 如果所有设备系统或功能模块中都没有带星或带三角的要求项则直接返回空字典 {{}}
### 示例输入1如下
{{
"控制键盘": [
"普通要求xx",
"★带星要求xx"
]
"摄像机"[
"★带星要求xx",
"▲带三角要求xx",
"普通要求xx"
]
"交换机":[
"普通要求xx",
"普通要求xxx"
]
}}
### 对应的输出如下:
{{
"摄像机控制键盘": [
"★带星要求xx"
]
"摄像机"[
"★带星要求xx",
"▲带三角要求xx"
]
}}
### 示例输入2如下
{{
"控制键盘": [
"普通要求xx",
"普通要求xxx"
]
"摄像机"[
"普通要求xx"
]
}}
### 对应的输出如下:
{{}}
输入文本内容{full_text}
"""
user_query = prompt_template.format(full_text=tech_string)
model_res = doubao_model(user_query)
# print(model_res)
tech_star_deviation = clean_json_string(model_res)
filtered_dict = {key: value for key, value in tech_star_deviation.items() if value} #过滤键值为空列表,二重保险。
return filtered_dict
def process_functions_in_parallel(tech_deviation_info, procurement_reqs, zige_info, fuhe_info, zigefuhe_info):
# 准备输入参数
# 定义任务和对应参数
tasks = [
("tech_star_deviation", get_tech_star_deviation, (tech_deviation_info,)),
("business_deviation_and_star", extract_business_deviation, (procurement_reqs,)),
("zigefuhe_deviation", extract_zige_deviation_table, (zige_info, fuhe_info, zigefuhe_info)),
]
results = {}
# 执行多线程任务
with concurrent.futures.ThreadPoolExecutor() as executor:
future_to_task = {executor.submit(func, *args): name for name, func, args in tasks}
time.sleep(0.5)
for future in concurrent.futures.as_completed(future_to_task):
name = future_to_task[future]
try:
result = future.result()
# 处理返回值(如果任务返回多个值,需要解包)
if name == "business_deviation_and_star":
results["business_deviation"], results["business_star_deviation"] = result
else:
results[name] = result
except Exception as e:
print(f"Task {name} failed with exception: {e}")
results[name] = None
# 返回结果
return (
results.get("tech_star_deviation"),
results.get("business_deviation"),
results.get("business_star_deviation"),
results.get("zigefuhe_deviation")
)
def get_tech_and_business_deviation(file_path,file_type,unique_id,output_folder):
2024-11-06 12:20:24 +08:00
global logger
logger = get_global_logger(unique_id)
if file_type == 1: # docx
2024-11-22 16:06:57 +08:00
pdf_path = docx2pdf(file_path) # 将docx转换为pdf以供后续处理
2024-11-06 12:20:24 +08:00
elif file_type == 2: # pdf
pdf_path = file_path
elif file_type == 3: # doc
pdf_path = docx2pdf(file_path)
else:
logger.error("Unsupported file type provided. Preprocessing halted.")
return None
2024-11-22 16:06:57 +08:00
selections=[1,3,5]
files=truncate_pdf_specific_goods(pdf_path,output_folder,selections,unique_id)
notice_path=files[0]
qualification_file=files[1]
procurement_file=files[2]
if not procurement_file:
procurement_file=pdf_path #直接传整份文件
tech_deviation={}
with concurrent.futures.ThreadPoolExecutor() as executor:
# 提交任务到线程池
future_procurement = executor.submit(fetch_procurement_reqs, procurement_file, pdf_path)
time.sleep(1)
future_review = executor.submit(combine_qualification_review, pdf_path, qualification_file, notice_path)
try:
# 获取函数执行结果
procurement_reqs = future_procurement.result()
except Exception as e:
logger.error(f'fetch_procurement_reqs 出现异常: {e}')
procurement_reqs = {} # 或根据需要进行处理
try:
review_standards_res = future_review.result()
except Exception as e:
logger.error(f'combine_qualification_review 出现异常: {e}')
review_standards_res = {} # 或根据需要进行处理
tech_requirements = get_nested(procurement_reqs, ["采购需求"], {})
if tech_requirements:
good_list = tech_requirements.pop('货物列表', []) # 如果 '货物列表' 不存在,返回 []
2024-11-06 12:20:24 +08:00
logger.info("Collected good_list from the processing function: %s", good_list)
2024-11-22 16:06:57 +08:00
tech_deviation = extract_matching_keys(tech_requirements, good_list)
tech_deviation_info = json.dumps(tech_deviation, ensure_ascii=False, indent=4)
2024-10-27 12:08:54 +08:00
else:
2024-11-22 16:06:57 +08:00
tech_deviation_info=""
zige_info, fuhe_info, zigefuhe_info = prepare_for_zige_info(review_standards_res.get("资格审查", {}))
tech_star_deviation, business_deviation, business_star_deviation, zigefuhe_deviation = process_functions_in_parallel(
tech_deviation_info=tech_deviation_info,
procurement_reqs=procurement_reqs,
zige_info=zige_info,
fuhe_info=fuhe_info,
zigefuhe_info=zigefuhe_info
)
return tech_deviation,tech_star_deviation,business_deviation,business_star_deviation,zigefuhe_deviation
2024-10-27 12:08:54 +08:00
if __name__ == "__main__":
2024-11-26 13:48:25 +08:00
file_path=r"C:\Users\Administrator\Desktop\fsdownload\5950ad84-30c8-4643-b6de-b13ef5be7a5c\ztbfile.pdf"
2024-11-06 12:20:24 +08:00
file_type=2
2024-11-26 13:48:25 +08:00
output_folder = r"C:\Users\Administrator\Desktop\fsdownload\5950ad84-30c8-4643-b6de-b13ef5be7a5c\tmp"
2024-11-22 16:06:57 +08:00
tech_deviation,tech_star_deviation,business_deviation,business_star_deviation,zigefuhe_deviation=get_tech_and_business_deviation(file_path,file_type,"123",output_folder)
2024-11-26 15:06:57 +08:00
print("技术偏离表")
2024-11-22 16:06:57 +08:00
print(json.dumps(tech_deviation,ensure_ascii=False,indent=4))
2024-11-26 15:06:57 +08:00
print("技术带星")
2024-11-22 16:06:57 +08:00
print(json.dumps(tech_star_deviation,ensure_ascii=False,indent=4))
2024-11-26 15:06:57 +08:00
print("商务偏离表")
2024-11-22 16:06:57 +08:00
print(json.dumps(business_deviation, ensure_ascii=False, indent=4))
2024-11-26 15:06:57 +08:00
print("商务带星")
2024-11-22 16:06:57 +08:00
print(json.dumps(business_star_deviation, ensure_ascii=False, indent=4))
2024-11-26 15:06:57 +08:00
print("资格审查")
2024-11-22 16:06:57 +08:00
print(json.dumps(zigefuhe_deviation, ensure_ascii=False, indent=4))