10.15货物标完整版

This commit is contained in:
zy123 2024-10-15 20:57:58 +08:00
parent 82de3ae202
commit dd06b7b199
21 changed files with 571 additions and 368 deletions

View File

@ -101,7 +101,7 @@ def rename_outer_key(original_data,new_key):
# 创建一个新的字典,使用新的键名
new_data = {new_key: original_value}
return json.dumps(new_data,ensure_ascii=False)
return new_data
def transform_json_values(data):
if isinstance(data, dict):
return {key: transform_json_values(value) for key, value in data.items()}

View File

@ -1,92 +1,51 @@
import json
from collections import defaultdict
import re
def parse_json_with_duplicates(json_string):
"""
解析具有重复键的 JSON 字符串将所有重复的键值对存储为列表
from docx import Document
def preprocess_paragraphs(paragraphs):
processed = []
index = 0
while index < len(paragraphs):
current_text = paragraphs[index].text.strip()
Args:
json_string (str): 需要解析的 JSON 字符串
# 检测是否为空白行
if current_text == '':
# 确保有前一行和后一行
if index > 0 and index + 1 < len(paragraphs):
prev_text = paragraphs[index - 1].text.strip()
# print(prev_text)
next_text = paragraphs[index + 1].text.strip()
# print(next_text)
# print("------------------------------")
# 检查前一行是否不以指定标点结尾
if not prev_text.endswith(('', ',', '', '!', '?')):
# 检查后一行是否以序号开头
if re.match(r'^(\d+(\s*\.\s*\d+)*)\s*', prev_text) and not re.match(r'^(\d+(\s*\.\s*\d+)*)\s*、',next_text) and len(prev_text)>30:
# 合并前一行和后一行
merged_text = prev_text + next_text
# print(merged_text)
# print("---------------------------------")
if processed:
# 用合并后的文本替换已处理的前一行
processed[-1] = merged_text
else:
processed.append(merged_text)
# 跳过后一行
index += 2
continue
else:
# 非空白行,直接添加到处理后的列表
processed.append(current_text)
Returns:
dict: 解析后的字典重复的键对应的值为列表
"""
def custom_object_pairs_hook(pairs):
d = defaultdict(list)
for key, value in pairs:
# 如果值是字典或列表,递归处理
if isinstance(value, dict):
value = process_dict(value)
elif isinstance(value, list):
value = process_list(value)
d[key].append(value)
# 将有多个值的键转换为列表,单个值的键保持原样
return {key: (values if len(values) > 1 else values[0]) for key, values in d.items()}
index += 1
return processed
def process_dict(d):
"""
递归处理字典确保所有重复键的值为列表
doc_path = 'D:\\flask_project\\flask_app\\static\\output\\015d997e-c32c-49d1-a611-a2e817ace6a1\\ztbfile.docx'
doc = Document(doc_path)
Args:
d (dict): 需要处理的字典
# 假设 doc 是您的文档对象
processed_paragraphs = preprocess_paragraphs(doc.paragraphs)
for i in processed_paragraphs:
print("___________________________")
print(i)
Returns:
dict: 处理后的字典
"""
return custom_object_pairs_hook(d.items())
def process_list(l):
"""
递归处理列表确保列表中的所有字典也被处理
Args:
l (list): 需要处理的列表
Returns:
list: 处理后的列表
"""
return [process_dict(item) if isinstance(item, dict) else item for item in l]
return json.loads(json_string, object_pairs_hook=custom_object_pairs_hook)
# 示例使用
input_string = '''
{
"商务评分": {
"综合实力": {
"评分": "5分",
"要求": "投标人具备有效期内的 ISO9001质量体系认证证书、具备有效期内的OHSA18001职业健康安全体系认证证书、具备有效期内的 IS014001环境管理体系认证证书、具备有效期内的 ISO20000信息技术服务体系认证证书、具备有效期内的 ISO27001信息安全体系认证证书全部满足得 5分每缺一项扣 1分开标时需提供原件"
},
"综合实力": {
"评分": "2分",
"要求": "投标人具备电子与智能化工程专业承包二级资质及以上证书得 2分不能够提供不得分开标时需提供原件"
},
"综合实力": {
"评分": "2分",
"要求": "投标人具有建筑机电安装工程专业承包三级资质或以上资质得 2分否则不得分。证书开标原件备查"
},
"综合实力": {
"评分": "3分",
"要求": "投标人需具有健全的信息技术运维服务能力通过ITSS信息技术服务运维标准符合性认证得 3分投标时需提供相关证书原件予以证明否则不得分。"
},
"综合实力": {
"评分": "2分",
"要求": "投标人具备 CCRC信息安全服务安全集成三级及以上证书得 2分不能够提供不得分开标时需提供原件"
},
"类似业绩": {
"评分": "4分",
"要求": "近三年(自投标截止时间前推 36个月以合同签订日期为准中标人作为独立承包人有已完成的类似业绩项目建设内容含应包含会议系统设备采购项目及改造每提供一份业绩得 2分最多可得 4分。业绩证明材料须提供中标公示截图、中标通知书、合同复印件,开标时备查,否则不得分。)"
},
"质量保证": {
"评分": "2分",
"要求": "投标人所投的MCU及视频会议终端设备产品如果不是自己生产的需提供制造商出具的授权及满足招标质保要求的售后服务承诺函提供得 2分开标时提供授权书及售后服务承诺函原件予以证明否则不得分。"
}
}
}
'''
# 解析具有重复键的 JSON 字符串
parsed_data = parse_json_with_duplicates(input_string)
# 打印结果
print(json.dumps(parsed_data, ensure_ascii=False, indent=4))

View File

@ -46,22 +46,23 @@ def merge_json_to_list(merged):
merged['分包'] = '不允许'
merged.pop('是否允许分包', None)
# 处理是否递交投标保证金
if merged.get('是否递交投标保证金') == '':
# 处理是否递交投标保证金或磋商保证金
guarantee_key = '是否递交投标保证金' if '是否递交投标保证金' in merged else '是否递交磋商保证金'
if merged.get(guarantee_key) == '':
chosen_numbers.extend([2, 3])
merged.pop('是否递交投标保证金', None)
elif merged.get('是否递交投标保证金') == '':
merged['投标保证金'] = '不提交'
merged['退还投标保证金'] = '/'
merged.pop('是否递交投标保证金', None)
else:
guarantee_type = '投标' if '投标' in guarantee_key else '磋商'
merged[f'{guarantee_type}保证金'] = '不提交'
merged[f'退还{guarantee_type}保证金'] = '/'
merged.pop(guarantee_key, None)
# 处理是否有履约保证金
if merged.get('是否履约保证金') == '':
if merged.get('是否提交履约保证金') == '':
chosen_numbers.append(4)
merged.pop('是否履约保证金', None)
elif merged.get('是否履约保证金') == '':
merged.pop('是否提交履约保证金', None)
elif merged.get('是否提交履约保证金') == '':
merged['履约保证金'] = '不提交'
merged.pop('是否履约保证金', None)
merged.pop('是否提交履约保证金', None)
# 处理是否有招标代理服务费
if merged.get('是否有招标代理服务费') == '':
@ -78,12 +79,13 @@ def merge_json_to_list(merged):
merged['踏勘现场']='不组织'
merged.pop('是否组织踏勘现场', None)
if merged.get('是否召开投标预备会') == '':
preparation_key = '是否召开投标预备会' if '是否召开投标预备会' in merged else '是否召开投标答疑会'
if merged.get(preparation_key) == '':
chosen_numbers.append(7)
merged.pop('是否召开投标预备会',None)
elif merged.get('是否召开投标预备会') == '':
merged['投标预备会']='不召开'
merged.pop('是否召开投标预备会', None)
else:
meeting_type = '预备会' if '预备会' in preparation_key else '答疑会'
merged[f'投标{meeting_type}'] = '不召开'
merged.pop(preparation_key, None)
if merged.get('是否允许偏离') == '':
chosen_numbers.append(8)
@ -123,7 +125,7 @@ def judge_whether_main(file_path,output_folder): #传入招标文件中‘投
read_pdf_and_judge_main(file_path, output_json_path) #提取打勾符号
qianwen_answer = qianwen_ask(output_json_path, user_query1) # 调用普通千问判断是、否、未知
user_query2 = construct_judge_questions(qianwen_answer) # 提取回答为”未知“的键
# 判断user_query是否为空
# 判断user_query2是否为空
if user_query2:
file_id = upload_file(file_path)
res = qianwen_long(file_id, user_query2) #整个前附表一起传问千问long

View File

@ -153,8 +153,6 @@ def combine_evaluation_standards(truncate2):
# target_values2=['投标报价','商务标','商务部分','报价部分','业绩','信誉','分值','计算公式','信用','人员','资格','奖项','认证','荣誉']
# update_json=combine_technical_and_business(clean_json_string(evaluation_res),target_values1,target_values2)
update_json = combine_technical_and_business(clean_json_string(evaluation_res), target_values1)
# evaluation_combined_res = json.dumps(update_json,ensure_ascii=False,indent=4)
# return evaluation_combined_res
return update_json #商务标技术标整合
if __name__ == "__main__":
truncate2="C:\\Users\\Administrator\\Desktop\\招标文件\\招标01_evaluation_method.pdf"

View File

