10.18小解析

This commit is contained in:
zy123 2024-10-19 12:53:25 +08:00
parent 861a54a378
commit e591850da1
8 changed files with 207 additions and 286 deletions

View File

@ -38,7 +38,8 @@ def little_parse_goods(output_folder, file_path):
dict: 包含 '基础信息' 的字典 dict: 包含 '基础信息' 的字典
""" """
# 截取特定的货物 PDF 文件 # 截取特定的货物 PDF 文件
files = truncate_pdf_specific_goods(file_path, output_folder) selections = [4, 5] # 仅处理 selection 4 和 5
files = truncate_pdf_specific_goods(file_path, output_folder,selections)
if not files: if not files:
raise ValueError("未找到截取后的文件。") raise ValueError("未找到截取后的文件。")
# 假设最后一个文件是需要处理的基础信息文件 # 假设最后一个文件是需要处理的基础信息文件
@ -71,7 +72,8 @@ def little_parse_engineering(output_folder, file_path):
dict: 包含 '基础信息' 的字典 dict: 包含 '基础信息' 的字典
""" """
# 截取特定的工程 PDF 文件 # 截取特定的工程 PDF 文件
files = truncate_pdf_specific_engineering(file_path, output_folder) selections = [5, 1,3]
files = truncate_pdf_specific_engineering(file_path, output_folder,selections)
if not files: if not files:
raise ValueError("未找到截取后的文件。") raise ValueError("未找到截取后的文件。")
# 假设最后一个文件是需要处理的基础信息文件 # 假设最后一个文件是需要处理的基础信息文件

View File

