9.19 货物标提取资格审查

This commit is contained in:
zy123 2024-09-19 18:00:24 +08:00
parent c98cd94bcd
commit d4238721ae
9 changed files with 329 additions and 422 deletions

View File

@ -3,65 +3,109 @@ import json
from flask_app.main.json_utils import clean_json_string
from flask_app.main.通义千问long import upload_file, qianwen_long
def combine_technical_and_business(data, target_values1, target_values2):
extracted_data = {} # 根级别存储所有数据
technical_found = False
business_found = False
# def combine_technical_and_business(data, target_values1, target_values2):
# extracted_data = {} # 根级别存储所有数据
# technical_found = False
# business_found = False
#
# def extract_nested(data, parent_key='', is_technical=False, is_business=False):
# nonlocal technical_found, business_found
# if isinstance(data, dict):
# for key, value in data.items():
# current_key = f"{parent_key}.{key}" if parent_key else key
#
# # 检查是否为技术标的内容
# if any(target in key for target in target_values1):
# if not is_technical:
# # 直接存储在根级别
# extracted_data[key] = value
# technical_found = True
# # 标记为技术标内容并停止进一步处理这个分支
# continue
#
# # 检查是否为商务标的内容
# elif any(target in key for target in target_values2):
# if not is_business:
# # 存储在'商务标'分类下
# if '商务标' not in extracted_data:
# extracted_data['商务标'] = {}
# extracted_data['商务标'][key] = value
# business_found = True
# # 标记为商务标内容并停止进一步处理这个分支
# continue
#
# # 如果当前值是字典或列表,且不在技术或商务分类下,继续递归搜索
# if isinstance(value, dict) or isinstance(value, list):
# extract_nested(value, current_key, is_technical, is_business)
#
# elif isinstance(data, list):
# for index, item in enumerate(data):
# extract_nested(item, f"{parent_key}[{index}]", is_technical, is_business)
#
# # 开始从顶级递归搜索
# extract_nested(data)
#
# # 处理未找到匹配的情况
# if not technical_found:
# extracted_data['技术标'] = ''
# if not business_found:
# extracted_data['商务标'] = ''
#
# return extracted_data
def extract_nested(data, parent_key='', is_technical=False, is_business=False):
nonlocal technical_found, business_found
if isinstance(data, dict):
for key, value in data.items():
current_key = f"{parent_key}.{key}" if parent_key else key
def combine_technical_and_business(data, target_values):
extracted_data = {} # 根级别存储所有数据
technical_found = False
business_found = False
# 检查是否为技术标的内容
if any(target in key for target in target_values1):
if not is_technical:
# 直接存储在根级别
extracted_data[key] = value
technical_found = True
# 标记为技术标内容并停止进一步处理这个分支
continue
def extract_nested(data, parent_key='', is_technical=False, is_business=False):
nonlocal technical_found, business_found
if isinstance(data, dict):
for key, value in data.items():
current_key = f"{parent_key}.{key}" if parent_key else key
# 检查是否为商务标的内容
elif any(target in key for target in target_values2):
if not is_business:
# 存储在'商务标'分类下
if '商务标' not in extracted_data:
extracted_data['商务标'] = {}
extracted_data['商务标'][key] = value
business_found = True
# 标记为商务标内容并停止进一步处理这个分支
continue
# 检查是否为技术标的内容
if any(target in key for target in target_values):
if not is_technical:
extracted_data[key] = value
technical_found = True
continue
# 如果当前值是字典或列表,且不在技术或商务分类下,继续递归搜索
if isinstance(value, dict) or isinstance(value, list):
extract_nested(value, current_key, is_technical, is_business)
# 默认其他所有内容都归为商务标
else:
if not is_business:
if '商务标' not in extracted_data:
extracted_data['商务标'] = {}
extracted_data['商务标'][key] = value
business_found = True
continue
elif isinstance(data, list):
for index, item in enumerate(data):
extract_nested(item, f"{parent_key}[{index}]", is_technical, is_business)
if isinstance(value, dict) or isinstance(value, list):
extract_nested(value, current_key, is_technical, is_business)
# 开始从顶级递归搜索
extract_nested(data)
elif isinstance(data, list):
for index, item in enumerate(data):
extract_nested(item, f"{parent_key}[{index}]", is_technical, is_business)
# 处理未找到匹配的情况
if not technical_found:
extracted_data['技术标'] = ''
if not business_found:
extracted_data['商务标'] = ''
extract_nested(data)
return extracted_data
if not technical_found:
extracted_data['技术标'] = ''
if not business_found:
extracted_data['商务标'] = ''
return extracted_data
def combine_evaluation_standards(truncate2):
# 商务标、技术标评分项:千问
file_id = upload_file(truncate2)
user_query_2 = (
"根据该文档中的评标办法前附表,请你列出该文件的技术标,商务标,投标报价评审标准以及它们对应的具体评分要求,若对应内容中存在其他信息,在键名如'技术标'中新增子键名'备注'存放该信息。如果评分内容不是这3个则返回文档中给定的评分内容以及它的评分要求都以json的格式返回结果。请不要回答有关形式、资格、响应性评审标准的内容")
evaluation_res = qianwen_long(file_id, user_query_2)
target_values1 = ['技术标','技术部分','设计', '实施', '方案']
target_values2=['投标报价','商务标','商务部分','报价部分','业绩','信誉','分值','计算公式','信用','人员','资格','奖项','认证','荣誉']
update_json=combine_technical_and_business(clean_json_string(evaluation_res),target_values1,target_values2)
print(evaluation_res)
target_values1 = ['技术标','技术部分','设计', '实施']
# 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