@ -1,10 +1,12 @@
import json
from flask_app.main.json_utils import clean_json_string, nest_json_under_key,rename_outer_key, combine_json_results
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 judge_whether_main, read_questions_from_judge
from flask_app.main.多线程提问 import read_questions_from_file, multi_threading
from flask_app.main.通义千问long import upload_file
def aggregate_basic_info(baseinfo_list):
"""
将基础信息列表中的数据进行合并和分类
@ -13,12 +15,11 @@ def aggregate_basic_info(baseinfo_list):
- baseinfo_list (list): 包含多个基础信息的列表
返回
- list: 合并和分类后的基础信息列表
- dict: 合并和分类后的基础信息字典
"""
combined_baseinfo_list = []
key_groups = {
"招标人/代理信息": ["招标人", "招标人联系方式", "招标代理机构", "招标代理机构联系方式"],
"项目信息": ["工程名称", "招标编号", "工程概况", "招标范围", "招标控制价", "投标竞争下浮率"],
"招标人/代理信息": ["招标人", "招标人联系方式", "招标代理机构", "招标代理机构联系方式","招标代理服务费"],
"项目信息": ["项目名称", "招标编号", "项目概况", "招标范围", "招标控制价", "投标竞争下浮率"],
"关键时间/内容": [
"投标文件递交截止日期",
"递交方式",
@ -30,7 +31,7 @@ def aggregate_basic_info(baseinfo_list):
"其他信息": [
"重新招标、不再招标和终止招标",
"是否退还投标文件",
"费用承担"
"投标费用承担"
]
}
@ -39,9 +40,9 @@ def aggregate_basic_info(baseinfo_list):
# 合并所有基础信息并收集相关键
for baseinfo in baseinfo_list:
json_data = clean_json_string(baseinfo)
combined_data.update(json_data)
relevant_keys_detected.update(json_data.keys())
# json_data = clean_json_string(baseinfo)
combined_data.update(baseinfo)
relevant_keys_detected.update(baseinfo.keys())
# 动态调整键组
dynamic_key_handling(key_groups, relevant_keys_detected)
@ -49,10 +50,12 @@ def aggregate_basic_info(baseinfo_list):
# 按键组分类并嵌套
for group_name, keys in key_groups.items():
group_data = {key: combined_data.get(key, "未提供") for key in keys}
combined_json = nest_json_under_key(group_data, group_name)
combined_baseinfo_list.append(combined_json)
combined_data[group_name] = group_data
# Optionally remove original keys to avoid duplication
for key in keys:
combined_data.pop(key, None)
return combined_baseinfo_list
return combined_data
def dynamic_key_handling(key_groups, detected_keys):
# 检查和调整键组配置
@ -83,9 +86,9 @@ def judge_consortium_bidding(baseinfo_list):
accept_bidding = True
# 从字典中移除特定键值对
json_data.pop("是否接受联合体投标", None)
# 将修改后的 json 数据转换回 JSON 字符串(如果需要)
updated_info = json.dumps(json_data)
updated_list.append(updated_info)
# # 将修改后的 json 数据转换回 JSON 字符串(如果需要)
# updated_info = json.dumps(json_data)
updated_list.append(json_data)
# 更新原始列表,如果你想保留修改
baseinfo_list[:] = updated_list
return accept_bidding
@ -104,6 +107,7 @@ def combine_basic_info(knowledge_name, truncate0, output_folder, clause_path):
"""
baseinfo_list = []
baseinfo_file_path = 'flask_app/static/提示词/前两章提问总结.txt'
# baseinfo_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\前两章提问总结.txt'
questions = read_questions_from_file(baseinfo_file_path)
res1 = multi_threading(questions, knowledge_name)
@ -121,6 +125,7 @@ def combine_basic_info(knowledge_name, truncate0, output_folder, clause_path):
baseinfo_list.append(merged)
judge_file_path = 'flask_app/static/提示词/是否相关问题.txt'
# judge_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\是否相关问题.txt'
judge_questions = read_questions_from_judge(judge_file_path, chosen_numbers)
judge_consortium = judge_consortium_bidding(baseinfo_list) # 通过招标公告判断是否接受联合体投标
@ -138,16 +143,13 @@ def combine_basic_info(knowledge_name, truncate0, output_folder, clause_path):
print("基础信息整合: multi_threading error!")
else:
for question, response in res2:
baseinfo_list.append(response)
baseinfo_list.append(clean_json_string(response))
rebidding_situation = extract_from_notice(clause_path, 3) # "重新招标, 不再招标和终止招标"需从投标人须知正文提取
update_json = rename_outer_key(rebidding_situation, "重新招标、不再招标和终止招标")
baseinfo_list.append(update_json)
aggregated_baseinfo = aggregate_basic_info(baseinfo_list) # 整合基础信息核心代码
baseinfo_combined_res = combine_json_results(aggregated_baseinfo) # 返回值是字典
return {"基础信息": baseinfo_combined_res}
aggregated_baseinfo = aggregate_basic_info(baseinfo_list) # 现在是一个字典
return {"基础信息": aggregated_baseinfo}
if __name__ == "__main__":
knowledge_name = "ztb"

View File

@ -1,8 +1,6 @@
# -*- encoding:utf-8 -*-
import json
import logging
import os
import sys
import time
from concurrent.futures import ThreadPoolExecutor
from flask_app.main.截取pdf import truncate_pdf_multiple

View File

@ -152,18 +152,23 @@ def clean_dict_datas(extracted_contents, keywords,excludes): #让正则表达
else:
# 如果没有找到关键词,保留原文本
cleaned_text = data
all_texts1.append(cleaned_text) # 将处理后的文本添加到结果列表
# 删除空格
cleaned_text_no_spaces = cleaned_text.replace(' ', '').replace(' ', '')
all_texts1.append(cleaned_text_no_spaces) # 将处理后的文本添加到结果列表
else:
# print(text_list)
new_text_list=preprocess_text_list(text_list)
# print(new_text_list)
pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)'
data = re.sub(pattern, '', new_text_list[0]).strip() #去除序号
# data = re.sub(pattern, '', new_text_list[0]).strip() #去除序号
data = re.sub(pattern, '', new_text_list[0]).replace(' ','').strip()
# 将修改后的第一个元素和剩余的元素连接起来
new_text_list[0] = data # 更新列表中的第一个元素
joined_text = "\n".join(new_text_list) # 如果列表中有多个元素,则连接它们
all_texts2.append(joined_text) # 将每个列表的内容添加到 all_texts 中
# 删除空格
joined_text_no_spaces = joined_text.replace(' ', '').replace(' ', '')
all_texts2.append(joined_text_no_spaces) # 将每个列表的内容添加到 all_texts 中
return all_texts1,all_texts2 #all_texts1要额外用gpt all_text2直接返回结果
def find_sentences_with_keywords(data, keywords, follow_up_keywords):
@ -208,13 +213,15 @@ def find_sentences_with_keywords(data, keywords, follow_up_keywords):
full_text = ' '.join(split_sentences[start_index:]).strip()
# pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+(\.\d+)*(\s|\.|、)?)'
pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)'
data=re.sub(pattern,'',full_text)
# data=re.sub(pattern,'',full_text)
data = re.sub(pattern, '', full_text).replace(' ','').strip()
sentences2.append(data) # 存储有后续关键词的情况
i = end_index if found_next_section else len(split_sentences)
else:
# pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+(\.\d+)*(\s|\.|、)?)'
pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)'
data = re.sub(pattern, '', sentence).replace('\n','').strip()
# data = re.sub(pattern, '', sentence).replace('\n','').strip()
data = re.sub(pattern, '', sentence).replace('\n', '').replace(' ','').strip()
sentences1.append(data) # 存储没有后续关键词的情况
i += 1
else:

View File

@ -28,7 +28,7 @@ def qianwen_long(file_id, user_query):
completion = client.chat.completions.create(
model="qwen-long",
top_p=0.5,
temperature=0.5,
temperature=0.4,
messages=[
{
'role': 'system',

View File

@ -1,11 +1,11 @@
1.该招标文件的工程名称(项目名称)是?招标编号招标人是招标代理机构是请按json格式给我提供信息键名分别是'工程名称','招标编号','招标人','招标代理机构',若存在未知信息,在对应的键值中填'未知'。
1.该招标文件的项目名称(或工程名称)是?招标编号(或项目编号)招标人是招标代理机构是请按json格式给我提供信息键名分别是'项目名称','招标编号','招标人','招标代理机构',若存在未知信息,在对应的键值中填'未知'。
#该招标文件的工程概况(或项目概况)是?招标范围是?招标控制价(可指代投标限价、投资概算金额、工程概算金额、合同估算价,但非监理费用)是?该项目的计划工期(监理服务期)是该项目是否接受联合体投标请按json格式给我提供信息键名分别为'工程概况','招标范围','招标控制价','计划工期','是否接受联合体投标',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知''是否接受联合体投标'的键值仅限于'是'、'否'、'未知'。
2.该招标文件的工程概况或项目概况招标范围是请按json格式给我提供信息键名分别为'工程概况','招标范围',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知'。
#该招标文件的项目概况(或工程概况)是?招标范围是?招标控制价(可指代投标限价、投资概算金额、工程概算金额、合同估算价,但非监理费用)是?该项目的计划工期(监理服务期)是该项目是否接受联合体投标请按json格式给我提供信息键名分别为'工程概况','招标范围','招标控制价','计划工期','是否接受联合体投标',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知''是否接受联合体投标'的键值仅限于'是'、'否'、'未知'。
2.该招标文件的工程概况或项目概况招标范围是请按json格式给我提供信息键名分别为'项目概况','招标范围',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知'。
3.该招标文件的招标控制价可指代投标限价、投资概算金额、工程概算金额、合同估算价但非监理费用请按json格式给我提供信息键名为'招标控制价',若存在未知信息,在对应的键值中填'未知'。
4.投标文件递交截止日期是递交方式是请按json格式给我提供信息键名分别是'投标文件递交截止日期','递交方式',若存在未知信息,在对应的键值中填'未知'。
4.投标文件递交截止日期是递交方式是请按json格式给我提供信息键名分别是'投标文件递交截止日期','投标文件递交方式',若存在未知信息,在对应的键值中填'未知'。
5.招标人和招标代理机构的联系方式是请按json格式给我提供信息键名分别是'招标人联系方式''招标代理机构联系方式',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知'。
@ -24,7 +24,7 @@
#8.该招标文件的电子招标文件获取方式是请按原文段落全部完整内容回答以json的格式给我提供信息键名是'电子招标文件获取方式',若存在未知信息,在对应的键值中填'未知'。
9.该招标文件对投标人准备和参加投标活动发生的费用是如何规定的请以json的格式给我提供信息键名是'费用承担',若存在未知信息,在对应的键值中填'未知'。
9.该招标文件对投标人准备和参加投标活动发生的费用是如何规定的请以json的格式给我提供信息键名是'投标费用承担',若存在未知信息,在对应的键值中填'未知'。
10.求澄清的招标文件截止时间是请以json的格式给我提供信息键名是'投标人要求澄清招标文件的截止时间',若存在未知信息,在对应的键值中填'未知'。

View File

@ -1,12 +1,12 @@
1.该招标文件的项目名称是项目编号或招标编号采购人或招标人采购代理机构或招标代理机构请按json格式给我提供信息键名分别是'项目名称','项目编号','采购人','采购代理机构',若存在未知信息,在对应的键值中填'未知'。
1.该招标文件的项目名称是项目编号或招标编号采购人或招标人采购代理机构或招标代理机构请按json格式给我提供信息键名分别是'项目名称','项目编号','招标人','招标代理机构',若存在未知信息,在对应的键值中填'未知'。
2.该招标文件的项目概况是项目基本情况是请按json格式给我提供信息键名分别为'项目概况','项目基本情况',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,而嵌套键值必须与原文保持一致,若存在未知信息,在对应的键值中填'未知'。
3.该招标文件的最高限价或招标控制价请按json格式给我提供信息键名为'招标控制价',若存在未知信息,在对应的键值中填'未知'。
4.投标文件或响应文件递交截止时间是递交地点或方式请按json格式给我提供信息键名分别是'投标文件递交截止日期','递交地点'(或'递交方式',若存在未知信息,在对应的键值中填'未知'。
4.投标文件或响应文件递交截止时间是递交地点或方式请按json格式给我提供信息键名分别是'投标文件递交截止日期','投标文件递交地点'(或'投标文件递交方式',若存在未知信息,在对应的键值中填'未知'。
5.采购人招标人和采购代理机构或招标代理机构的联系方式是请按json格式给我提供信息键名分别是'采购人联系方式''采购代理机构联系方式',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知'。
5.采购人招标人和采购代理机构或招标代理机构的联系方式是请按json格式给我提供信息键名分别是'招标人联系方式''招标代理机构联系方式',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知'。
6.该招标文件的信息公示媒介在哪请按json格式给我提供信息键名是'信息公示媒介',若存在未知信息,在对应的键值中填'未知'。
@ -14,13 +14,17 @@
8.该项目的投标有效期或响应文件有效期是什么请按json格式给我提供信息键名是'投标有效期',若存在未知信息,在对应的键值中填'未知'。
9.该招标文件对投标人准备和参加投标活动发生的费用是如何规定的请以json的格式给我提供信息键名是'费用承担',若存在未知信息,在对应的键值中填'未知'。
9.该招标文件对投标人(或供应商)准备和参加投标活动发生的费用是如何规定的请以json的格式给我提供信息键名是'投标费用承担',若存在未知信息,在对应的键值中填'未知'。
10.求澄清的招标文件截止时间是请以json的格式给我提供信息键名是'投标人要求澄清招标文件的截止时间',若存在未知信息,在对应的键值中填'未知'。
10.招标人(或招标代理机构)对招标文件的澄清(或答疑)的截止时间是请以json的格式给我提供信息键名是'澄清招标文件的截止时间',若存在未知信息,在对应的键值中填'未知'。
11.该文档要求扣留的质量保证金百分比是多少请以json格式给我提供信息键名为'质量保证金',如果没有则以'未知'填充。
12.该项目是否接受联合体投标请按json格式给我提供信息键名为'是否接受联合体投标''是否接受联合体投标'的键值仅限于'是'、'否'、'未知'。
13.该招标文件中对投标文件中偏离项的要求或内容是怎样的请以json格式给我提供信息外层键名为'偏离',请不要回答具体的技术参数,若存在未知信息,在对应的键值中填'未知'。

View File

@ -0,0 +1,9 @@
#pdf提取之后的提示词调用普通通译千问
#请你依据以上信息回答,是否允许分包? 是否需要递交投标保证金是否有履约保证金履约担保是否有招标代理服务费请按json格式给我提供信息键名分别为'是否允许分包','是否递交投标保证金','是否有履约保证金','是否有招标代理服务费',键值仅限于'是','否','未知'。
1.该招标文件对于分包的要求是怎样的请按json格式给我提供信息键名为'分包',若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。。
2.根据招标文件第二章投标人须知该项目投标保证金或磋商保证金的内容或要求是什么请按json格式给我提供信息外层键名为"投标保证金"(或"磋商保证金"),若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
3.该招标文件对于投标保证金的退还相关的规章办法是怎样的请按json格式给我提供信息键名为'退还投标保证金',若存在嵌套信息,嵌套内容键名以文档中对应字段命名。
4.根据投标人须知前附表,该项目对于履约保证金(担保)的内容或要求是怎样的请按json格式给我提供信息外层键名为"履约保证金",若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
5.本项目的招标代理服务费或中标服务费、成交服务费的相关内容是怎样的请按json格式给我提供信息外层键名为'招标代理服务费',若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
6.该招标文件对于踏勘现场的内容或要求是怎样的请按json格式给我提供信息外层键名为"踏勘现场",若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。
7.该招标文件对于投标预备会或投标答疑会内容是怎样的请按json格式给我提供信息外层键名为"投标预备会"(或"投标答疑会"),若需要以嵌套键值对返回结果,那么嵌套键名为你对相应要求的总结,而对应键值需要完全与原文保持一致。

View File

@ -37,7 +37,7 @@ def find_exists(truncate_file, required_keys):
else:
relevant_text = text[start_index:]
relevant_text=re.sub(r'\s+', '', relevant_text) #删除换行符 空格
print(relevant_text)
# print(relevant_text)
# Custom logic for "服务要求"
matched_requirements = []
punctuation = r"[,。?!、;:,.?!]*"
@ -60,7 +60,7 @@ def find_exists(truncate_file, required_keys):
def generate_queries(truncate_file, required_keys):
key_list = find_exists(truncate_file, required_keys)
queries = []
user_query_template = "这是一份货物标中采购要求部分的内容,请告诉我\"{}\"是什么请以json格式返回结果外层键名是\"{}\",内层键值对中的键是你对该要求的总结,而值需要完全与原文保持一致,不可擅自总结删减,注意你无需回答具体设备的技术要求"
user_query_template = "这是一份货物标中采购要求部分的内容,请告诉我\"{}\"是什么请以json格式返回结果外层键名是\"{}\",内层键值对中的键是你对该要求的总结,而值需要完全与原文保持一致,不可擅自总结删减,注意你无需回答具体设备的技术要求"
for key in key_list:
query_base = user_query_template.format(key, key)
other_keys = [k for k in key_list if k != key]
@ -68,7 +68,7 @@ def generate_queries(truncate_file, required_keys):
query_base += "也不需要回答\"{}\"中的内容,".format("\"\"".join(other_keys))
query_base += "若相关要求不存在,在键值中填'未知'"
queries.append(query_base)
print(query_base)
# print(query_base)
return queries

View File

@ -1,14 +1,70 @@
# -*- encoding:utf-8 -*-
import json
import threading
import time
from flask_app.main.json_utils import clean_json_string, nest_json_under_key,rename_outer_key, combine_json_results
from flask_app.main.json_utils import clean_json_string
from flask_app.main.基础信息整合 import judge_consortium_bidding
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, qianwen_long
from flask_app.main.判断是否分包等 import merge_json_to_list, read_questions_from_judge
from flask_app.货物标.提取采购需求main import fetch_procurement_reqs
def aggregate_basic_info(baseinfo_list):
"""
将基础信息列表中的数据进行合并和分类
参数
- baseinfo_list (list): 包含多个基础信息的列表
返回
- dict: 合并和分类后的基础信息字典
"""
key_groups = {
"招标人/代理信息": ["招标人", "招标人联系方式", "招标代理机构", "招标代理机构联系方式"],
"项目信息": ["项目名称", "项目编号", "项目概况", "项目基本情况", "招标控制价", "投标竞争下浮率"],
"采购要求": ["技术要求","商务要求","服务要求","其他要求"],
"关键时间/内容": [
"投标文件递交截止日期",
"澄清招标文件的截止时间",
"投标有效期",
"信息公示媒介"
],
"保证金相关": ["质量保证金"],
"其他信息": [
"是否退还投标文件",
"投标费用承担",
"招标代理服务费"
]
}
combined_data = {}
relevant_keys_detected = set()
# 合并所有基础信息并收集相关键
for baseinfo in baseinfo_list:
combined_data.update(baseinfo)
relevant_keys_detected.update(baseinfo.keys())
# 动态调整键组
dynamic_key_handling(key_groups, relevant_keys_detected)
# 按键组分类并嵌套
for group_name, keys in key_groups.items():
group_data = {key: combined_data.get(key, "未提供") for key in keys}
combined_data[group_name] = group_data
# Optionally remove original keys to avoid duplication
for key in keys:
combined_data.pop(key, None)
return combined_data
def dynamic_key_handling(key_groups, detected_keys):
# 检查和调整键组配置
for key in detected_keys:
if "投标保证金" in key or "履约保证金" in key:
# print(key)
if "保证金" in key:
key_groups["保证金相关"].append(key)
elif "是否接受联合体" in key:
key_groups["项目信息"].append(key)
@ -18,106 +74,81 @@ def dynamic_key_handling(key_groups, detected_keys):
key_groups["项目信息"].append(key)
elif "踏勘现场" in key:
key_groups["其他信息"].append(key)
elif "投标预备会" in key:
elif "投标预备会" in key or "投标答疑会" in key:
key_groups["其他信息"].append(key)
elif "偏离" in key:
key_groups["其他信息"].append(key)
def aggregate_basic_info(baseinfo_list):
"""
将基础信息列表中的数据进行合并和分类
elif "递交方式" in key or "递交地点" in key:
key_groups["关键时间/内容"].append(key)
参数
- baseinfo_list (list): 包含多个基础信息的列表
def get_base_info(baseinfo_file_path):
file_id = upload_file(baseinfo_file_path)
baseinfo_file_path='flask_app/static/提示词/基本信息货物标.txt'
# baseinfo_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\基本信息货物标.txt'
questions = read_questions_from_file(baseinfo_file_path)
more_query = "请你根据招标文件信息,回答以下问题:是否组织踏勘现场?是否召开投标预备会(或投标答疑会)?是否退还投标文件?是否允许分包? 是否需要递交投标保证金或磋商保证金是否需要提交履约保证金或履约担保是否有招标代理服务费或中标、成交服务费请按json格式给我提供信息键名分别为'是否组织踏勘现场','是否召开投标预备会'(或'是否召开投标答疑会','是否退还投标文件',是否允许分包','是否递交投标保证金'(或'是否递交磋商保证金','是否提交履约保证金','是否有招标代理服务费',键值仅限于'','','未知',若存在矛盾信息,请回答'未知'"
questions.append(more_query)
baseinfo_results = multi_threading(questions, "", file_id, 2) # 1代表使用百炼rag 2代表使用qianwen-long
baseinfo_list = [res for _, res in baseinfo_results] if baseinfo_results else []
chosen_numbers, merged = merge_json_to_list(clean_json_string(baseinfo_list.pop()))
baseinfo_list.append(merged)
返回
- list: 合并和分类后的基础信息列表
"""
combined_baseinfo_list = []
key_groups = {
"招标人/代理信息": ["招标人", "招标人联系方式", "招标代理机构", "招标代理机构联系方式"],
"项目信息": ["工程名称", "招标编号", "工程概况", "招标范围", "招标控制价", "投标竞争下浮率"],
"关键时间/内容": [
"投标文件递交截止日期",
"递交方式",
"投标人要求澄清招标文件的截止时间",
"投标有效期",
"评标结果公示媒介"
],
"保证金相关": ["质量保证金", "退还投标保证金"],
"其他信息": [
"重新招标、不再招标和终止招标",
"是否退还投标文件",
"费用承担"
]
}
judge_file_path = 'flask_app/static/提示词/是否相关问题.txt'
# judge_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\是否相关问题货物标.txt'
judge_questions = read_questions_from_judge(judge_file_path, chosen_numbers)
# print(judge_questions)
judge_consortium = judge_consortium_bidding(baseinfo_list) # 通过招标公告判断是否接受联合体投标
combined_data = {}
relevant_keys_detected = set()
if judge_consortium:
judge_consortium_question = (
"该招标文件对于联合体投标的要求是怎样的请按json格式给我提供信息"
"外层键名为'联合体投标要求',其中有一个嵌套键值对为:\"是否接受联合体投标\":\"\""
)
judge_questions.append(judge_consortium_question)
res2 = multi_threading(judge_questions, "", file_id, 2) # 调用千问-long
if not res2:
print("基础信息整合: multi_threading error!")
else:
for question, response in res2:
baseinfo_list.append(clean_json_string(response))
return baseinfo_list
# 合并所有基础信息并收集相关键
for baseinfo in baseinfo_list:
json_data = clean_json_string(baseinfo)
combined_data.update(json_data)
relevant_keys_detected.update(json_data.keys())
def combine_basic_info(baseinfo_file_path, procurement_file_path):
baseinfo_list = []
temp_list = []
procurement_reqs = {}
# 定义一个线程函数来获取基础信息
def get_base_info_thread():
nonlocal temp_list
temp_list = get_base_info(baseinfo_file_path)
# 定义一个线程函数来获取采购需求
def fetch_procurement_reqs_thread():
nonlocal procurement_reqs
procurement_reqs = fetch_procurement_reqs(procurement_file_path)
# 创建并启动获取基础信息的线程
thread1 = threading.Thread(target=get_base_info_thread)
thread1.start()
# 等待一秒后启动获取采购需求的线程
time.sleep(1)
thread2 = threading.Thread(target=fetch_procurement_reqs_thread)
thread2.start()
# 等待两个线程都完成
thread1.join()
thread2.join()
# 合并结果
baseinfo_list += temp_list # temp_list 是一个列表
baseinfo_list.append(procurement_reqs) # procurement_reqs 是一个字典
aggregated_baseinfo = aggregate_basic_info(baseinfo_list)
# 动态调整键组
dynamic_key_handling(key_groups, relevant_keys_detected)
return {"基础信息": aggregated_baseinfo}
# 按键组分类并嵌套
for group_name, keys in key_groups.items():
group_data = {key: combined_data.get(key, "未提供") for key in keys}
combined_json = nest_json_under_key(group_data, group_name)
combined_baseinfo_list.append(combined_json)
return combined_baseinfo_list
def combine_basic_info(knowledge_name,output_folder,clause_path):
# file_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\6.2定版视频会议磋商文件(1)\\6.2定版视频会议磋商文件_1-21.pdf"
# file_id = upload_file(file_path)
# baseinfo_file_path='flask_app/static/提示词/前两章提问总结.txt'
# questions=read_questions_from_file(baseinfo_file_path)
# results = multi_threading(questions, "", file_id, 2) # 1代表使用百炼rag 2代表使用qianwen-long
# if not results:
# print("errror!")
# else:
# # 打印结果
# for question, response in results:
# print(f"Question: {question}")
# print(f"Response: {response}")
baseinfo_combined_res={
"招标人/代理信息": {
"招标人": "黄石临空建设管理有限公司",
"招标人联系方式": {
"名称": "黄石临空建设管理有限公司",
"地址": "大冶市还地桥镇",
"联系人": "王先生",
"电话": "13545510946",
"传真": "未知",
"电子邮件": "未知",
"网址": "未知",
"开户银行": "未知",
"账号": "未知"
},
"招标代理机构": "湖北民成工程项目管理有限公司",
"招标代理机构联系方式": {
"名称": "湖北民成工程项目管理有限公司",
"地址": "大冶市港湖还建楼 20栋二单元 102室",
"联系人": "尹工",
"电话": "18327823905",
"传真": "未知",
"电子邮件": "未知",
"网址": "未知",
"开户银行": "未知",
"账号": "未知"
}
}
}
return {"基础信息":baseinfo_combined_res}
if __name__ == "__main__":
knowledge_name = "ztb"
output_folder="C:\\Users\Administrator\Desktop\\fsdownload\\3424b7cb-1f85-44b4-a432-44539b870405"
truncate0="C:\\Users\Administrator\Desktop\\fsdownload\\3424b7cb-1f85-44b4-a432-44539b870405\\ztbfile_tobidders_notice_table.pdf"
clause_path="C:\\Users\Administrator\Desktop\\fsdownload\\3424b7cb-1f85-44b4-a432-44539b870405\\clause1.json"
res=combine_basic_info(knowledge_name,output_folder,clause_path)
print(json.dumps(res,ensure_ascii=False,indent=4))
start_time=time.time()
baseinfo_file_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zboutpub\\merged_baseinfo.pdf"
# procurement_file_path = "C:\\Users\\Administrator\\Desktop\\fsdownload\\b4601ea1-f087-4fa2-88ae-336ad4d8e1e9\\tmp\\ztbfile_procurement.pdf"
procurement_file_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zboutpub\\广水农商行门禁控制主机及基础验证设备采购项目——磋商文件定稿三次_procurement.pdf"
res = combine_basic_info(baseinfo_file_path, procurement_file_path)
print(json.dumps(res, ensure_ascii=False, indent=4))
end_time=time.time()
print("elasped time:"+str(end_time-start_time))

View File

@ -66,9 +66,24 @@ def postprocess(data):
# 递归处理顶层数据
return {key: convert_dict(val) if isinstance(val, dict) else val for key, val in data.items()}
def get_technical_requirements(file_id):
user_query1 = "这是一份货物标中采购要求部分的内容请告诉我需要采购的系统货物如果有采购清单请直接根据清单上的货物名称给出结果若没有采购清单你要从文中摘取需要采购的系统货物采购需求中可能包含层次关系如大系统中包含若干子系统你需要保留这种层次关系给出系统货物名称请以json格式返回外层键名为\"采购需求\",嵌套键名为对应的系统名称或货物名称,需与原文保持一致,无需给出采购数量和单位,如有未知内容,在对应键值处填\"未知\""
user_query1 = """
这是一份货物标中采购要求部分的内容请告诉我需要采购的系统或货物如果有采购清单请直接根据清单上的货物名称给出结果若没有采购清单你要从文中摘取需要采购的系统或货物采购需求中可能包含层次关系如某大系统中包含若干货物那么需要用嵌套键值对表示这种关系请以json格式返回最外层键名为'采购需求'嵌套键名为对应的系统名称或货物名称需与原文保持一致无需给出采购数量和单位如有未知内容在对应键值处填'未知'以下为示例输出
{
"采购需求": {
"交通信号灯": {},
"交通监控视频子系统": {
"高清视频抓拍像机":{},
"补光灯":{}
},
"交通诱导子系统": {},
"电子警察子系统": {},
}
}
"""
res = qianwen_long(file_id, user_query1)
cleaned_res = clean_json_string(res)
print(res)
keys_list ,no_keys_added= generate_key_paths(cleaned_res['采购需求']) # 提取需要采购的货物清单
if '采购需求' in cleaned_res:
cleaned_res['技术要求'] = cleaned_res.pop('采购需求')
@ -78,9 +93,10 @@ def get_technical_requirements(file_id):
user_query_template = "这是一份货物标中采购要求部分的内容,请你给出\"{}\"的技术参数或采购要求和数量请以json格式返回结果外层键名为\"{}\", 键值对中的键是你对该要求的总结,而值需要完全与原文保持一致,不可擅自总结删减。"
queries = []
for key in keys_list:
# 替换 user_query2 中的 "网络硬盘录像机" 为当前 key
new_query = user_query_template.format(key, key)
print(new_query)
# 将键中的 '.' 替换为 '下的'
modified_key = key.replace('.', '下的')
# 使用修改后的键填充第一个占位符,原始键填充第二个占位符
new_query = user_query_template.format(modified_key, key)
queries.append(new_query)
results = multi_threading(queries, "", file_id, 2)
technical_requirements = []
@ -124,7 +140,7 @@ def test_all_files_in_folder(input_folder, output_folder):
if __name__ == "__main__":
truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output1\\2-招标文件_procurement.pdf"
truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\zboutpub\\广水农商行门禁控制主机及基础验证设备采购项目——磋商文件(定稿)(三次)_procurement.pdf"
file_id = upload_file(truncate_file)
res=get_technical_requirements(file_id)
json_string = json.dumps(res, ensure_ascii=False, indent=4)

View File

@ -23,18 +23,17 @@ def fetch_procurement_reqs(truncate_file):
business_requirements = future_business.result()
# 构建最终的嵌套结构,确保四个键平级
procurement_reqs = {
"采购要求": {
"技术要求": technical_requirements.get("技术要求", {}),
"商务要求": business_requirements.get("商务要求", {}),
"服务要求": business_requirements.get("服务要求", {}),
"其他要求": business_requirements.get("其他要求", {})
}
}
return procurement_reqs
if __name__ == "__main__":
output_folder = "C:\\Users\\Administrator\\Desktop\\货物标\\货物标output"
file_path="C:\\Users\\Administrator\\Desktop\\货物标\\output1\\2-招标文件2020年广水市中小学教师办公电脑系统及多媒体“班班通”设备采购安装项目_procurement.pdf"
# file_path="C:\\Users\\Administrator\\Desktop\\货物标\\output1\\2-招标文件2020年广水市中小学教师办公电脑系统及多媒体“班班通”设备采购安装项目_procurement.pdf"
file_path="C:\\Users\\Administrator\\Desktop\\货物标\\output1\\磋商文件_procurement.pdf"
res=fetch_procurement_reqs(file_path)
print(json.dumps(res, ensure_ascii=False, indent=4))

View File

@ -8,6 +8,7 @@
#这是一份货物标中采购要求部分的内容请你给出所需的设备名称以及设备的具体型号参数要求请以json格式返回结果外层键名为采购要求。
这是一份货物标中采购要求部分的内容,请你给出\"{}\"的具体型号参数要求和数量请以json格式返回结果外层键名为\"{}\", 键值对中的键是你对该要求的总结,而值需要完全与原文保持一致,不可擅自总结删减。
#这是一份货物标中采购要求部分的内容,请你给出"网络硬盘录像机"的具体型号参数要求请以json格式返回结果外层键名为"网络硬盘录像机",键值对中的键是你对该要求的总结,而值需要完全与原文保持一致,不可擅自总结删减。
user_query1 = "这是一份货物标中采购要求部分的内容请告诉我需要采购的系统货物如果有采购清单请直接根据清单上的货物名称给出结果若没有采购清单你要从文中摘取需要采购的系统货物采购需求中可能包含层次关系如大系统中包含若干子系统你需要保留这种层次关系给出系统货物名称请以json格式返回外层键名为\"采购需求\",嵌套键名为对应的系统名称或货物名称,需与原文保持一致,无需给出采购数量和单位,如有未知内容,在对应键值处填\"未知\"。"
这是一份货物标中采购要求部分的内容请告诉我商务要求和其他要求是什么请以json格式返回结果外层键名分别是"商务要求"和"其他要求",内层键值对中的键是你对该要求的总结,而值需要完全与原文保持一致,不可擅自总结删减,注意你无需回答具体设备的技术要求,若相关要求不存在,在键值中填"未知"。

View File

@ -11,6 +11,46 @@ from docx import Document
#如果当前段落有序号,则向下匹配直接遇到相同的序号样式
#如果当前段落无序号,则向下匹配序号,把若干同类的序号都摘出来。
def preprocess_paragraphs(paragraphs):
processed = []
index = 0
while index < len(paragraphs):
current_text = paragraphs[index].text.strip()
# 检测是否为空白行
if current_text == '':
# 确保有前一行和后一行
if index > 0 and index + 1 < len(paragraphs):
prev_text = paragraphs[index - 1].text.strip()
# print(prev_text)
next_text = paragraphs[index + 1].text.strip()
# print(next_text)
# print("------------------------------")
# 检查前一行是否不以指定标点结尾
if not prev_text.endswith(('', ',', '', '!', '?')):
# 检查后一行是否以序号开头
if re.match(r'^\s*([(]\d+[)]|[A-Za-z]\.\s*|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)', prev_text) \
and not re.match(r'^\s*([(]\d+[)]|[A-Za-z]\.\s*|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)',next_text) \
and len(prev_text)>30:
# 合并前一行和后一行
merged_text = prev_text + next_text
# print(merged_text)
# print("---------------------------------")
if processed:
# 用合并后的文本替换已处理的前一行
processed[-1] = merged_text
else:
processed.append(merged_text)
# 跳过后一行
index += 2
continue
else:
# 非空白行,直接添加到处理后的列表
processed.append(current_text)
index += 1
return processed
def extract_text_with_keywords(doc_path, keywords, follow_up_keywords):
from collections import OrderedDict
from docx import Document
@ -117,9 +157,11 @@ def extract_text_with_keywords(doc_path, keywords, follow_up_keywords):
return current_index
processed_paragraphs = preprocess_paragraphs(doc.paragraphs)
index = 0
while index < len(doc.paragraphs):
index = extract_from_text(doc.paragraphs[index].text.strip(), index)
while index < len(processed_paragraphs):
index = extract_from_text(processed_paragraphs[index].strip(), index)
index += 1
return extracted_paragraphs
@ -175,7 +217,9 @@ def clean_dict_datas(extracted_contents, keywords,excludes): #让正则表达
else:
# 如果没有找到关键词,保留原文本
cleaned_text = data
all_texts1.append(cleaned_text) # 将处理后的文本添加到结果列表
# 删除空格
cleaned_text_no_spaces = cleaned_text.replace(' ', '').replace(' ', '')
all_texts1.append(cleaned_text_no_spaces) # 将处理后的文本添加到结果列表
else:
new_text_list=preprocess_text_list(text_list)
@ -186,7 +230,9 @@ def clean_dict_datas(extracted_contents, keywords,excludes): #让正则表达
# 将修改后的第一个元素和剩余的元素连接起来
new_text_list[0] = data # 更新列表中的第一个元素
joined_text = "\n".join(new_text_list) # 如果列表中有多个元素,则连接它们
all_texts2.append(joined_text) # 将每个列表的内容添加到 all_texts 中
# 删除空格
joined_text_no_spaces = joined_text.replace(' ', '').replace(' ', '')
all_texts2.append(joined_text_no_spaces) # 将每个列表的内容添加到 all_texts 中
return all_texts1,all_texts2 #all_texts1要额外用gpt all_text2直接返回结果
@ -262,8 +308,16 @@ def extract_table_with_keywords(data, keywords, follow_up_keywords):
continue
# 分割句子保证句子完整性按标点符号和序号分割eg:(?=\d+\.\d+):匹配诸如 1.1、2.2 之类的序号,并在序号前进行分割。
split_sentences = re.split(r'(?<=[。!?\!\?])|(?=\d+\.\d+)|(?=\d+[\\.])|(?=[(]\d+[)])', item)
split_sentences = re.split(
r'(?<=[。!?!?\?])|' # 在中文句号、感叹号或问号后分割
r'(?=\d+\.\d+)|' # 在类似1.1的数字序号前分割
r'(?=\d+[\s、\.])|' # 在数字后跟空格、顿号或点号前分割
r'(?=[(]\d+[)])|' # 在括号包围的数字前分割
r'(?=[A-Za-z]\.\s*)|' # 在字母加点如A.、a.)前分割
r'(?=[A-Za-z]?\d+\s*(?:\.\s*\d+)*)|' # 在可选字母加数字或多级编号前分割
r'(?=[一二三四五六七八九十]+、)', # 在中文数字加顿号(如一、、二、)前分割
item
)
i = 0
while i < len(split_sentences):
sentence = split_sentences[i].strip()
@ -292,15 +346,15 @@ def extract_table_with_keywords(data, keywords, follow_up_keywords):
else:
full_text = ' '.join(split_sentences[start_index:]).strip()
# pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+(\.\d+)*(\s|\.|、)?)'
pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)'
full_text = re.sub(pattern, '', full_text)
pattern = r'^\s*([(]\d+[)]|[A-Za-z]\.\s*|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)'
full_text = re.sub(pattern, '', full_text).replace(' ','').strip() #删去了空格
sentences2.append(full_text) # 存储有后续关键词的情况
i = end_index if found_next_section else len(split_sentences)
else:
# 没有后续关键词的情况
# pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+(\.\d+)*(\s|\.|、)?)'
pattern = r'^\s*([(]\d+[)]|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)'
cleaned_sentence = re.sub(pattern, '', sentence).replace('\n', '').strip()
pattern = r'^\s*([(]\d+[)]|[A-Za-z]\.\s*|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、|)?|[一二三四五六七八九十]+、)'
cleaned_sentence = re.sub(pattern, '', sentence).replace('\n', '').replace(' ','').strip()
sentences1.append(cleaned_sentence) # 存储没有后续关键词的情况
i += 1
else:

View File

@ -225,6 +225,7 @@ if __name__ == "__main__":
# truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\2-招标文件_evaluation_method.pdf"
# truncate_file = "C:\\Users\\Administrator\\Desktop\\货物标\\output2\\2-招标文件统计局智能终端二次招标_evaluation_method.pdf"
# truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\广水市妇幼招标文件最新W改_evaluation_method.pdf"
truncate_file = "C:\\Users\\Administrator\\Desktop\\货物标\\output2\\622二次视频会议磋商文件_evaluation_method.pdf"
# truncate_file = "C:\\Users\\Administrator\\Desktop\\fsdownload\\2d481945-1f82-45a5-8e56-7fafea4a7793\\ztbfile_evaluation_method.pdf"
truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\招标文件实高电子显示屏_evaluation_method.pdf"
res = combine_evaluation_standards(truncate_file)
print(json.dumps(res, ensure_ascii=False, indent=4))

View File

@ -1,3 +1,5 @@
import glob
import fitz
from PyPDF2 import PdfReader, PdfWriter
import re # 导入正则表达式库
@ -5,7 +7,6 @@ import os # 用于文件和文件夹操作
from flask_app.main.format_change import docx2pdf
def clean_page_content(text, common_header):
# 首先删除抬头公共部分
if common_header: # 确保有公共抬头才进行替换
@ -21,7 +22,8 @@ def clean_page_content(text, common_header):
return text
#PYPDF2版本
# PYPDF2版本
def extract_common_header(pdf_path):
pdf_document = PdfReader(pdf_path)
headers = []
@ -58,7 +60,7 @@ def extract_common_header(pdf_path):
return '\n'.join(common_headers)
#fitz库版本
# fitz库版本
# def extract_common_header(pdf_path):
# doc = fitz.open(pdf_path)
# headers = []
@ -103,6 +105,7 @@ def convert_to_pdf(file_path):
return docx2pdf(file_path)
return file_path
def judge_file_exist(original_path, new_suffix):
# 提取目录路径和原始文件名
directory = os.path.dirname(original_path)
@ -121,6 +124,8 @@ def judge_file_exist(original_path, new_suffix):
return new_file_path
else:
return None
#合并PDF
def merge_pdfs(paths, output_path):
pdf_writer = PdfWriter()
last_page_text = None # 用于存储上一个PDF的最后一页的文本
@ -148,6 +153,8 @@ def merge_pdfs(paths, output_path):
# 写入合并后的PDF到文件
with open(output_path, 'wb') as out:
pdf_writer.write(out)
def merge_and_cleanup(output_pdf_path, suffix_to_merge):
another_file_path = judge_file_exist(output_pdf_path, suffix_to_merge)
if another_file_path:
@ -156,6 +163,7 @@ def merge_and_cleanup(output_pdf_path, suffix_to_merge):
os.remove(another_file_path)
print(f"文件 {another_file_path} 已删除。")
def process_files(file_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix):
pdf_path = convert_to_pdf(file_path)
result = extract_pages(pdf_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix)
@ -167,6 +175,7 @@ def process_files(file_path, output_folder, begin_pattern, begin_page, end_patte
return result
return None
def process_input(input_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
@ -196,7 +205,7 @@ def process_input(input_path, output_folder, begin_pattern, begin_page, end_patt
return generated_files
#默认逻辑是start_page匹配上就不再设置了一般不匹配上目录的原因是设置了begin_page=5但是匹配'第一章 招标公告'的时候start_page可能会错误匹配到目录。
# 默认逻辑是start_page匹配上就不再设置了一般不匹配上目录的原因是设置了begin_page=5但是匹配'第一章 招标公告'的时候start_page可能会错误匹配到目录。
def extract_pages_generic(pdf_document, begin_pattern, end_pattern, begin_page, common_header, exclusion_pattern=None,
output_suffix="normal"):
start_page = None
@ -231,7 +240,8 @@ def extract_pages(pdf_path, output_folder, begin_pattern, begin_page, end_patter
if output_suffix == "tobidders_notice":
exclusion_pattern = re.compile(r'文件的构成|文件的组成|须对应|需对应|须按照|需按照|须根据|需根据')
start_page, mid_page, end_page = extract_pages_tobidders_notice(pdf_document, begin_pattern, end_pattern,
begin_page, common_header,exclusion_pattern)
begin_page, common_header,
exclusion_pattern)
if start_page is None or mid_page is None or end_page is None:
print(f"first: {output_suffix} 未找到起始或结束页在文件 {pdf_path} 中!尝试备用提取策略。")
return extract_pages_twice_tobidders_notice(pdf_path, output_folder, output_suffix, common_header)
@ -244,7 +254,8 @@ def extract_pages(pdf_path, output_folder, begin_pattern, begin_page, end_patter
# 原有的处理逻辑保持不变
if output_suffix == "qualification1":
exclusion_pattern = re.compile(r'文件的构成|文件的组成|须对应|需对应|须按照|需按照|须根据|需根据')
start_page, end_page = extract_pages_generic(pdf_document, begin_pattern, end_pattern, begin_page, common_header, exclusion_pattern,output_suffix)
start_page, end_page = extract_pages_generic(pdf_document, begin_pattern, end_pattern, begin_page,
common_header, exclusion_pattern, output_suffix)
if start_page is None or end_page is None:
print(f"first: {output_suffix} 未找到起始或结束页在文件 {pdf_path} 中!尝试备用提取策略。")
return extract_pages_twice(pdf_path, output_folder, output_suffix, common_header)
@ -255,7 +266,9 @@ def extract_pages(pdf_path, output_folder, begin_pattern, begin_page, end_patter
print(f"Error processing {pdf_path}: {e}")
return None
def extract_pages_tobidders_notice(pdf_document, begin_pattern, end_pattern, begin_page, common_header,exclusion_pattern):
def extract_pages_tobidders_notice(pdf_document, begin_pattern, end_pattern, begin_page, common_header,
exclusion_pattern):
start_page = None
mid_page = None
end_page = None
@ -274,6 +287,7 @@ def extract_pages_tobidders_notice(pdf_document, begin_pattern, end_pattern, beg
break
return start_page, mid_page, end_page
def get_patterns_for_procurement():
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(?:服务|项目|商务).*?要求|'
@ -305,18 +319,21 @@ def get_patterns_for_qualification():
end_pattern_new = re.compile(
r'^附件\s*\d+|^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+', re.MULTILINE)
return begin_pattern_new, end_pattern_new
return begin_pattern_new, end_pattern_new
def get_patterns_for_notice():
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(?:公告|邀请书).*', re.MULTILINE
)
end_pattern = re.compile(
r'^(?:第[一二三四五六七八九十百千]+(?:章|部分)\s*(?:投标人须知|磋商须知|供应商须知)+|(?:一\s*、\s*)?(?:投标人须知|磋商须知|供应商须知)前附表)', re.MULTILINE
r'^(?:第[一二三四五六七八九十百千]+(?:章|部分)\s*(?:投标人须知|磋商须知|供应商须知)+|(?:一\s*、\s*)?(?:投标人须知|磋商须知|供应商须知)前附表)',
re.MULTILINE
)
return begin_pattern, end_pattern
def extract_pages_twice_tobidders_notice(pdf_path, output_folder, output_suffix, common_header): #投标人须知前附表/正文二次提取
def extract_pages_twice_tobidders_notice(pdf_path, output_folder, output_suffix, common_header): # 投标人须知前附表/正文二次提取
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*(?:(?:投标人|磋商|供应商|谈判供应商|磋商供应商)须知前附表)+'
)
@ -332,14 +349,17 @@ def extract_pages_twice_tobidders_notice(pdf_path, output_folder, output_suffix,
return None, None
# 提取第二部分
start_page2 = end_page1 # 第二部分的开始页就是第一部分的结束页
_, end_page2 = extract_pages_generic(pdf_document, end_pattern, end_pattern, start_page2 - 1, common_header,exclusion_pattern)
_, end_page2 = extract_pages_generic(pdf_document, end_pattern, end_pattern, start_page2 - 1, common_header,
exclusion_pattern)
if end_page2 is None:
print(f"second: {output_suffix} 未找到第二部分的结束页在文件 {pdf_path} 中!")
return None, None
# 保存提取的页面
path1 = save_extracted_pages(pdf_document, start_page1, end_page1, pdf_path, output_folder, "tobidders_notice_part1")
path2 = save_extracted_pages(pdf_document, start_page2, end_page2, pdf_path, output_folder, "tobidders_notice_part2")
path1 = save_extracted_pages(pdf_document, start_page1, end_page1, pdf_path, output_folder,
"tobidders_notice_part1")
path2 = save_extracted_pages(pdf_document, start_page2, end_page2, pdf_path, output_folder,
"tobidders_notice_part2")
return path1, path2
@ -347,30 +367,31 @@ def extract_pages_twice(pdf_path, output_folder, output_suffix, common_header):
exclusion_pattern = re.compile(r'文件的构成|文件的组成|须对应|需对应|须按照|需按照|须根据|需根据')
pdf_document = PdfReader(pdf_path)
patterns = None
begin_page=0
begin_page = 0
if output_suffix == "procurement":
patterns = [get_patterns_for_procurement()]
begin_page=5
elif output_suffix == "evaluation_method" or output_suffix=="qualification2" or output_suffix=="qualification3":
begin_page = 5
elif output_suffix == "evaluation_method" or output_suffix == "qualification2" or output_suffix == "qualification3":
patterns = [get_patterns_for_evaluation_method()]
begin_page = 5
elif output_suffix == "qualification1":
patterns = [get_patterns_for_qualification()] # This now returns a tuple of pattern pairs
begin_page = 5
elif output_suffix == "notice":
patterns=[get_patterns_for_notice()]
patterns = [get_patterns_for_notice()]
begin_page = 0
# Try each set of patterns until a valid range is found
for pattern_pair in patterns:
start_page, end_page = extract_pages_generic(pdf_document, pattern_pair[0], pattern_pair[1], begin_page, common_header,
exclusion_pattern,output_suffix)
start_page, end_page = extract_pages_generic(pdf_document, pattern_pair[0], pattern_pair[1], begin_page,
common_header,
exclusion_pattern, output_suffix)
if start_page is not None and end_page is not None:
break
if start_page is None or end_page is None:
if output_suffix == "qualification1":
print(f"second: {output_suffix} 未找到起始或结束页在文件 {pdf_path} 中!")
print("third:尝试提取评分办法章节...")
temp=truncate_pdf_main(pdf_path,output_folder,2,"qualification2")
temp = truncate_pdf_main(pdf_path, output_folder, 2, "qualification2")
if len(temp) > 0:
return temp[0]
else:
@ -381,9 +402,33 @@ def extract_pages_twice(pdf_path, output_folder, output_suffix, common_header):
return save_extracted_pages(pdf_document, start_page, end_page, pdf_path, output_folder, output_suffix)
# def save_extracted_pages(pdf_document, start_page, end_page, pdf_path, output_folder, output_suffix):
# if output_suffix=='notice':
# print(start_page)
# base_file_name = os.path.splitext(os.path.basename(pdf_path))[0]
# output_pdf_path = os.path.join(output_folder, f"{base_file_name}_{output_suffix}.pdf")
# output_doc = PdfWriter()
# for page_num in range(start_page, end_page + 1):
# output_doc.add_page(pdf_document.pages[page_num])
# with open(output_pdf_path, 'wb') as f:
# output_doc.write(f)
# print(f"{output_suffix} 已截取并保存页面从 {start_page} 到 {end_page} 为 {output_pdf_path}")
# return output_pdf_path
def save_extracted_pages(pdf_document, start_page, end_page, pdf_path, output_folder, output_suffix):
base_file_name = os.path.splitext(os.path.basename(pdf_path))[0]
output_pdf_path = os.path.join(output_folder, f"{base_file_name}_{output_suffix}.pdf")
if output_suffix == 'notice' and start_page - 1 >= 0:
before_pdf_path = os.path.join(output_folder, f"{base_file_name}_before.pdf")
before_doc = PdfWriter()
for page_num in range(0, start_page):
before_doc.add_page(pdf_document.pages[page_num])
with open(before_pdf_path, 'wb') as f:
before_doc.write(f)
print(f"已保存页面从 0 到 {start_page - 1}{before_pdf_path}")
output_doc = PdfWriter()
for page_num in range(start_page, end_page + 1):
output_doc.add_page(pdf_document.pages[page_num])
@ -392,6 +437,41 @@ def save_extracted_pages(pdf_document, start_page, end_page, pdf_path, output_fo
print(f"{output_suffix} 已截取并保存页面从 {start_page}{end_page}{output_pdf_path}")
return output_pdf_path
#合并封面+招标公告+投标人须知前附表+须知正文
def merge_selected_pdfs(output_folder, truncate_files, output_path):
"""
合并 output_folder 中以 _before 结尾的 PDF 文件以及 truncate_files 中的指定文件
参数
- output_folder (str): 包含以 _before 结尾的 PDF 文件的文件夹路径
- truncate_files (list): 包含 PDF 文件路径的列表
- output_path (str): 合并后的 PDF 文件保存路径
"""
# 1. 查找 output_folder 中以 _before.pdf 结尾的 PDF 文件
before_pdfs = glob.glob(os.path.join(output_folder, '*_before.pdf'))
print(f"找到 {len(before_pdfs)} 个以 '_before.pdf' 结尾的文件。")
# 2. 获取 truncate_files 中指定的文件索引5、3、4
selected_indices = [5, 3, 4] # 注意索引从0开始
selected_truncate_pdfs = []
for idx in selected_indices:
if idx < len(truncate_files):
selected_truncate_pdfs.append(truncate_files[idx])
print(f"选中 truncate_files[{idx}]: {truncate_files[idx]}")
else:
print(f"truncate_files 列表中没有索引为 {idx} 的元素。")
# 3. 合并所有 PDF 文件
all_pdfs_to_merge = before_pdfs + selected_truncate_pdfs
print(f"总共将要合并的 PDF 文件数量: {len(all_pdfs_to_merge)}")
if not all_pdfs_to_merge:
print("没有找到要合并的 PDF 文件。")
return
# 调用 merge_pdfs 函数
merge_pdfs(all_pdfs_to_merge, output_path)
def truncate_pdf_main(input_path, output_folder, selection, output_suffix="default"):
if selection == 1:
@ -423,17 +503,18 @@ def truncate_pdf_main(input_path, output_folder, selection, output_suffix="defau
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+', re.MULTILINE
)
local_output_suffix = "qualification1"
elif selection ==4: #投标人须知前附表和正文
begin_page=1
elif selection == 4: # 投标人须知前附表和正文
begin_page = 1
begin_pattern = re.compile(
r'^(?:第[一二三四五六七八九十百千]+(?:章|部分)\s*(?:投标人|磋商|供应商|谈判供应商|磋商供应商)须知+|(?:一\s*、\s*)?(?:投标人|磋商|供应商)须知前附表)', re.MULTILINE
r'^(?:第[一二三四五六七八九十百千]+(?:章|部分)\s*(?:投标人|磋商|供应商|谈判供应商|磋商供应商)须知+|(?:一\s*、\s*)?(?:投标人|磋商|供应商)须知前附表)',
re.MULTILINE
)
end_pattern=re.compile(
end_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+', re.MULTILINE
)
local_output_suffix = "tobidders_notice"
elif selection==5:
begin_page=0
elif selection == 5: # 招标公告
begin_page = 0
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(?:公告|邀请书).*'
)
@ -453,21 +534,24 @@ def truncate_pdf_main(input_path, output_folder, selection, output_suffix="defau
return process_input(input_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix)
def truncate_pdf_multiple(input_path, output_folder):
truncate_files = []
for selection in range(1, 6):
files = truncate_pdf_main(input_path, output_folder, selection)
truncate_files.extend(files)
merged_output_path =os.path.join(output_folder,"merged_baseinfo.pdf")
merge_selected_pdfs(output_folder,truncate_files,merged_output_path)
print(merged_output_path)
truncate_files.append(merged_output_path)
return truncate_files
# TODO:交通智能系统和招标(1)(1)文件有问题 sele=4的时候excludsion有问题
if __name__ == "__main__":
input_path = "C:\\Users\\Administrator\\Desktop\\fsdownload\\b4601ea1-f087-4fa2-88ae-336ad4d8e1e9\\ztbfile.pdf"
output_folder = "C:\\Users\\Administrator\\Desktop\\fsdownload\\b4601ea1-f087-4fa2-88ae-336ad4d8e1e9"
files=truncate_pdf_multiple(input_path,output_folder)
input_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\094定稿-湖北工业大学轻武器模拟射击设备采购项目招标文件.pdf"
output_folder = "C:\\Users\\Administrator\\Desktop\\货物标\\zboutpub"
files = truncate_pdf_multiple(input_path, output_folder)
print(files)
# selection = 1 # 例如1 - 商务技术服务要求, 2 - 评标办法, 3 - 资格审查后缀有qualification1或qualification2与评标办法一致 4.投标人须知前附表part1 投标人须知正文part2
# selection = 1 # 例如1 - 商务技术服务要求, 2 - 评标办法, 3 - 资格审查后缀有qualification1或qualification2与评标办法一致 4.投标人须知前附表part1 投标人须知正文part2 5-公告
# generated_files = truncate_pdf_main(input_path, output_folder, selection)

View File

@ -1,4 +1,4 @@
#竞磋 竞谈 磋商 询价 邀请 单一来源
# 竞磋 竞谈 磋商 询价 邀请 单一来源
import json
import time
@ -15,16 +15,21 @@ from flask_app.货物标.无效标和废标和禁止投标整合main import comb
from flask_app.货物标.资格审查main import combine_qualification_review
from flask_app.货物标.评分标准提取main import combine_evaluation_standards
import logging
def get_global_logger(unique_id):
if unique_id is None:
return logging.getLogger() # 获取默认的日志器
logger = logging.getLogger(unique_id)
return logger
logger=None
logger = None
# 创建全局线程池
executor = ThreadPoolExecutor()
def preprocess_files(output_folder, file_path, file_type, unique_id):
logger.info("starting 文件预处理...")
logger.info("output_folder..." + output_folder)
@ -36,25 +41,27 @@ def preprocess_files(output_folder, file_path, file_type, unique_id):
elif file_type == 2: # pdf
pdf_path = file_path
docx_path = pdf2docx(pdf_path) # 将pdf转换为docx以供上传到知识库
elif file_type ==3: #doc
pdf_path=docx2pdf(file_path)
docx_path=pdf2docx(pdf_path)
elif file_type == 3: # doc
pdf_path = docx2pdf(file_path)
docx_path = pdf2docx(pdf_path)
else:
logger.error("Unsupported file type provided. Preprocessing halted.")
return None
# 异步上传知识库
future_knowledge = executor.submit(addfileToKnowledge, docx_path, "招标解析" + unique_id)
# # 异步上传知识库
# future_knowledge = executor.submit(addfileToKnowledge, docx_path, "招标解析" + unique_id)
# 调用截取PDF多次
truncate_files = truncate_pdf_multiple(pdf_path, output_folder) #index: 0->商务技术服务要求 1->评标办法 2->资格审查 3->投标人须知前附表 4->投标人须知正文
truncate_files = truncate_pdf_multiple(pdf_path,
output_folder) # index: 0->商务技术服务要求 1->评标办法 2->资格审查 3->投标人须知前附表 4->投标人须知正文
# 处理各个部分
invalid_docpath = docx_path #docx截取无效标部分
procurement_path = truncate_files[0] #商务技术服务要求
evaluation_method_path = truncate_files[1] #评标办法
qualification_path=truncate_files[2] #资格审查
tobidders_notice_path = truncate_files[4] #投标人须知正文
notice_path=truncate_files[5]
invalid_docpath = docx_path # docx截取无效标部分
procurement_path = truncate_files[0] # 商务技术服务要求
evaluation_method_path = truncate_files[1] # 评标办法
qualification_path = truncate_files[2] # 资格审查
tobidders_notice_path = truncate_files[4] # 投标人须知正文
notice_path = truncate_files[5]
merged_baseinfo_path = truncate_files[6] # 合并封面+招标公告+投标人须知前附表+须知正文
clause_path = convert_clause_to_json(tobidders_notice_path, output_folder) # 投标人须知正文条款pdf->json
logger.info("文件预处理done")
@ -66,23 +73,27 @@ def preprocess_files(output_folder, file_path, file_type, unique_id):
'procurement_path': procurement_path,
'evaluation_method_path': evaluation_method_path,
'qualification_path': qualification_path,
'notice_path':notice_path,
'knowledge_future': future_knowledge, # 返回 Future 对象
'notice_path': notice_path,
# 'knowledge_future': future_knowledge, # 返回 Future 对象
'clause_path': clause_path,
'invalid_docpath': invalid_docpath
'invalid_docpath': invalid_docpath,
'merged_baseinfo_path': merged_baseinfo_path
}
def fetch_project_basic_info(knowledge_name, output_folder, clause_path): # 投标人须知前附表
def fetch_project_basic_info(merged_baseinfo_path, procurement_file_path): # 投标人须知前附表
logger.info("starting基础信息...")
basic_res = combine_basic_info(knowledge_name,output_folder,clause_path)
basic_res = combine_basic_info(merged_baseinfo_path, procurement_file_path)
logger.info("基础信息done")
return basic_res
def fetch_qualification_review(output_folder,qualification_path,notice_path,knowledge_name): #资格审查
def fetch_qualification_review(output_folder, qualification_path, notice_path,merged_baseinfo_path): # 资格审查
logger.info("starting资格审查...")
review_standards_res = combine_qualification_review(output_folder,qualification_path,notice_path, knowledge_name)
review_standards_res = combine_qualification_review(output_folder, qualification_path, notice_path,merged_baseinfo_path)
logger.info("资格审查done")
return review_standards_res
def fetch_evaluation_standards(evaluation_method_path): # 评标细则
logger.info("starting 商务评分和技术评分...")
# 获取评标办法前附表的字典结果
@ -98,7 +109,8 @@ def fetch_evaluation_standards(evaluation_method_path): # 评标细则
"commercial_standards": commercial_standards
}
#TODO:doc文档转换
# TODO:doc文档转换
def fetch_invalid_requirements(invalid_docpath, output_folder):
# 废标项要求:千问
logger.info("starting无效标与废标...")
@ -111,16 +123,18 @@ def fetch_bidding_documents_requirements(clause_path):
logger.info("starting投标文件要求...")
fetch_bidding_documents_requirements_json = extract_from_notice(clause_path, 1)
logger.info("投标文件要求done...")
return {"投标文件要求":fetch_bidding_documents_requirements_json}
return {"投标文件要求": fetch_bidding_documents_requirements_json}
# 开评定标流程
def fetch_bid_opening(clause_path):
logger.info("starting开评定标流程...")
fetch_bid_opening_json = extract_from_notice(clause_path, 2)
logger.info("开评定标流程done...")
return {"开评定标流程":fetch_bid_opening_json}
return {"开评定标流程": fetch_bid_opening_json}
def goods_bid_main(output_folder,file_path, file_type, unique_id):
def goods_bid_main(output_folder, file_path, file_type, unique_id):
global logger
logger = get_global_logger(unique_id)
@ -132,10 +146,19 @@ def goods_bid_main(output_folder,file_path, file_type, unique_id):
with concurrent.futures.ThreadPoolExecutor() as executor:
# 立即启动不依赖 knowledge_name 和 index 的任务
futures = {
'evaluation_standards': executor.submit(fetch_evaluation_standards, processed_data['evaluation_method_path']),
'invalid_requirements': executor.submit(fetch_invalid_requirements, processed_data['invalid_docpath'],output_folder),
'bidding_documents_requirements': executor.submit(fetch_bidding_documents_requirements, processed_data['clause_path']),
'opening_bid': executor.submit(fetch_bid_opening, processed_data['clause_path'])
'evaluation_standards': executor.submit(fetch_evaluation_standards,
processed_data['evaluation_method_path']),
'invalid_requirements': executor.submit(fetch_invalid_requirements, processed_data['invalid_docpath'],
output_folder),
'bidding_documents_requirements': executor.submit(fetch_bidding_documents_requirements,
processed_data['clause_path']),
'opening_bid': executor.submit(fetch_bid_opening, processed_data['clause_path']),
'base_info': executor.submit(fetch_project_basic_info, processed_data['merged_baseinfo_path'],
processed_data['procurement_path']),
'qualification_review': executor.submit(fetch_qualification_review, output_folder,
processed_data['qualification_path'],
processed_data['notice_path'],
processed_data['merged_baseinfo_path']),
}
# 提前处理这些不依赖的任务,按完成顺序返回
@ -148,8 +171,10 @@ def goods_bid_main(output_folder,file_path, file_type, unique_id):
technical_standards = result["technical_standards"]
commercial_standards = result["commercial_standards"]
# 分别返回技术标和商务标
yield json.dumps({'technical_standards': transform_json_values(technical_standards)}, ensure_ascii=False)
yield json.dumps({'commercial_standards': transform_json_values(commercial_standards)}, ensure_ascii=False)
yield json.dumps({'technical_standards': transform_json_values(technical_standards)},
ensure_ascii=False)
yield json.dumps({'commercial_standards': transform_json_values(commercial_standards)},
ensure_ascii=False)
else:
# 处理其他任务的结果
yield json.dumps({key: transform_json_values(result)}, ensure_ascii=False)
@ -157,39 +182,41 @@ def goods_bid_main(output_folder,file_path, file_type, unique_id):
logger.error(f"Error processing {key}: {exc}")
yield json.dumps({'error': f'Error processing {key}: {str(exc)}'}, ensure_ascii=False)
# 只有在需要 knowledge_name 和 index 时才等待 future_knowledge 完成
try:
knowledge_name = "招标解析" + unique_id
index = processed_data['knowledge_future'].result() # 阻塞等待知识库上传任务完成
# 提交依赖 knowledge_name 和 index 的任务
future_dependencies = {
'base_info': executor.submit(fetch_project_basic_info, knowledge_name,
output_folder, processed_data['clause_path']),
'qualification_review': executor.submit(fetch_qualification_review,output_folder, processed_data['qualification_path'],processed_data['notice_path'],knowledge_name),
}
# 按完成顺序返回依赖任务的结果
for future in concurrent.futures.as_completed(future_dependencies.values()):
key = next(k for k, v in future_dependencies.items() if v == future)
try:
result = future.result()
yield json.dumps({key: transform_json_values(result)}, ensure_ascii=False)
except Exception as exc:
logger.error(f"Error processing {key}: {exc}")
yield json.dumps({'error': f'Error processing {key}: {str(exc)}'}, ensure_ascii=False)
except Exception as e:
logger.error(f"Error uploading to knowledge base: {e}")
yield json.dumps({'error': f'Knowledge upload failed: {str(e)}'}, ensure_ascii=False)
# # 只有在需要 knowledge_name 和 index 时才等待 future_knowledge 完成
# try:
# knowledge_name = "招标解析" + unique_id
# index = processed_data['knowledge_future'].result() # 阻塞等待知识库上传任务完成
#
# # 提交依赖 knowledge_name 和 index 的任务
# future_dependencies = {
# 'base_info': executor.submit(fetch_project_basic_info,processed_data['merged_baseinfo_path'], processed_data['procurement_path']),
# 'qualification_review': executor.submit(fetch_qualification_review, output_folder,
# processed_data['qualification_path'],
# processed_data['notice_path'], processed_data['merged_baseinfo_path']),
# }
# # 按完成顺序返回依赖任务的结果
# for future in concurrent.futures.as_completed(future_dependencies.values()):
# key = next(k for k, v in future_dependencies.items() if v == future)
# try:
# result = future.result()
# yield json.dumps({key: transform_json_values(result)}, ensure_ascii=False)
# except Exception as exc:
# logger.error(f"Error processing {key}: {exc}")
# yield json.dumps({'error': f'Error processing {key}: {str(exc)}'}, ensure_ascii=False)
#
# except Exception as e:
# logger.error(f"Error uploading to knowledge base: {e}")
# yield json.dumps({'error': f'Knowledge upload failed: {str(e)}'}, ensure_ascii=False)
# 删除知识索引
deleteKnowledge(index)
# deleteKnowledge(index)
if __name__ == "__main__":
output_folder = "flask_app/static/output/zytest1"
start_time = time.time()
file_type = 1 #1:docx 2:pdf 3:其他
file_type = 1 # 1:docx 2:pdf 3:其他
input_file = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\6.2定版视频会议磋商文件.pdf"
goods_bid_main(output_folder, input_file, file_type, "uuidzyzy11")
end_time = time.time()

View File

@ -214,14 +214,21 @@ def preprocess_value(value):
def generate_questions(input_list):
template = (
"关于'{key}',{value}的内容是怎样的请按json格式给我提供信息键名为'{key}',而键值需要完全与原文保持一致,不要擅自总结、删减,如果存在未知信息,请在对应键值处填'未知'"
"关于{modified_key},{value}的内容是怎样的请按json格式给我提供信息"
"键名为'{original_key}',而键值需要完全与原文保持一致,不要擅自总结、删减,"
"如果存在未知信息,请在对应键值处填'未知'"
)
questions = []
for input_dict in input_list:
for key, value in input_dict.items():
processed_value = preprocess_value(value)
question = template.format(key=key, value=processed_value)
for original_key, value in input_dict.items():
# 将第一个 '.' 替换为 '中的'
if '.' in original_key:
modified_key = original_key.replace('.', '中的', 1)
else:
modified_key = original_key # 如果没有 '.', 保持不变
processed_value = preprocess_value(value) # 假设这是你需要的预处理函数
question = template.format(modified_key=modified_key, original_key=original_key, value=processed_value)
questions.append(question)
return questions
@ -319,7 +326,7 @@ def process_match_keys(match_keys, clause_path_file):
#处理如'符合本采购文件第一章第二款要求'的情况,跳转到指定地方摘取内容
def process_additional_queries(combined_res, match_keys, output_folder, notice_path, knowledge_name):
def process_additional_queries(combined_res, match_keys, output_folder, notice_path, merged_baseinfo_path):
"""
处理额外的查询并更新结果
@ -339,18 +346,21 @@ def process_additional_queries(combined_res, match_keys, output_folder, notice_p
if updated_match_keys != match_keys:
form_response_dict = update_json_data(combined_res, updated_match_keys)
else:
# 招标公告没找到内容,继续问大模型
ques = generate_questions(match_keys)
results = multi_threading(ques, knowledge_name)
for _, response in results:
if response and len(response) > 1:
try:
temp = extract_content_from_json(response[1])
updated_match_keys.append(temp)
except Exception as e:
print(f"形式响应评审Error processing response: {e}")
else:
print(f"形式响应评审Warning: Missing or incomplete response data.")
file_id = upload_file(merged_baseinfo_path)
qianwen_results = multi_threading(ques, "", file_id, 2) # 1代表使用百炼rag 2代表使用qianwen-long
updated_match_keys = [clean_json_string(res) for _, res in qianwen_results] if qianwen_results else []
# results = multi_threading(ques, knowledge_name)
# for _, response in results:
# if response and len(response) > 1:
# try:
# temp = extract_content_from_json(response[1])
# updated_match_keys.append(temp)
# except Exception as e:
# print(f"形式响应评审Error processing response: {e}")
# else:
# print(f"形式响应评审Warning: Missing or incomplete response data.")
form_response_dict = update_json_data(combined_res, updated_match_keys)
@ -408,6 +418,7 @@ if __name__ == "__main__":
output_folder="C:\\Users\\Administrator\\Desktop\\货物标\\zboutpub"
qualification_path = "C:\\Users\\Administrator\\Desktop\\货物标\\output3\\094定稿-湖北工业大学轻武器模拟射击设备采购项目招标文件_qualification2.pdf"
notice_path="C:\\Users\\Administrator\\Desktop\\货物标\\output5\\094定稿-湖北工业大学轻武器模拟射击设备采购项目招标文件_notice.pdf"
knowledge_name = "6.2视频会议docx"
res = combine_qualification_review(output_folder,qualification_path, notice_path,knowledge_name)
# knowledge_name = "6.2视频会议docx"
baseinfo_path="C:\\Users\\Administrator\\Desktop\\货物标\\zboutpub\\merged_baseinfo.pdf"
res = combine_qualification_review(output_folder,qualification_path, notice_path,baseinfo_path)
print(json.dumps(res, ensure_ascii=False, indent=4))