@ -1,242 +1,36 @@
import re def second_query(baseinfo_list):
result_list = []
for item in baseinfo_list:
for key, value in item.items():
if isinstance(value, dict):
# 检查所有最内层的值是否都是 "未知"
if all(v == "未知" for v in value.values()):
result_list.append(key)
return result_list
data={ # 示例用法
"基础信息": { baseinfo_list = [
"招标人/代理信息": { {
"招标人": "广水市公安局", "招标人联系方式": {
"招标人联系方式": { "名称": "未知",
"名称": "广水市公安局", "地址": "未知",
"联系电话": "0722-6248000",
"地址": "广水市应山办事处应十大道189号"
},
"招标代理机构": "湖北楚振捷工程项目管理有限公司",
"招标代理机构联系方式": {
"名称": "湖北楚振捷工程项目管理有限公司",
"联系电话": "0722-6256088",
"地址": "广水市永阳一路41号"
},
"项目联系方式": {
"名称": "吴凡",
"联系电话": "18871130808"
}
}, },
"项目信息": { "项目名称": {
"项目名称": "广水市公安局视频会议高清化改造项目", "名称": "项目A",
"项目编号": "HBCZJ-2021-CS12", "地址": "未知",
"项目概况": "广水市公安局视频会议高清化改造项目", }
"项目基本情况": { },
"项目编号": "HBCZJ-2021-CS12", {
"采购计划备案号": "2021-04-000399", "供应商信息": {
"项目名称": "广水市公安局视频会议高清化改造项目", "名称": "未知",
"采购方式": "竞争性磋商", "联系方式": "未知",
"预算金额": "192万元",
"最高限价": "192万元",
"采购需求": {
"项目概况及内容": "广水市公安局视频会议高清化改造项目。",
"招标范围": "广水市公安局视频会议高清化改造项目,具体内容见磋商文件第三章。",
"项目地点": "具体以合同约定为准。"
},
"合同履行期限": "以合同签订为准。",
"本项目(是/否)接受联合体投标": "",
"是否可采购进口产品": ""
},
"招标控制价": "192万元",
"投标竞争下浮率": "未知",
"是否允许分包": "未知",
"是否接受联合体投标": ""
}, },
"采购要求": { "合同详情": {
"技术要求": "未提供", "金额": "100万",
"商务要求": "未提供", "期限": "未知",
"服务要求": "未提供",
"其他要求": "未提供"
},
"关键时间/内容": {
"投标文件递交截止日期": "2021年 6月 18日 15点 00分",
"投标文件递交地点": "广水市公共资源交易中心五楼 501号开标室",
"开标时间": "2021年 6月 18日 15点 00分",
"开标地点": "广水市公共资源交易中心五楼 501号开标室",
"澄清招标文件的截止时间": "同采购文件获取截止时间(如有)",
"投标有效期": "90日历天",
"信息公示媒介": "中国湖北政府采购网http://www.ccgp-hubei.gov.cn, 中国广水网http://www.zggsw.gov.cn/"
},
"保证金相关": {
"投标保证金": "不提交",
"履约保证金": "不提交",
"是否递交磋商保证金": "",
"退还投标保证金": "/",
"质量保证金": "未知"
},
"其他信息": {
"投标费用承担": "供应商应承担所有与准备和参加磋商有关的费用,不论磋商的结果如何,采购人和采购代理机构均无义务和责任承担这些费用。",
"招标代理服务费": {
"收费标准": "根据国家发展与改革委员会办公厅发改办价格【2003】857 号文的规定经协商由成交供应商按国家发展和改革委员发改价格【2011】534号文规定货物类取费标准向采购代理机构支付招标代理服务费包含“招标代理费、评标会务费、评标费”如本项目各包服务费不足叁仟元则供应商按叁仟元支付服务费。",
"递交方式": "成交服务费由成交供应商在领取成交通知书的同时向代理机构支付,可使用现金或电汇办理,汇款账户信息如下: 账户名:湖北楚振捷工程项目管理有限公司 账户号码580742561314 开户银行:中国银行股份有限公司广水支行营业部"
},
"是否退还投标文件": "",
"是否召开投标答疑会": "",
"投标预备会": "不召开",
"踏勘现场": "不组织",
"偏离": {
"偏离项要求": "供应商需在响应文件中提供《采购需求响应、偏离说明表/导读表》具体格式见第六章响应文件格式中的49页。供应商需对采购需求中的各项技术参数和服务要求进行逐项响应明确表明无偏离、正偏离或负偏离的情况。",
"正偏离定义": "供应商提供的产品或服务优于采购需求中的技术参数和服务要求。",
"负偏离定义": "供应商提供的产品或服务未能完全满足采购需求中的技术参数和服务要求。",
"偏离项处理": "非★号条款允许的负偏离项数及范围见第四章评标办法,★号条款不允许负偏离。",
"偏离项数量限制": "非★号条款允许的负偏离项数及范围见第四章评标办法,具体数量限制未知。",
"偏离项评分影响": "负偏离可能会影响供应商的评分,具体评分标准见第四章评定办法。"
}
} }
} }
} ]
def inner_post_processing(base_info):
"""
处理 '基础信息' 部分提取所需字段
参数: result = second_query(baseinfo_list)
base_info (dict): 包含 '基础信息' 的字典 print(result) # 输出: ['招标人联系方式', '供应商信息']
返回:
dict: 提取的信息字典 extracted_info
"""
# 初始化提取的信息字典
extracted_info = {}
# 定义一个辅助函数用于获取嵌套字典中的值
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 find_keys_containing(dic, substring):
found_values = []
if isinstance(dic, dict):
for key, value in dic.items():
if substring in key:
found_values.append(value)
if isinstance(value, dict):
found_values.extend(find_keys_containing(value, substring))
elif isinstance(value, list):
for item in value:
if isinstance(item, dict):
found_values.extend(find_keys_containing(item, substring))
return found_values
# 定义一个辅助函数用于根据候选键列表提取值(部分匹配)
def extract_field(contact_info, candidate_keys):
for candidate in candidate_keys:
for key, value in contact_info.items():
if candidate in key and value not in ["未知", ""]:
return value
return ""
# 定义一个辅助函数用于提取 '投标保证金'
def extract_bid_bond(guarantee_info):
# 定义投标保证金的候选键
bid_bond_candidates = ["投标保证金", "磋商保证金"]
# 第一步:查找包含 "投标保证金" 或 "磋商保证金" 的键
for candidate in bid_bond_candidates:
for key, value in guarantee_info.items():
if candidate in key:
if isinstance(value, dict):
# 在嵌套字典中查找包含 "金额" 的键
for sub_key, sub_value in value.items():
if "金额" in sub_key and sub_value not in ["未知", ""]:
return sub_value
elif isinstance(value, str):
if "金额" in key and value not in ["未知", ""]:
return value
else:
# 如果 value 既不是 dict 也不是 str忽略
continue
# 第二步:如果没有找到包含 "金额" 的键,尝试在所有键值中查找符合模式的值
amount_pattern = re.compile(r'(?:\d{1,3}(?:[,]\d{3})*(?:\.\d+)?|\d+(?:\.\d+)?|[\u4e00-\u9fff]+(?:\.\d+)?)\s*(?:元|万元)')
for key, value in guarantee_info.items():
if isinstance(value, str):
match = amount_pattern.search(value)
if match:
return match.group()
elif isinstance(value, dict):
# 递归查找嵌套字典中的金额
found_amount = extract_bid_bond(value)
if found_amount:
return found_amount
# 如果都没有找到,则返回空字符串
return ""
# 定义所需字段的映射关系,暂时不包含'联系人'和'联系电话'以及'招标项目地点'
mapping = {
"代理机构名称": [["招标人/代理信息", "招标代理机构"]],
"招标项目名称": [["项目信息", "项目名称"], ["项目信息", "工程名称"]],
"招标项目编号": [["项目信息", "项目编号"], ["项目信息", "招标编号"]],
"开标时间": [["关键时间/内容", "开标时间"]],
"报名截止日期": [["关键时间/内容", "投标文件递交截止日期"]],
"招标项目预算": [["项目信息", "招标控制价"]],
"招标单位名称": [["招标人/代理信息", "招标人"]],
"招标公告地址": [["关键时间/内容", "信息公示媒介"], ["关键时间/内容", "评标结果公示媒介"]]
}
# 提取并映射字段
for new_key, paths in mapping.items():
value = None
for path in paths:
value = get_nested(base_info, path)
if value:
break
extracted_info[new_key] = value if value else ""
# 特殊处理 '招标项目地点'
# 在 '项目信息' 下查找包含 "地点" 的键
project_info = base_info.get("项目信息", {})
location_candidates = find_keys_containing(project_info, "地点")
if location_candidates:
# 选择第一个找到的地点
extracted_info["招标项目地点"] = location_candidates[0]
else:
extracted_info["招标项目地点"] = ""
# 特殊处理 '联系人' 和 '联系电话'
# 提取 '项目联系方式'
project_contact = get_nested(base_info, ["招标人/代理信息", "项目联系方式"], {})
# 提取 '招标人联系方式'
bidder_contact = get_nested(base_info, ["招标人/代理信息", "招标人联系方式"], {})
# 定义候选键列表,按优先级排序
name_candidates = ["名称", "联系人", "招标"]
phone_candidates = ["电话", "手机", "联系方式"]
# 提取 '联系人'
contact_names = [project_contact, bidder_contact]
contact_name = ""
for contact in contact_names:
extracted_name = extract_field(contact, name_candidates)
if extracted_name:
contact_name = extracted_name
break
extracted_info["联系人"] = contact_name
# 提取 '联系电话'
contact_phones = [project_contact, bidder_contact]
contact_phone = ""
for contact in contact_phones:
extracted_phone = extract_field(contact, phone_candidates)
if extracted_phone:
contact_phone = extracted_phone
break
extracted_info["联系电话"] = contact_phone
# 特殊处理 '投标保证金'
# 提取 '保证金相关'
guarantee_info = get_nested(base_info, ["保证金相关"], {})
extracted_info["投标保证金"] = extract_bid_bond(guarantee_info)
return extracted_info
res=inner_post_processing(data["基础信息"])
print(res)