View File

@ -1,31 +0,0 @@
import PyPDF2
import re # 导入正则表达式库
def clean_page_numbers(text):
# 使用正则表达式删除页码
# 假设页码在文本的最开始,最结尾,或中间(如 /129 或 129
cleaned_text = re.sub(r'^\s*\d+\s+', '', text) # 删除开头的页码
cleaned_text = re.sub(r'\s+\d+\s*$', '', cleaned_text) # 删除结尾的页码
cleaned_text = re.sub(r'\s*\/\s*\d+\s*', '', cleaned_text) # 删除形如 /129 的页码
return cleaned_text
def extract_text_by_page(file_path):
result = ""
with open(file_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
num_pages = len(reader.pages)
# print(f"Total pages: {num_pages}")
for page_num in range(num_pages):
page = reader.pages[page_num]
text = page.extract_text()
if text:
cleaned_text = clean_page_numbers(text)
# print(cleaned_text)
result += cleaned_text
# print(f"Page {page_num + 1} Content:\n{cleaned_text}")
else:
print(f"Page {page_num + 1} is empty or text could not be extracted.")
return result
if __name__ == '__main__':
file_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\招标文件正文(1)(1).pdf"
extract_text_by_page(file_path)

View File

View File

@ -0,0 +1,77 @@
import PyPDF2
import re # 导入正则表达式库
from PyPDF2 import PdfReader
def clean_page_content(text, common_header):
# 首先删除抬头公共部分
if common_header: # 确保有公共抬头才进行替换
for header_line in common_header.split('\n'):
if header_line.strip(): # 只处理非空行
# 替换首次出现的完整行
text = re.sub(r'^' + re.escape(header_line.strip()) + r'\n?', '', text, count=1)
# 删除页码 eg:89/129 这个代码分三步走可以把89/129完全删除
text = re.sub(r'^\s*\d+\s*(?=\D)', '', text) # 删除开头的页码,仅当紧跟非数字字符时
text = re.sub(r'\s+\d+\s*$', '', text) # 删除结尾的页码
text = re.sub(r'\s*\/\s*\d+\s*', '', text) # 删除形如 /129 的页码
return text
def extract_common_header(pdf_path):
pdf_document = PdfReader(pdf_path)
headers = []
total_pages = len(pdf_document.pages)
# 确定要读取的页数和起始页
if total_pages == 2:
pages_to_read = 2
start_page = 0
else:
pages_to_read = 3
middle_page = total_pages // 2
start_page = max(0, middle_page - 1)
for i in range(start_page, min(start_page + pages_to_read, total_pages)):
page = pdf_document.pages[i]
text = page.extract_text() or ""
if text:
# 只取每页的前三行
first_lines = text.strip().split('\n')[:3]
headers.append(first_lines)
if len(headers) < 2:
return "" # 如果没有足够的页来比较,返回空字符串
# 寻找每一行中的公共部分
common_headers = []
for lines in zip(*headers):
# 在每一行中寻找公共单词
common_line = set(lines[0].split()).intersection(*[set(line.split()) for line in lines[1:]])
if common_line:
common_headers.append(' '.join(common_line))
return '\n'.join(common_headers)
def extract_text_by_page(file_path):
common_header = extract_common_header(file_path)
result = ""
with open(file_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
num_pages = len(reader.pages)
# print(f"Total pages: {num_pages}")
for page_num in range(num_pages):
page = reader.pages[page_num]
text = page.extract_text()
if text:
cleaned_text = clean_page_content(text,common_header)
print(cleaned_text)
result += cleaned_text
# print(f"Page {page_num + 1} Content:\n{cleaned_text}")
else:
print(f"Page {page_num + 1} is empty or text could not be extracted.")
return result
if __name__ == '__main__':
file_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\ztbfile.pdf"
res=extract_text_by_page(file_path)
# print(res)

View File

@ -1,4 +1,4 @@
from flask_app.main.按页读取pdf import extract_text_by_page
from flask_app.main.读取文件.按页读取pdf import extract_text_by_page
def check_strings_in_pdf(file_path):
judge_list=['施工机械设备', '企业信息登记']

View File

@ -1,229 +1,31 @@
# -*- encoding:utf-8 -*-
import json
import re
def get_patterns_for_qualification():
# begin_pattern 匹配以'资格审查'或'资格性检查'开始的标题,同时允许前面有章节编号和标题
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(资格审查|资格性检查).*', re.MULTILINE)
def combine_technical_and_business(data, target_values1):
extracted_data = {} # 根级别存储所有数据
technical_found = False
business_found = False
# end_pattern 匹配下一个章节的开始或以'附件'加数字开头的页
end_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+|附件\s*\d+', re.MULTILINE)
def extract_nested(data, parent_key='', is_technical=False, is_business=False):
nonlocal technical_found, business_found
if isinstance(data, dict):
for key, value in data.items():
current_key = f"{parent_key}.{key}" if parent_key else key
return begin_pattern, end_pattern
# 检查是否为技术标的内容
if any(target in key for target in target_values1):
if not is_technical:
extracted_data[key] = value
technical_found = True
continue
# 获取编译后的正则表达式
begin_pattern, end_pattern = get_patterns_for_qualification()
# 默认其他所有内容都归为商务标
else:
if not is_business:
if '商务标' not in extracted_data:
extracted_data['商务标'] = {}
extracted_data['商务标'][key] = value
business_found = True
continue
# 示例字符串,用于测试正则表达式是否工作
test_string = """
第一章 资格审查
资格性检查
第二章 实施规范
附件 4 使用说明
"""
if isinstance(value, dict) or isinstance(value, list):
extract_nested(value, current_key, is_technical, is_business)
# 测试 begin_pattern
begin_matches = begin_pattern.findall(test_string)
print("Begin Matches:", begin_matches)
elif isinstance(data, list):
for index, item in enumerate(data):
extract_nested(item, f"{parent_key}[{index}]", is_technical, is_business)
extract_nested(data)
if not technical_found:
extracted_data['技术标'] = ''
if not business_found:
extracted_data['商务标'] = ''
return extracted_data
# 示例数据
data = {
"一包评分标准": {
"报价部分": {
"价格评议": {
"分值": 30,
"评分标准": "对确定资格性和符合性审查合格的投标文件进行价格评议报价分采用低价优先法计算即满足招标文件要求且投标价格最低的投标报价为评标基准价其报价分30分。其他投标人的价格分按照下列公式计算投标报价得分评标基准价/投标报价×30%×100得分保留小数点后2位"
}
},
"商务部分": {
"企业实力": [
{
"分值": 2,
"评分标准": "投标人具有有效期内的CCRC信息安全集成服务资质贰级以上资质证书得2分不具备得0分。原件备查"
},
{
"分值": 2,
"评分标准": "投标人有健全的信息技术运维服务能力并通过ITSS信息技术服务运维标准符合性认证证书贰级以上得2分不具备得0分。原件备查"
},
{
"分值": 2,
"评分标准": "投标人具有ISO 14001环境管理体系认证以及OHSAS 18001职业健康安全管理体系认证具有ISO 20000 IT服务管理体系认证以及ISO 27001信息安全管理体 系认证每满足一项得2分否则不得分。原件备查"
},
{
"分值": 3,
"评分标准": "投标人同时具有省级及以上高新技术企业证书、AAA企业信用等级证书、上 年度纳税信用评价为A级完全满足得3分每缺一项扣1分直至得0分原件备查"
},
{
"分值": 2,
"评分标准": "所投主要产品制造商应具备较强的合同执行诚信度和执行能力要求获得政府权威机构颁发的国家级或省级守合同重信用公示企业国家级得2分省级得1分不提供不得分。原件备查"
},
{
"分值": 2,
"评分标准": "所投主要产品制造商通过CMMI软件能力成熟度模型集成认证认证等级不低于5级得2分4级得1分,其他不得分。(原件备查)"
},
{
"分值": 3,
"评分标准": "投标人近三年以来具有类似项目经验提供中标通知书及合同原件。每提供一个得1分最多得3分。"
},
{
"分值": 2,
"评分标准": "投标人具备GB/T27922五星级服务认证证书的得2分不满足得0分。原件备查"
}
],
"产品制造商实力": [
{
"分值": 2,
"评分标准": "为保证系统的稳定性所投产品制造商应具有完善的工业信息安全应急体系、良好的安全应急能力能被工业信息安全产业发展联盟认定为“工业信息安全应急服务支撑单位”满足得2分否则不得分。原件备查"
},
{
"分值": 2,
"评分标准": "所投主要产品制造商通过CMMI软件能力成熟度模型集成认证认证等级不低于5级得2分4级得1分,其他不得分。(原件备查)"
}
]
},
"技术部分": {
"技术要求": {
"分值": 30,
"评分标准": "投标人所投产品完全满足招标文件要求的30分''指标不符合招标文件技术参数的视为无效投标,针对''指标,投标人须对应招标文件第三章商务技术要求产品参数要求逐条提供包括但不限于第三方产品检测报告、产品技术白皮书、产品官网或公开的技术资料等佐证资料,并加盖产品制造商公章;''指标不满足招标文件第三章商务技术要求产品参数要求的每项扣2分其他非指标性参数不满足招标文件第三章商务技术要求产品参数要求的每项扣1分扣完为止未提供佐证资料的将作产品不响应处理''指标产品参数要求必须提供第三方权威机构检测报告佐证并加盖产品制造商公章,非指标参数提供产品彩页佐证并加盖产品制造商公章即可。",
"备注": "针对''指标,投标人须对应招标文件第三章商务技术要求产品参数要求逐条提供包括但不限于第三方产品检测报告、产品技术白皮书、产品官网或公开的技术资料等佐证资料,并加盖产品制造商公章;''指标产品参数要求必须提供第三方权威机构检测报告佐证并加盖产品制造商公章,非指标参数提供产品彩页佐证并加盖产品制造商公章即可。"
},
"质量保证": {
"分值": 2,
"评分标准": "投标人所投的电子警察系统、信号控制系统如果不是自己生产的需提供制造商出具的授权及满足招标质保要求的售后服务承诺函提供得2分开标时提供授权书及售后服务承诺函原件予以证明否则不得分。"
},
"实施方案": {
"分值": 5,
"评分标准": "投标人对于本项目的实施保障方案中包含①施工组织方案、②施工协调经验、③进度计划、④质量保障、⑤工期保障五个方案的可行性、合理性、实用性等方面进行综合性评分每个实施方案清晰、完整、合理、可行的得1分。"
},
"实施团队": {
"分值": 9,
"评分标准": "1、投标人拟派项目经理具有机电工程专业壹级注册建造师资格及安全生产考核合格证书B证并同时具备相关专业高级工程师职称的得2分其他不得分2、项目技术负责人具备相关专业高级工程师职称及高级信息系统项目管理师证书的得2分其他不得分3、项目管理团队中的施工员、质量员、安全员C证、材料员、资料员须具有中级或以上职称完全满足得5分每缺一个扣1分直至得0分以上人员不可兼任提供证书复印件、身份证、劳动合同及社保证明文件加盖公章"
},
"运维服务": {
"分值": 2,
"评分标准": "投标人应对维护期给出合理、完善的运维及售后服务方案。包括售后服务团队管理、运维服务内容日常运作、服务咨询、巡检保养、主动监测、故障修复、特殊保障和升级优化、备件方案、运维服务流程、运维管理服务方案清晰、完整、合理、可行2分服务方案较完整1分服务方案不完整不明确得0分。"
},
"培训方案": {
"分值": 2,
"评分标准": "投标人应针对本项目制定全面、可操作性强的技术培训方案有具体的培训时间、人员安排的2分其它得0分。"
}
}
},
"二包评分标准": {
"报价部分": {
"价格评议": {
"分值": 30,
"评分标准": "对确定资格性和符合性审查合格的投标文件进行价格评议报价分采用低价优先法计算即满足招标文件要求且投标价格最低的投标报价为评标基准价其报价分30分。其他投标人的价格分按照下列公式计算投标报价得分评标基准价/投标报价×30%×100得分保留小数点后2位"
}
},
"商务部分": {
"企业实力": [
{
"分值": 3,
"评分标准": "投标人具有三级交通运输企业安全生产标准化建设等级证明的得1分具有二级交通运输企业安全生产标准化建设等级证明的得2分具有一级交通运输企业安全生产标准化建设等级证明的得3分。提供相关证明材料加盖公章"
},
{
"分值": 3,
"评分标准": "投标人具有ISO 14001环境管理体系认证以及OHSAS 18001职业健康安全管理体系认证具有ISO 09001质量管理体系认证全部满足得3分每满足一项得1分否则不得分。原件备查"
},
{
"分值": 4,
"评分标准": "投标人具有AAA企业信用等级证书且在有效期内的、上年度纳税信用评价为A级完全满足得4分每缺一项扣2分直至得0分提供相关证明材料"
},
{
"分值": 8,
"评分标准": "投标人近三年以来具有类似项目经验提供中标通知书及合同原件。每提供一个得2分最多得8分。"
},
{
"分值": 9,
"评分标准": "1、投标人拟派项目经理具有高级工程师职称的得2分其他不得分2、项目技术负责人具备相关专业高级工程师职称得2分其他不得分3、项目管理团队中的施工员、质量员、安全员C证、材料员、资料员须具有中级或以上职称完全满足得5分每缺一个扣1分直至得0分以上人员不可兼任提供证书复印件、身份证、劳动合同及社保证明文件加盖公章"
}
]
},
"技术部分": {
"技术要求": {
"分值": 20,
"评分标准": "投标人所投产品完全满足招标文件要求的20分''指标低于招标文件第三章商务技术要求产品参数要求的每项扣3分扣完为止。"
},
"质量保证措施": {
"分值": 8,
"评分标准": "投标人提供的用于本项目的质量保障措施,符合采购要求,并包含以下几点:(1)质量管理目标;(2)质量管理规章制度;(3)质量管理方案;(4)质量承诺。每一项2分完全满足得8分缺一项扣2分直至得0分。"
},
"售后服务承诺": {
"分值": 12,
"评分标准": "1、投标人承诺对招标人提供优先供货服务以满足招标人的紧急需求(2)投标人承诺接到故障报修24小时内提供解决方案并开始组织实施(3)投标人承诺现场施工降低污染文明施工每一项5分。完全满足得12分缺一项扣4分直至得0分。"
},
"培训方案": {
"分值": 3,
"评分标准": "投标人应针对本项目制定全面、可操作性强的技术培训方案有具体的培训时间、人员安排的3分其它得0分。"
}
}
},
"三包评分标准": {
"报价部分": {
"分值": 40,
"评分标准": "对确定资格性和符合性审查合格的投标文件进行价格评议报价分采用低价优先法计算即满足招标文件要求且投标价格最低的投标报价为评标基准价其报价分40分。其他投标人的价格分按照下列公式计算投标报价得分评标基准价/投标报价×40%×100得分保留小数点后2位"
},
"技术指标": {
"分值": 40,
"评分标准": "1供应商的技术投标书的响应性、符合性、完整性及编制质量在技术参数*号项、普通项全部满足的得满分40分2技术参数中*号项一项不满足的扣2分扣完为止3技术参数中普通项一项不满足扣1分扣完为止4复制招标文件技术要求作为实际响应数据或投标响应数据无对应支持文件的其技术响应将按负偏离处理。"
},
"服务保障": {
"分值": 10,
"评分标准": "投标人应对维护期给出合理、完善的运维及售后服务方案。包括售后服务团队管理、运维服务内容日常运作、服务咨询、巡检保养、主动监测、故障修复、特殊保障和升级优化、备件方案、运维服务流程、运维管理服务方案清晰、完整、合理、可行2分服务方案较完整1分服务方案不完整不明确得0分。"
},
"业绩证明": {
"分值": 5,
"评分标准": "投标人近三年以来具有类似项目经验提供中标通知书及合同原件。每提供一个得2.5分最多得5分。"
},
"投标文件的规范性": {
"分值": 2,
"评分标准": "投标文件的制作质量、文档编排、规范性、方便查阅性等进行综合比较优得2分良得1分。"
},
"售后服务": {
"分值": 3,
"评分标准": "产品故障响应省内24小时、省外48小时内到达现场处理并修复且有完善售后服务网络和巡检方案的得3分不完善售后服务酌情扣分。"
}
}
}
# 转换字典
def get_evaluation_standards(truncate_file):
include = ['一包', '二包', '三包', '四包', '五包']
target_values1 = ['技术', '设计', '实施', '方案']
updated_jsons = {}
for key in data.keys():
if any(item in key for item in include):
inner_dict = data[key]
# 将处理后的结果存储到updated_jsons中每个包名为键
updated_jsons[key] = combine_technical_and_business(inner_dict, target_values1)
# 将updated_jsons转换为JSON格式
evaluation_combined_res = json.dumps(updated_jsons, ensure_ascii=False, indent=4)
return evaluation_combined_res
res=get_evaluation_standards("1")
print(res)
# 测试 end_pattern
end_matches = end_pattern.findall(test_string)
print("End Matches:", end_matches)

View File

@ -1,131 +1,89 @@
# -*- encoding:utf-8 -*-
import json
from flask_app.main.通义千问long import upload_file, qianwen_long
from flask_app.main.json_utils import clean_json_string
def combine_technical_and_business(data, target_values):
extracted_data = {} # 根级别存储所有数据
technical_found = False
business_found = False
def combine_technical_and_business(data, target_values1, target_values2):
extracted_data = {} # 根级别存储所有数据
technical_found = False
business_found = False
def extract_nested(data, parent_key='', is_technical=False, is_business=False):
nonlocal technical_found, business_found
if isinstance(data, dict):
for key, value in data.items():
current_key = f"{parent_key}.{key}" if parent_key else key
def extract_nested(data, parent_key='', is_technical=False, is_business=False):
nonlocal technical_found, business_found
if isinstance(data, dict):
for key, value in data.items():
current_key = f"{parent_key}.{key}" if parent_key else key
# 检查是否为技术标的内容
if any(target in key for target in target_values):
if not is_technical:
extracted_data[key] = value
technical_found = True
continue
# 检查是否为技术标的内容
if any(target in key for target in target_values1): #模糊匹配
if not is_technical:
# 直接存储在根级别
extracted_data[key] = value
technical_found = True
# 标记为技术标内容并停止进一步处理这个分支
continue
# 默认其他所有内容都归为商务标
else:
if not is_business:
if '商务标' not in extracted_data:
extracted_data['商务标'] = {}
extracted_data['商务标'][key] = value
business_found = True
continue
# 检查是否为商务标的内容
elif any(target in key for target in target_values2):
if not is_business:
# 存储在'商务标'分类下
if '商务标' not in extracted_data:
extracted_data['商务标'] = {}
extracted_data['商务标'][key] = value
business_found = True
# 标记为商务标内容并停止进一步处理这个分支
continue
if isinstance(value, dict) or isinstance(value, list):
extract_nested(value, current_key, is_technical, is_business)
# 如果当前值是字典或列表,且不在技术或商务分类下,继续递归搜索
if isinstance(value, dict) or isinstance(value, list):
extract_nested(value, current_key, is_technical, is_business)
elif isinstance(data, list):
for index, item in enumerate(data):
extract_nested(item, f"{parent_key}[{index}]", is_technical, is_business)
elif isinstance(data, list):
for index, item in enumerate(data):
extract_nested(item, f"{parent_key}[{index}]", is_technical, is_business)
extract_nested(data)
# 开始从顶级递归搜索
extract_nested(data)
if not technical_found:
extracted_data['技术标'] = ''
if not business_found:
extracted_data['商务标'] = ''
# 处理未找到匹配的情况
if not technical_found:
extracted_data['技术标'] = ''
if not business_found:
extracted_data['商务标'] = ''
return extracted_data
return extracted_data
#如果外键直接是'评分因素',应该这个函数可以提取其中内容。
def process_data_based_on_key(data, word):
# 获取字典的键列表
keys = list(data.keys())
# 检查键的数量是否为1并且该键是否包含指定的词
if len(keys) == 1 and word in keys[0]:
# 返回内层的字典
return data[keys[0]]
# 如果条件不满足,则返回原始字典
return data
def get_evaluation_standards(truncate_file):
file_id = upload_file(truncate_file)
user_query = "根据该文档中的评标办法前附表,请你列出该文件的技术标,商务标,投标报价评审标准以及它们对应的具体评分要求,若对应内容中存在其他信息,在键名如'技术标'中新增子键名'备注'存放该信息。如果评分内容不是这3个则返回文档中给定的评分内容以及它的评分要求都以json的格式返回结果。请不要回答有关形式、资格、响应性评审标准的内容"
user_query = "根据该文档中的评标办法前附表或者评分标准表,请你列出该文件的技术标,商务标,投标报价评审标准以及它们对应的具体评分要求,外层键名分别为'技术标','商务标','投标报价'。如果评分内容不是这3个则返回文档中给定的评分内容以及它的评分要求都以json的格式返回结果,如果该采购活动有多个包,则最外层键名为对应的包名。请不要回答有关资格审查的内容"
evaluation_res = qianwen_long(file_id, user_query)
cleaned_evaluation_res = clean_json_string(evaluation_res)
result_data = process_data_based_on_key(cleaned_evaluation_res, '评分')
include = ['一包', '二包', '三包', '四包', '五包']
target_values1 = ['技术标', '技术部分', '设计', '实施', '方案']
target_values2 = ['投标报价', '商务标', '商务部分', '报价部分', '业绩', '信誉', '分值', '计算公式', '信用', '人员',
'资格', '奖项', '认证', '荣誉']
target_values = ['技术', '设计', '实施']
updated_jsons = {}
for key in cleaned_evaluation_res.keys():
if any(item in key for item in include):
inner_dict = cleaned_evaluation_res[key]
# 将处理后的结果存储到updated_jsons中每个包名为键
updated_jsons[key] = combine_technical_and_business(inner_dict, target_values1, target_values2)
# 检查是否有外层键匹配include列表
if any(key for key in result_data if any(included in key for included in include)):
# 有匹配的项,处理这些项
for key in result_data:
if any(item in key for item in include):
inner_dict = result_data[key]
updated_jsons[key] = combine_technical_and_business(inner_dict, target_values)
else:
# 没有匹配的项,对整个字典运行
updated_jsons = combine_technical_and_business(result_data, target_values)
# 将updated_jsons转换为JSON格式
evaluation_combined_res = json.dumps(updated_jsons, ensure_ascii=False, indent=4)
return evaluation_combined_res
def combine_technical_and_business(data, target_values1, target_values2):
extracted_data = {} # 根级别存储所有数据
technical_found = False
business_found = False
def extract_nested(data, parent_key='', is_technical=False, is_business=False):
nonlocal technical_found, business_found
if isinstance(data, dict):
for key, value in data.items():
current_key = f"{parent_key}.{key}" if parent_key else key
# 检查是否为技术标的内容
if any(target in key for target in target_values1):
if not is_technical:
# 直接存储在根级别
extracted_data[key] = value
technical_found = True
# 标记为技术标内容并停止进一步处理这个分支
continue
# 检查是否为商务标的内容
elif any(target in key for target in target_values2):
if not is_business:
# 存储在'商务标'分类下
if '商务标' not in extracted_data:
extracted_data['商务标'] = {}
extracted_data['商务标'][key] = value
business_found = True
# 标记为商务标内容并停止进一步处理这个分支
continue
# 如果当前值是字典或列表,且不在技术或商务分类下,继续递归搜索
if isinstance(value, dict) or isinstance(value, list):
extract_nested(value, current_key, is_technical, is_business)
elif isinstance(data, list):
for index, item in enumerate(data):
extract_nested(item, f"{parent_key}[{index}]", is_technical, is_business)
# 开始从顶级递归搜索
extract_nested(data)
# 处理未找到匹配的情况
if not technical_found:
extracted_data['技术标'] = ''
if not business_found:
extracted_data['商务标'] = ''
return extracted_data
if __name__ == "__main__":
truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\招标文件(107国道)_evaluation_method.pdf"
truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\竞争性谈判文件(3)_evaluation_method.pdf"
res=get_evaluation_standards(truncate_file)
cleaned_res=clean_json_string(res).get("")
print(res)

View File

@ -18,6 +18,8 @@ def clean_page_content(text, common_header):
text = re.sub(r'\s*\/\s*\d+\s*', '', text) # 删除形如 /129 的页码
return text
def extract_common_header(pdf_path):
pdf_document = PdfReader(pdf_path)
headers = []
@ -53,16 +55,19 @@ def extract_common_header(pdf_path):
return '\n'.join(common_headers)
def is_pdf_or_doc(filename):
# 判断文件是否为PDF或Word文档
return filename.lower().endswith(('.pdf', '.doc', '.docx'))
def convert_to_pdf(file_path):
# 假设 docx2pdf 函数已经被定义,这里仅根据文件扩展名来决定是否需要转换
if file_path.lower().endswith(('.doc', '.docx')):
return docx2pdf(file_path)
return file_path
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)
@ -79,13 +84,15 @@ def process_input(input_path, output_folder, begin_pattern, begin_page, end_patt
generated_files.append(output_pdf_path)
elif os.path.isfile(input_path) and input_path.endswith(".pdf"):
# 处理单个PDF文件
output_pdf_path = extract_pages(input_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix)
output_pdf_path = extract_pages(input_path, output_folder, begin_pattern, begin_page, end_pattern,
output_suffix)
if output_pdf_path:
generated_files.append(output_pdf_path)
else:
print("提供的路径既不是文件夹也不是PDF文件。")
return generated_files
def extract_pages_generic(pdf_document, begin_pattern, end_pattern, begin_page, common_header, exclusion_pattern=None):
start_page = None
end_page = None
@ -101,19 +108,22 @@ def extract_pages_generic(pdf_document, begin_pattern, end_pattern, begin_page,
break
return start_page, end_page
def extract_pages(pdf_path, output_folder, begin_pattern, begin_page, end_pattern, output_suffix):
try:
common_header = extract_common_header(pdf_path)
pdf_document = PdfReader(pdf_path)
start_page, end_page = extract_pages_generic(pdf_document, begin_pattern, end_pattern, begin_page, common_header)
start_page, end_page = extract_pages_generic(pdf_document, begin_pattern, end_pattern, begin_page,
common_header)
if start_page is None or end_page is None:
print(f"未找到起始或结束页在文件 {pdf_path} 中!尝试备用提取策略。")
print(f"first: {output_suffix} 未找到起始或结束页在文件 {pdf_path} 中!尝试备用提取策略。")
return 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)
except Exception as e:
print(f"Error processing {pdf_path}: {e}")
return None
def get_patterns_for_procurement():
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(?:服务|项目|商务).*?要求|'
@ -123,6 +133,7 @@ def get_patterns_for_procurement():
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+', re.MULTILINE)
return begin_pattern, end_pattern
def get_patterns_for_evaluation_method():
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(磋商|谈判|评标|评定|评审)(方法|办法).*', re.MULTILINE)
@ -130,17 +141,49 @@ def get_patterns_for_evaluation_method():
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+', re.MULTILINE)
return begin_pattern, end_pattern
def get_patterns_for_qualification():
# 原始匹配逻辑
begin_pattern_original = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(资格审查).*', re.MULTILINE)
end_pattern_original = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+', re.MULTILINE)
# 新匹配逻辑
begin_pattern_new = re.compile(
r'^资格性检查', re.MULTILINE)
end_pattern_new = re.compile(
r'^附件\s*\d+', re.MULTILINE)
return (begin_pattern_original, end_pattern_original), (begin_pattern_new, end_pattern_new)
def extract_pages_twice(pdf_path, output_folder, output_suffix, common_header):
exclusion_pattern = re.compile(r'文件的构成|文件的组成')
exclusion_pattern = re.compile(r'文件的构成|文件的组成|须对应|需对应|须按照|需按照|须根据|需根据')
pdf_document = PdfReader(pdf_path)
patterns = None
if output_suffix == "procurement":
begin_pattern, end_pattern = get_patterns_for_procurement()
elif output_suffix == "evaluation_method":
begin_pattern, end_pattern = get_patterns_for_evaluation_method()
start_page, end_page = extract_pages_generic(pdf_document, begin_pattern, end_pattern, 5, common_header, exclusion_pattern)
patterns = [get_patterns_for_procurement()]
elif output_suffix == "evaluation_method" or output_suffix=="qualification2":
patterns = [get_patterns_for_evaluation_method()]
elif output_suffix == "qualification":
patterns = get_patterns_for_qualification() # This now returns a tuple of pattern pairs
# 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], 5, common_header,
exclusion_pattern)
if start_page is not None and end_page is not None:
break
if start_page is None or end_page is None:
print(f"twice: 未找到起始或结束页在文件 {pdf_path} 中!")
return ""
if output_suffix == "qualification":
print(f"second: {output_suffix} 未找到起始或结束页在文件 {pdf_path} 中!")
print("third:尝试提取评分办法章节...")
return truncate_pdf_main(pdf_path,output_folder,2,"qualification2")
else:
print(f"second: {output_suffix} 未找到起始或结束页在文件 {pdf_path} 中!")
return ""
return save_extracted_pages(pdf_document, start_page, end_page, pdf_path, output_folder, output_suffix)
@ -152,51 +195,65 @@ def save_extracted_pages(pdf_document, start_page, end_page, pdf_path, output_fo
output_doc.add_page(pdf_document.pages[page_num])
with open(output_pdf_path, 'wb') as f:
output_doc.write(f)
print(f"已截取并保存页面从 {start_page}{end_page}{output_pdf_path}")
print(f"{output_suffix} 已截取并保存页面从 {start_page}{end_page}{output_pdf_path}")
return output_pdf_path
def truncate_pdf_main(input_path, output_folder, selection):
def truncate_pdf_main(input_path, output_folder, selection, output_suffix="default"):
if selection == 1:
# 更新的正则表达式以匹配"第x章"和"第x部分",考虑到可能的空格和其他文字
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(?:服务|项目|商务).*?要求|'
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?采购.*',
# r'^[一二三四五六七八九十百千]+、\s*采购清单',
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?采购.*'
)
begin_page = 5
end_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+'
)
output_suffix = "procurement"
local_output_suffix = "procurement"
elif selection == 2:
begin_pattern=re.compile(
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(磋商|谈判|评标|评定|评审)(方法|办法).*'
)
begin_page = 5
end_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+'
)
output_suffix = "evaluation_method"
local_output_suffix = "evaluation_method"
elif selection == 3:
begin_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(资格审查).*'
)
begin_page = 5
end_pattern = re.compile(
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+'
)
local_output_suffix = "qualification"
else:
print("无效的选择")
return None
# 如果传入的 output_suffix 是 'default',则使用本地生成的 output_suffix
if output_suffix == "default":
output_suffix = local_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):
truncate_files = []
for selection in range(1, 3):
for selection in range(1, 4):
files = truncate_pdf_main(input_path, output_folder, selection)
truncate_files.extend(files)
return truncate_files
#TODO:交通智能系统和招标(1)(1)文件有问题
# TODO:交通智能系统和招标(1)(1)文件有问题
if __name__ == "__main__":
input_path = "C:\\Users\\Administrator\\Desktop\\货物标\\zbfiles\\交通智能运行维护招标文件.pdf"
output_folder = "C:\\Users\\Administrator\\Desktop\\货物标\\output2"
output_folder = "C:\\Users\\Administrator\\Desktop\\货物标\\output3"
# truncate_pdf_multiple(input_path,output_folder)
selection = 2 # 例如1 - 投标人须知前附表, 2 - 评标办法, 3 - 投标人须知正文 4-招标公告-合同条款前
selection = 3 # 例如1 - 商务技术服务要求, 2 - 评标办法, 3 - 资格审查
generated_files = truncate_pdf_main(input_path, output_folder, selection)