View File

@ -1,8 +1,10 @@
import json import json
import threading
import time
from flask_app.main.json_utils import clean_json_string, nest_json_under_key,rename_outer_key from flask_app.main.json_utils import clean_json_string, nest_json_under_key,rename_outer_key
from flask_app.main.投标人须知正文提取指定内容 import extract_from_notice from flask_app.main.投标人须知正文提取指定内容 import extract_from_notice
from flask_app.main.判断是否分包等 import judge_whether_main, read_questions_from_judge from flask_app.main.判断是否分包等 import judge_whether_main, read_questions_from_judge, merge_json_to_list
from flask_app.main.多线程提问 import read_questions_from_file, multi_threading from flask_app.main.多线程提问 import read_questions_from_file, multi_threading
from flask_app.main.通义千问long import upload_file from flask_app.main.通义千问long import upload_file
@ -110,7 +112,70 @@ def judge_consortium_bidding(baseinfo_list):
# 更新原始列表,如果你想保留修改 # 更新原始列表,如果你想保留修改
baseinfo_list[:] = updated_list baseinfo_list[:] = updated_list
return accept_bidding return accept_bidding
def combine_basic_info(merged_baseinfo_path,truncate0, output_folder, clause_path):
def generate_query(baseinfo_list):
questions_list = []
for item in baseinfo_list:
# print(json.dumps(item, ensure_ascii=False, indent=4))
for key, value in item.items():
if value == "未知" or (isinstance(value, dict) and all(v == "未知" for v in value.values())):
question = (
f"根据该招标文件中的信息,{key}的内容是怎样的?"
f"请按json格式给我提供信息键名是'{key}'"
f"若存在未知信息,在对应的键值中填'未知'"
)
questions_list.append(question)
return questions_list
def update_baseinfo_lists(baseinfo_list1, baseinfo_list2):
# 创建一个字典,用于存储 baseinfo_list1 中的所有键值对
combined_dict = {}
for item in baseinfo_list1:
combined_dict.update(item)
# 使用 baseinfo_list2 中的信息更新 combined_dict
for item in baseinfo_list2:
for key, value in item.items():
if key in combined_dict:
combined_dict[key] = value
# 重新构建 baseinfo_list1保持原有的结构
updated_list = []
for item in baseinfo_list1:
updated_item = {}
for key in item:
updated_item[key] = combined_dict[key]
updated_list.append(updated_item)
return updated_list
def process_judge_questions(judge_file_path, chosen_numbers, truncate0, baseinfo_list1):
judge_questions = read_questions_from_judge(judge_file_path, chosen_numbers)
judge_consortium = judge_consortium_bidding(baseinfo_list1)
if judge_consortium:
judge_consortium_question = (
"该招标文件对于联合体投标的要求是怎样的请按json格式给我提供信息"
"外层键名为'联合体投标要求',其中有一个嵌套键值对为:\"是否接受联合体投标\":\"\""
)
judge_questions.append(judge_consortium_question)
file_id3 = upload_file(truncate0)
res2 = multi_threading(judge_questions, "", file_id3, 2)
if not res2:
print("基础信息整合: multi_threading error!")
else:
for question, response in res2:
baseinfo_list1.append(clean_json_string(response))
def process_questions_list(questions_list, truncate2):
if questions_list:
file_id = upload_file(truncate2)
baseinfo_results = multi_threading(questions_list, "", file_id, 2)
return [clean_json_string(res) for _, res in baseinfo_results] if baseinfo_results else []
else:
return []
def combine_basic_info(merged_baseinfo_path,truncate0,truncate2, clause_path):
""" """
综合和处理基础信息生成最终的基础信息字典 综合和处理基础信息生成最终的基础信息字典
@ -124,54 +189,60 @@ def combine_basic_info(merged_baseinfo_path,truncate0, output_folder, clause_pat
- dict: 综合后的基础信息 - dict: 综合后的基础信息
""" """
# baseinfo_prompt_file_path='flask_app/static/提示词/基本信息工程标qianwen-long.txt' # baseinfo_prompt_file_path='flask_app/static/提示词/基本信息工程标qianwen-long.txt'
file_id1 = upload_file(merged_baseinfo_path)
baseinfo_prompt_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\基本信息工程标qianwen-long.txt' baseinfo_prompt_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\基本信息工程标qianwen-long.txt'
questions = read_questions_from_file(baseinfo_prompt_file_path) questions = read_questions_from_file(baseinfo_prompt_file_path)
file_id = upload_file(merged_baseinfo_path)
baseinfo_results = multi_threading(questions, "", file_id, 2)
# 清理 JSON 字符串
baseinfo_list = [clean_json_string(res) for _, res in baseinfo_results] if baseinfo_results else []
for i in baseinfo_list:
print(json.dumps(i,ensure_ascii=False,indent=4))
# 判断是否分包、是否需要递交投标保证金等 # 判断是否分包、是否需要递交投标保证金等
chosen_numbers, merged = judge_whether_main(truncate0, output_folder) more_query = "请你根据招标文件信息,回答以下问题:是否组织踏勘现场?是否召开投标预备会?是否允许偏离?是否退还投标文件?是否允许分包? 是否需要递交投标保证金是否需要提交履约保证金履约担保是否有招标代理服务费请按json格式给我提供信息键名分别为'是否组织踏勘现场','是否召开投标预备会','是否允许偏离','是否退还投标文件',是否允许分包','是否递交投标保证金','是否提交履约保证金','是否有招标代理服务费',键值仅限于'','','未知',若存在矛盾信息,请回答'未知'"
baseinfo_list.append(merged) questions.append(more_query)
baseinfo_results = multi_threading(questions, "", file_id1, 2)
# 清理 JSON 字符串
baseinfo_list1 = [clean_json_string(res) for _, res in baseinfo_results] if baseinfo_results else []
# judge_file_path = 'flask_app/static/提示词/是否相关问题.txt' chosen_numbers, merged = merge_json_to_list(baseinfo_list1.pop())
judge_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\是否相关问题.txt' baseinfo_list1.append(merged)
judge_questions = read_questions_from_judge(judge_file_path, chosen_numbers)
judge_consortium = judge_consortium_bidding(baseinfo_list) # 通过招标公告判断是否接受联合体投标
if judge_consortium: questions_list=generate_query(baseinfo_list1)
judge_consortium_question = (
"该招标文件对于联合体投标的要求是怎样的请按json格式给我提供信息"
"外层键名为'联合体投标要求',其中有一个嵌套键值对为:\"是否接受联合体投标\":\"\""
)
judge_questions.append(judge_consortium_question)
file_id = upload_file(truncate0) # judge_file_path = 'flask_app/static/提示词/是否相关问题qianwen-long.txt'
res2 = multi_threading(judge_questions, "", file_id, 2) # 调用千问-long judge_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\是否相关问题qianwen-long.txt'
# 创建两个线程
thread1 = threading.Thread(target=process_judge_questions,
args=(judge_file_path, chosen_numbers, truncate0, baseinfo_list1))
thread2 = threading.Thread(target=process_questions_list, args=(questions_list, truncate2))
if not res2: # 启动线程
print("基础信息整合: multi_threading error!") thread1.start()
else: thread2.start()
for question, response in res2:
baseinfo_list.append(clean_json_string(response)) # 等待两个线程完成
thread1.join()
thread2.join()
# 处理结果
# baseinfo_list1 已经在 process_judge_questions 中被更新
baseinfo_list2 = process_questions_list(questions_list, truncate2)
updated_list=update_baseinfo_lists(baseinfo_list1,baseinfo_list2)
rebidding_situation = extract_from_notice(clause_path, 3) # "重新招标, 不再招标和终止招标"需从投标人须知正文提取 rebidding_situation = extract_from_notice(clause_path, 3) # "重新招标, 不再招标和终止招标"需从投标人须知正文提取
update_json = rename_outer_key(rebidding_situation, "重新招标、不再招标和终止招标") update_json = rename_outer_key(rebidding_situation, "重新招标、不再招标和终止招标")
baseinfo_list.append(update_json) updated_list.append(update_json)
aggregated_baseinfo = aggregate_basic_info_engineering(baseinfo_list) # 现在是一个字典 aggregated_baseinfo = aggregate_basic_info_engineering(updated_list) # 现在是一个字典
return {"基础信息": aggregated_baseinfo} return {"基础信息": aggregated_baseinfo}
#TODO:先不带投标人须知正文,如果是未知,再直接问正文, #TODO:先不带投标人须知正文,如果是未知,再直接问正文,
if __name__ == "__main__": if __name__ == "__main__":
start_time=time.time()
merged_baseinfo_path="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_merged_baseinfo.pdf" merged_baseinfo_path="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_merged_baseinfo.pdf"
output_folder="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output" # output_folder="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output"
truncate0="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_tobidders_notice_table.pdf" truncate0="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_tobidders_notice_table.pdf"
truncate2="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_tobidders_notice.pdf"
clause_path="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\clause1.json" clause_path="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\clause1.json"
res=combine_basic_info(merged_baseinfo_path,truncate0,output_folder,clause_path) res=combine_basic_info(merged_baseinfo_path,truncate0,truncate2,clause_path)
print(json.dumps(res,ensure_ascii=False,indent=4)) print(json.dumps(res,ensure_ascii=False,indent=4))
end_time=time.time()
print("elapsed_time:"+str(end_time-start_time))

View File

@ -290,13 +290,28 @@ def truncate_pdf_main(input_path, output_folder, selection):
return process_input(input_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix) return process_input(input_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix)
def truncate_pdf_multiple(input_path, output_folder): def truncate_pdf_multiple_old(input_path, output_folder):
truncate_files = [] truncate_files = []
for selection in range(1, 5): for selection in range(1, 5):
files = truncate_pdf_main(input_path, output_folder, selection) files = truncate_pdf_main(input_path, output_folder, selection)
truncate_files.extend(files) truncate_files.extend(files)
return truncate_files return truncate_files
def truncate_pdf_multiple(input_path, output_folder):
base_file_name = os.path.splitext(os.path.basename(input_path))[0] #纯文件名
truncate_files = []
for selection in range(1, 6):
files = truncate_pdf_main(input_path, output_folder, selection)
truncate_files.extend(files)
if truncate_files:
merged_output_path = os.path.join(output_folder, f"{base_file_name}_merged_baseinfo.pdf")
merge_selected_pdfs(output_folder, truncate_files, merged_output_path,base_file_name)
truncate_files.append(merged_output_path)
print(f"已生成合并文件: {merged_output_path}")
else:
print(f"没有文件需要合并 for {input_path}")
return truncate_files
def merge_selected_pdfs(output_folder, truncate_files, output_path, base_file_name): def merge_selected_pdfs(output_folder, truncate_files, output_path, base_file_name):
""" """
合并 output_folder 中以 {base_file_name}_before.pdf 结尾的 PDF 文件 合并 output_folder 中以 {base_file_name}_before.pdf 结尾的 PDF 文件
@ -359,7 +374,7 @@ def merge_selected_pdfs(output_folder, truncate_files, output_path, base_file_na
merge_pdfs(all_pdfs_to_merge, output_path) merge_pdfs(all_pdfs_to_merge, output_path)
print(f"已成功合并 PDF 文件到 '{output_path}'") print(f"已成功合并 PDF 文件到 '{output_path}'")
def truncate_pdf_specific_engineering(pdf_path, output_folder): def truncate_pdf_specific_engineering(pdf_path, output_folder,selections):
""" """
处理 PDF 文件选择 selection 51 3 的部分并合并结果 处理 PDF 文件选择 selection 51 3 的部分并合并结果
@ -373,7 +388,6 @@ def truncate_pdf_specific_engineering(pdf_path, output_folder):
try: try:
base_file_name = os.path.splitext(os.path.basename(pdf_path))[0] base_file_name = os.path.splitext(os.path.basename(pdf_path))[0]
truncate_files = [] truncate_files = []
selections = [5, 1, 3] # 仅处理 selection 5、1 和 3
for selection in selections: for selection in selections:
files = truncate_pdf_main(pdf_path, output_folder, selection) files = truncate_pdf_main(pdf_path, output_folder, selection)
@ -402,10 +416,11 @@ def truncate_pdf_specific_engineering(pdf_path, output_folder):
if __name__ == "__main__": if __name__ == "__main__":
# input_path = "C:\\Users\\Administrator\\Desktop\\fsdownload\\4bda9fde-89fc-4e5e-94a4-ce6c43010f74\\ztbfile.pdf" # input_path = "C:\\Users\\Administrator\\Desktop\\fsdownload\\4bda9fde-89fc-4e5e-94a4-ce6c43010f74\\ztbfile.pdf"
# output_folder = "C:\\Users\\Administrator\\Desktop\\fsdownload\\4bda9fde-89fc-4e5e-94a4-ce6c43010f74" # output_folder = "C:\\Users\\Administrator\\Desktop\\fsdownload\\4bda9fde-89fc-4e5e-94a4-ce6c43010f74"
input_path="C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest16.pdf" input_path="C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest2.pdf"
output_folder="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output" output_folder="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output"
# files=truncate_pdf_multiple(input_path,output_folder) files=truncate_pdf_multiple(input_path,output_folder)
files=truncate_pdf_specific_engineering(input_path,output_folder) # selections = [5, 1] # 仅处理 selection 5、1 和 3
# files=truncate_pdf_specific_engineering(input_path,output_folder,selections)
print(files) print(files)
# selection = 3 # 例如1 - 投标人须知前附表, 2 - 评标办法, 3 - 投标人须知正文 4-资格审查条件 5-招标公告 6-无效标 # selection = 3 # 例如1 - 投标人须知前附表, 2 - 评标办法, 3 - 投标人须知正文 4-资格审查条件 5-招标公告 6-无效标
# generated_files = truncate_pdf_main(input_path, output_folder, selection) # generated_files = truncate_pdf_main(input_path, output_folder, selection)

View File

@ -219,7 +219,12 @@ def post_process(value):
return processed_blocks return processed_blocks
# 递归地处理嵌套结构 """
递归处理嵌套的数据结构字典和列表
对最内层的字符串值应用 post_process 函数
post_process 函数尝试将长字符串按特定模式分割成块每块至少包含50个中英文字符
如果字典中所有值都是 """/" 或空列表则返回''的列表
"""
def process_nested_data(data): def process_nested_data(data):
# 先检查是否所有值都是 ""、"/" 或空列表 # 先检查是否所有值都是 ""、"/" 或空列表
if isinstance(data, dict) and all(v == "" or v == "/" or (isinstance(v, list) and not v) for v in data.values()): if isinstance(data, dict) and all(v == "" or v == "/" or (isinstance(v, list) and not v) for v in data.values()):
@ -229,7 +234,12 @@ def process_nested_data(data):
# 如果当前项是字典,继续递归遍历其键值对 # 如果当前项是字典,继续递归遍历其键值对
result = {} result = {}
for key, value in data.items(): for key, value in data.items():
result[key] = process_nested_data(value) # 递归处理子项 processed_value = process_nested_data(value)
# 如果处理后的值是只有一个元素的列表,就直接使用该元素
if isinstance(processed_value, list) and len(processed_value) == 1:
result[key] = processed_value[0]
else:
result[key] = processed_value
return result return result
elif isinstance(data, list): elif isinstance(data, list):
# 如果是列表,直接返回列表,保持原样 # 如果是列表,直接返回列表,保持原样
@ -239,6 +249,8 @@ def process_nested_data(data):
return post_process(data) return post_process(data)
# 读取JSON数据提取内容转换结构并打印结果 # 读取JSON数据提取内容转换结构并打印结果
def extract_from_notice(clause_path, type): def extract_from_notice(clause_path, type):
if type == 1: if type == 1:
@ -258,6 +270,7 @@ def extract_from_notice(clause_path, type):
# print(json.dumps(extracted_data,ensure_ascii=False,indent=4)) # print(json.dumps(extracted_data,ensure_ascii=False,indent=4))
sorted_data = sort_clean_data_keys(extracted_data) # 对输入的字典 data 的键进行预处理和排序 sorted_data = sort_clean_data_keys(extracted_data) # 对输入的字典 data 的键进行预处理和排序
transformed_data = transform_json(sorted_data) transformed_data = transform_json(sorted_data)
# print(json.dumps(transformed_data,ensure_ascii=False,indent=4))
final_result = process_nested_data(transformed_data) final_result = process_nested_data(transformed_data)
return final_result return final_result
@ -265,10 +278,9 @@ def extract_from_notice(clause_path, type):
# TODO: extract_json新版本仍有问题未知。 # TODO: extract_json新版本仍有问题未知。
if __name__ == "__main__": if __name__ == "__main__":
# file_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\3bffaa84-2434-4bd0-a8ee-5c234ccd7fa0\\clause1.json' # file_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\3bffaa84-2434-4bd0-a8ee-5c234ccd7fa0\\clause1.json'
# file_path = 'C:\\Users\\Administrator\\Desktop\\货物标\\output4\\tmp1\\clause9.json' file_path="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\clause1.json"
file_path="C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\clause1.json"
try: try:
res = extract_from_notice(file_path, 2) # 可以改变此处的 type 参数测试不同的场景 res = extract_from_notice(file_path, 1) # 可以改变此处的 type 参数测试不同的场景
res2 = json.dumps(res, ensure_ascii=False, indent=4) res2 = json.dumps(res, ensure_ascii=False, indent=4)
print(res2) print(res2)
except ValueError as e: except ValueError as e:

View File

@ -0,0 +1,18 @@
1.该招标文件对于分包的要求是怎样的请按json格式给我提供信息键名为'分包'。
2.根据招标文件第二章投标人须知该项目投标保证金的内容或要求是什么请按json格式给我提供信息外层键名为"投标保证金",若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
3.该招标文件对于投标保证金的退还相关的规章办法是怎样的请按json格式给我提供信息键名为'退还投标保证金',若存在嵌套信息,嵌套内容键名以文档中对应字段命名。示例格式如下:
{
"退还投标保证金":"投标保证金的退还按《xxx》相关条款执行。"
}
4.根据投标人须知前附表,该项目对于履约保证金(履约担保)的内容或要求是怎样的请按json格式给我提供信息外层键名为"履约保证金",若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
5.本项目的招标代理服务费或中标服务费、成交服务费的相关内容是怎样的请按json格式给我提供信息外层键名为'招标代理服务费',若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
6.该招标文件对于踏勘现场的内容或要求是怎样的请按json格式给我提供信息外层键名为"踏勘现场",若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
7.该招标文件对于投标预备会或投标答疑会内容是怎样的请按json格式给我提供信息外层键名为"投标预备会"(或"投标答疑会"),若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
8.本项目可偏离的项目和范围是怎样的请以json格式给我提供信息外层键名为'偏离',若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。

View File

@ -175,7 +175,12 @@ def post_process(value):
# 如果所有的块都符合条件,返回分割后的列表 # 如果所有的块都符合条件,返回分割后的列表
return processed_blocks return processed_blocks
# 递归地处理嵌套结构 """
递归处理嵌套的数据结构字典和列表
对最内层的字符串值应用 post_process 函数
post_process 函数尝试将长字符串按特定模式分割成块每块至少包含50个中英文字符
如果字典中所有值都是 """/" 或空列表则返回''的列表
"""
def process_nested_data(data): def process_nested_data(data):
# 先检查是否所有值都是 ""、"/" 或空列表 # 先检查是否所有值都是 ""、"/" 或空列表
if isinstance(data, dict) and all(v == "" or v == "/" or (isinstance(v, list) and not v) for v in data.values()): if isinstance(data, dict) and all(v == "" or v == "/" or (isinstance(v, list) and not v) for v in data.values()):
@ -185,7 +190,12 @@ def process_nested_data(data):
# 如果当前项是字典,继续递归遍历其键值对 # 如果当前项是字典,继续递归遍历其键值对
result = {} result = {}
for key, value in data.items(): for key, value in data.items():
result[key] = process_nested_data(value) # 递归处理子项 processed_value = process_nested_data(value)
# 如果处理后的值是只有一个元素的列表,就直接使用该元素
if isinstance(processed_value, list) and len(processed_value) == 1:
result[key] = processed_value[0]
else:
result[key] = processed_value
return result return result
elif isinstance(data, list): elif isinstance(data, list):
# 如果是列表,直接返回列表,保持原样 # 如果是列表,直接返回列表,保持原样

View File

@ -576,7 +576,7 @@ def truncate_pdf_multiple(pdf_path, output_folder):
return truncate_files return truncate_files
#小解析,只需要前三章内容 #小解析,只需要前三章内容
def truncate_pdf_specific_goods(pdf_path, output_folder): def truncate_pdf_specific_goods(pdf_path, output_folder,selections):
""" """
处理 PDF 文件选择 selection 4 5 的部分并合并结果 处理 PDF 文件选择 selection 4 5 的部分并合并结果
@ -589,7 +589,6 @@ def truncate_pdf_specific_goods(pdf_path, output_folder):
""" """
base_file_name = os.path.splitext(os.path.basename(pdf_path))[0] base_file_name = os.path.splitext(os.path.basename(pdf_path))[0]
truncate_files = [] truncate_files = []
selections = [4, 5] # 仅处理 selection 4 和 5
for selection in selections: for selection in selections:
files = truncate_pdf_main(pdf_path, output_folder, selection) files = truncate_pdf_main(pdf_path, output_folder, selection)
@ -615,7 +614,7 @@ if __name__ == "__main__":
# input_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\交警支队机动车查验监管系统项目采购.pdf" # input_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\交警支队机动车查验监管系统项目采购.pdf"
input_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles" input_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles"
output_folder = "C:\\Users\\Administrator\\Desktop\\货物标\\截取test" output_folder = "C:\\Users\\Administrator\\Desktop\\货物标\\截取test"
# files = truncate_pdf_multiple(input_path, output_folder) files = truncate_pdf_multiple(input_path, output_folder)
# files=truncate_pdf_specific_goods(input_path,output_folder) # files=truncate_pdf_specific_goods(input_path,output_folder)
# print(files) # print(files)
selection = 1# 例如1 - 商务技术服务要求, 2 - 评标办法, 3 - 资格审查后缀有qualification1或qualification2与评标办法一致 4.投标人须知前附表part1 投标人须知正文part2 5-公告 selection = 1# 例如1 - 商务技术服务要求, 2 - 评标办法, 3 - 资格审查后缀有qualification1或qualification2与评标办法一致 4.投标人须知前附表part1 投标人须知正文part2 5-公告