zbparse/flask_app/main/招标文件解析.py
2024-08-29 16:37:09 +08:00

217 lines
25 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- encoding:utf-8 -*-
import json
import logging
import os
import time
from 截取pdf import truncate_pdf_multiple
from table_content_extraction import extract_tables_main
from 知识库操作 import addfileToKnowledge, deleteKnowledge
from 投标人须知正文条款提取成json文件 import convert_clause_to_json
from json_utils import nest_json_under_key, transform_json_values, combine_json_results
from 无效标和废标和禁止投标整合 import combine_find_invalid
from 投标人须知正文提取指定内容 import extract_from_notice
import concurrent.futures
from 基础信息整合 import project_basic_info
from 资格审查模块 import combine_review_standards
from 商务标技术标整合 import combine_evaluation_standards
from format_change import pdf2docx,docx2pdf
global_logger=None
def get_global_logger(unique_id):
if unique_id is None:
return logging.getLogger() # 获取默认的日志器
logger = logging.getLogger(unique_id)
return logger
#可能有问题pdf转docx导致打勾符号消失
def preprocess_files(output_folder, downloaded_file_path, file_type, unique_id):
# 根据文件类型处理文件路径
global docx_path, pdf_path
if file_type == 1: # docx
docx_path = downloaded_file_path
pdf_path = docx2pdf(docx_path) # 将docx转换为pdf以供后续处理
elif file_type == 2: # pdf
pdf_path = downloaded_file_path
docx_path = pdf2docx(pdf_path) # 将pdf转换为docx以供上传到知识库
# 上传知识库
knowledge_name = "招标解析" + unique_id
index = addfileToKnowledge(docx_path, knowledge_name)
# 调用截取PDF多次
truncate_files = truncate_pdf_multiple(pdf_path, output_folder) # [前附表, 评标办法, 须知正文, 无效标]
# 处理各个部分
truncate0_docpath = pdf2docx(truncate_files[0]) # 投标人须知前附表转docx
invalid_docpath = pdf2docx(truncate_files[3]) if file_type == 2 else downloaded_file_path # 无效标
truncate0_jsonpath = os.path.join(output_folder, "truncate_output.json")
extract_tables_main(truncate0_docpath, truncate0_jsonpath) # 投标人须知前附表docx->json,从表格提取数据
clause_path = convert_clause_to_json(truncate_files[2], output_folder) # 投标人须知正文条款pdf->json
return {
'knowledge_index': index,
'knowledge_name':knowledge_name,
'truncate_files': truncate_files,
'truncate0_jsonpath': truncate0_jsonpath,
'clause_path': clause_path,
'invalid_docpath': invalid_docpath
}
#基本信息
def fetch_project_basic_info(knowledge_name,truncate0,output_folder,clause_path): #投标人须知前附表
global_logger.info("starting基础信息...")
basic_res=project_basic_info(knowledge_name,truncate0,output_folder,clause_path)
global_logger.info("基础信息done")
return basic_res
#形式、响应、资格评审
def fetch_review_standards(truncate1,truncate4,knowledge_name,truncate0_jsonpath,clause_path):
global_logger.info("starting资格审查...")
review_standards_res=combine_review_standards(truncate1,truncate4,knowledge_name,truncate0_jsonpath,clause_path)
global_logger.info("资格审查done")
return review_standards_res
#评分细则
def fetch_evaluation_standards(truncate1): #评标办法前附表
global_logger.info("starting商务标技术标...")
evaluation_standards_res= combine_evaluation_standards(truncate1)
global_logger.info("商务标技术标done")
return evaluation_standards_res
#无效、废标项解析
def fetch_invalid_requirements(invalid_docpath,output_folder,truncate0_jsonpath,clause_path,truncate4):
#废标项要求:千问
global_logger.info("starting无效标与废标...")
find_invalid_res=combine_find_invalid(invalid_docpath, output_folder, truncate0_jsonpath,clause_path,truncate4)
global_logger.info("无效标与废标done...")
return find_invalid_res
#投标文件要求
def fetch_bidding_documents_requirements(clause_path):
global_logger.info("starting投标文件要求...")
fetch_bidding_documents_requirements_json = extract_from_notice(clause_path, 1)
qualify_nested_res = nest_json_under_key(fetch_bidding_documents_requirements_json, "投标文件要求")
global_logger.info("投标文件要求done...")
return qualify_nested_res
#开评定标流程
def fetch_bid_opening(clause_path):
global_logger.info("starting开评定标流程...")
fetch_bid_opening_json=extract_from_notice(clause_path,2)
qualify_nested_res = nest_json_under_key(fetch_bid_opening_json, "开评定标流程")
global_logger.info("开评定标流程done...")
return qualify_nested_res
# def main_processing(output_folder,downloaded_file_path,file_type,unique_id): #file_type=1->docx file_type=2->pdf
# global global_logger
# global_logger= get_global_logger(unique_id)
# # Preprocess files and get necessary data paths and knowledge index
# processed_data = preprocess_files(output_folder, downloaded_file_path, file_type, unique_id)
#
#
# with concurrent.futures.ThreadPoolExecutor() as executor:
# # Submit all tasks to the executor
# futures = {
# 'base_info': executor.submit(fetch_project_basic_info, processed_data['knowledge_name'],
# processed_data['truncate_files'][0], output_folder,
# processed_data['clause_path']),
# 'review_standards': executor.submit(fetch_review_standards, processed_data['truncate_files'][1],processed_data['truncate_files'][4],
# processed_data['knowledge_name'], processed_data['truncate0_jsonpath'],
# processed_data['clause_path']),
# 'evaluation_standards': executor.submit(fetch_evaluation_standards, processed_data['truncate_files'][1]),
# 'invalid_requirements': executor.submit(fetch_invalid_requirements, processed_data['invalid_docpath'],
# output_folder, processed_data['truncate0_jsonpath'],processed_data['clause_path'],processed_data['truncate_files'][4]),
# 'bidding_documents_requirements': executor.submit(fetch_bidding_documents_requirements,
# processed_data['clause_path']),
# 'opening_bid': executor.submit(fetch_bid_opening, processed_data['clause_path'])
# }
#
# comprehensive_responses = []
# # Collect results in the defined order
# for key in ['base_info', 'review_standards', 'evaluation_standards', 'invalid_requirements', 'bidding_documents_requirements','opening_bid']:
# try:
# # Wait for the future to complete and get the result
# result = futures[key].result()
# comprehensive_responses.append(result)
# except Exception as exc:
# global_logger.info(f"Error processing {key}: {exc}")
# # 合并 JSON 结果
# combined_final_result = combine_json_results(comprehensive_responses)
# modified_json = transform_json_values(combined_final_result)
#
# final_result_path = os.path.join(output_folder, "final_result.json")
# with open(final_result_path, 'w', encoding='utf-8') as file:
# json.dump(modified_json, file, ensure_ascii=False, indent=2)
# global_logger.info("final_result.json has been saved")
# deleteKnowledge(processed_data['knowledge_index'])
# return final_result_path
#TODO:{
#目前返回结果:
# "opening_bid": "{<br> \"开评定标流程\": {<br> \"开标\": {<br> \"开标时间和地点\": [<br> \"招标人在本章第4.2.1项规定的投标截止时间(开标时间)在“电子交易平台”上公开进行开标,所有投标人均应当准时在线参加开标。\",<br> \"招标人通过互联网在投标人须知前附表规定的地点组织开标并在投标截止时间30分钟前使用CA数字证书登录“电子交易平台”进入“开标室”选择相应标段作在线开标的准备工作。\",<br> \"投标人应当在能够保证设施设备可靠、互联网畅通的任意地点通过互联网在线参加开标。在投标截止时间前使用加密其投标文件的CA数字证书登录“电子交易平台”进入“开标室”选择所投标段进行签到并实时在线关注招标人的操作情况。5.2开标程序\",<br> \"主持人按下列程序在“电子交易平台”的“开标室”进行在线开标1宣布开标纪律2公布主持人、招标人代表、监标人等有关人员姓名3公布在投标截止时间前投标文件的递交情况4公布投标保证金递交情况5按照投标人须知前附表规定抽取评标基准价下浮值如有规定最高投标限价计算方法的计算并公布最高投标限价如适用当众公布后记录在案6读取已解密的投标文件的内容7公布投标人名称、标段名称、投标保证金的递交情况、投标报价、项目经理姓名及其他内容并生成开标记录8开标结束。\",<br> \"在本章第5.2.16目规定的时间内非因“电子交易平台”原因造成投标文件未解密的视为投标人撤回投标文件。已解密的投标文件少于三个的招标失败已解密的投标文件不少于三个开标继续进行。\"<br> ],<br> \"开标异议\": [<br> \"投标人对开标有异议的,应当在开标过程中提出;招标人当场对异议作出答复,并记入开标记录。异议与答复应通过“开标室”在“异议与答复”菜单以书面形式进行。本处所称异议是指投标人在开标过程中对投标文件提交、投标截止时间、开标程序、开标记录以及投标人和招标人或者投标人相互之间存在利益冲突的情形等提出的质疑。\",<br> \"投标人异议成立的,招标人将及时采取纠正措施,或者提交评标委员会评审确认;投标人异议不成立的,招标人将当场给予解释说明。\"<br> ],<br> \"特殊情况的处置\": [<br> \"因“电子交易平台”系统故障导致无法投标的,交易中心及时通知招标人,招标人视情况决定是否顺延投标截止时间。因投标人自身原因导致无法完成投标的,由投标人自行承担后果。\",<br> \"因“电子交易平台”系统故障导致无法正常开标的,招标人将暂停开标,待系统恢复正常后继续开标。\",<br> \"“电子交易平台”系统故障是指下列情形1系统服务器发生故障无法访问或无法使用系统2系统的软件或数据库出现错误不能进行正常操作3系统发现有安全漏洞有潜在的泄密危险4出现断电、断网事故5其他无法保证招投标过程正常进行的情形。\"<br> ]<br> },<br> \"评标\": {<br> \"评标委员会\": [<br> \"评标由招标人依法组建的评标委员会负责。评标委员会由招标人代表以及有关技术、经济等方面的专家组成。评标委员会成员人数以及技术、经济等方面专家的确定方式见投标人须知前附表。\",<br> \"评标委员会成员有下列情形之一的应当回避1投标人或投标人主要负责人的近亲属2项目主管部门或者行政监督部门的人员3与投标人有经济利益关系可能影响对投标公正评审的4曾因在招标、评标以及其他与招标投标有关活动中从事违法行为而受过行政处罚或刑事处罚的。\",<br> \"定标会招标人原则上应当在定标候选人公示结束后5个工作日内召开定标会如有特殊情况最迟应当在定标候选人公示结束后10个工作日内召开定标会定标会进入公共资源交易中心进行。\",<br> \"定标流程1签到宣读定标委员会成员名单2监督小组监督员宣读定标纪律3招标人代表或招标代理机构人员向定标委员会介绍定标项目相关情况4定标委员成员会有疑问的可以向招标人代表进行提问5阅相关资料6投票7招标人代表或招标代理机构人员进行统计8定标委员会组长宣读得分结果和定标结果定标委员会成员签署定标报告会议结束。\",<br> \"定标原则1组建定标委员会由招标人组建定标委员会负责定标工作按照定标委员会定标法进行定标。定标委员会成员数量为5人招标人的法定代表人或其授权代表为领导班子成员之一应当参加定标会并推荐担任定标会组长主持定标会定标委员会其他成员从招标人组建的定标成员库中随机抽取确定。定标委员会成员与定标候选人有利害关系的应当回避。所有参加定标会的定标委员会成员的意见应有书面记录并由所有定标委员会成员签字确认。2组建定标监督小组由招标人组建定标监督小组对定标委员会的定标活动全过程进行监督定标监督小组由2人组成一般为招标人本单位或上级单位纪检监察人员也可由招标人的法定代表人或主要负责人指定骨干成员参加。定标监督小组有权就定标委员会违反定标规则的行为进行质询。评估是否符合内控机制及价值取向确保定标过程公正、公平。定标前招标人或者招标代理机构在定标前可以介绍项目情况、招标情况、对投标人或者项目负责人的考察、质询情况招标人可以邀请评标专家代表介绍评标情况、专家评审意见及评标结论、提醒注意事项。定标委员会成员有疑问的可以向招标人或者招标代理机构、评标专家提问。\",<br> \"定标办法1定标会成员根据评标委员会提出书面评标报告结合定标候选人的投标报价、商务标、技术标、市场信誉等招标人应当按照充分竞争、合理低价的原则集体讨论后采用简单多数原则进行票决在进入投票范围的定标候选人中以每人投票支持一个定标候选人的方式得票最多且过半数的定标候选人为中标人。当没有定标候选人得票超过半数但有2个定标候选人得票较多时选择得票较多的2个定标候选人按上一轮得票多少的顺序选择在选择第2个定标候选人时出现同票的投标人时所有同票定标候选人一并纳入下一轮的投票范围作为二次投票的范围直至出现得票过半数的定标候选人为止。如果没有2个定标候选人得票较多时重新投票。2定标会由招标人或代理机构的工作人员发放选票、定标会成员填写选票须说明推荐理由并署名定标过程公开、公平、公正。定标会成员按有关规定及招标文件约定的定标方法确定一名中标人。投票定标选票招标项目名称支持的投标人支持理由定标委员签名时间本项目采用“评定分离”方法实施招投标活动。本项目定标办法详见第三章附件定标办法。\",<br> \"定标标准1择优要素。招标人在定标前应对评标委员会评审结果与实际情况进行实质性审查核实重点对投标人的企业实力、企业信誉、履约能力的真实性、准确性、一致性进行核实招标人应如实记录审查核实情况并作为定标参考。在考虑价格因素时招标人应坚持投标人投标报价和其履约能力、服务质量等与招标项目相匹配的原则。企业实力包括资质等级、近几年营业额、过往业绩含业绩影响力、难易程度等方面。企业信誉包括获得各种荣誉、过往业绩履约情况同时应重点关注近几年的不良信息包括建设行政主管部门作出的各种不良处罚以及其他失信记录。对拟派团队履约能力与履约水平考核方式可以考察团队主要负责人类似业绩情况也可以对拟派项目负责人进行答辩。为确保可追溯性答辩工作在有录音、录像场所进行。各项考核动作要针对所有投标人统一进行不宜针对部分投标人进行考核以体现公平原则。在同等条件下择优的相对标准有以下几个方面1投标报价各定标候选人的报价结合其履约能力服务质量等与招标项目相匹配经综合比较价格最合理得优2工程业绩综合比较投标人投标人近五年完成的单项合同额在2000万元以上的装饰装修项目主要比较项目难易程度和项目造价工程业绩总造价高且项目难度大的优于工程业绩总造价低且设计难度小的若总体难度差异不大且造价类似的情况下业绩数量多的优于业绩数量少的3技术方案对项目理解程度高、与本项目针对性强、技术方案完善且合理性相应程度高的企业优于项目理解程度一般、技术方案基本完善且进度控制一般的企业4企业实力企业财务指标良好整体营业收入、资产负债率等的企业优于财务指标一般得企业以水平相同的情况下营业收入的优劣为准5企业获奖近五年指从投标截止日往前推算五年类似项目获得国家级奖项优于获得省级奖项6企业信誉无不良行为记录企业优于有不良行为记录企业不良行为记录较轻企业优于不良行为记录较重企业。定标会在评议时优先进行“比优”无法比优情况下可进行“比劣”“比劣”可参考以下等要素进行1有无串通投标围标以行贿等不正当手段谋取中标行为2有无挂靠以他人名义投标出让或者出租资格、资质证书供他人投标行为投标人在招标人的项目中有无严重违约或重大工程质量安全问题投标人在近一年内经查实有以上行为的不确定为中标人。\"<br> ],<br> \"评标原则\": \"评标活动遵循公平、公正、科学和择优的原则。\",<br> \"评标\": \"评标委员会按照第三章“评标办法”规定的方法、评审因素、标准和程序对投标文件进行评审。第三章“评标办法”没有规定的方法、评审因素和标准,不作为评标依据。\",<br> \"评标结果公示\": \"招标人将自收到评标报告之日起3日内在投标人须知前附表规定的媒介公示中标候选人。公示期不少于3日。投标人或者其他利害关系人对评标结果有异议的应当在评标结果公示期间提出。招标人自收到异议之日起3日内作出答复作出答复前暂停招标投标活动。异议与答复应当通过“电子交易平台”在“异议与答复”菜单以书面形式进行。\",<br> \"履约能力的审查(如有)\": \"如果中标候选人的经营、财务状况发生较大变化或者存在违法行为,招标人认为可能影响其履约能力的,将在发出中标通知书前报行政监督部门后,召集原评标委员会按照招标文件规定的标准和方法审查确认。\"<br> },<br> \"定标\": {<br> \"评标结果\": \"1评标委员会完成评标后应当向招标人提出书面评标报告阐明评标委员会对各投标文件的评审和比较意见并按照招标文件中规定的评标方法在投标报价合格的基础上按照最终得分保留2位小数由高到低推荐定标候选人。定标候选人不少于3家不超过5家。投标人的数量少于或者等于10家时评标委员会推荐的定标候选人数量不超过3家经评标委员会评审符合招标文件要求的定标候选人不足3家时由评标委员会作出是否具备竞争性如具备竞争性可继续推荐定标候选人招标人可继续定标否则招标人应重新招标。定标侯选人进入定标程序。2经评标委员会评审符合招标文件要求的定标候选人不足3家时由评标委员会作出是否具备竞争性如具备竞争性可继续推荐定标候选人招标人可继续定标否则招标人应重新招标。3招标人应当自收到评标报告之日起3日内公示定标候选人公示期不少于3日。对评标结果的异议的提出和处理适用《招标投标法实施条例》第五十四条的规定。评标结果定标候选人公示期间因异议或投诉导致定标候选人少于招标文件规定的数量时招标人继续定标还是在原评标委员会评审的基础上递补定标候选人由招标人在招标文件中明确。评标结果定标候选人公示期间有定标候选人因异议或投诉并查实被取消中标资格时若有效定标候选人不少于3家的不再递补招标人继续定标除评标委员会作出具备竞争性情形外若有效定标候选人少于3家的按投标人得分高低补足至3家。对于递补的定标候选人需在黄石市公共资源交易信息网公示不少于3日。\"<br> }<br> }<br>}"
#}
def main_processing(output_folder, downloaded_file_path, file_type, unique_id):
global global_logger
global_logger = get_global_logger(unique_id)
processed_data = preprocess_files(output_folder, downloaded_file_path, file_type, unique_id)
with concurrent.futures.ThreadPoolExecutor() as executor:
# 提交任务到线程池
futures = {
'base_info': executor.submit(fetch_project_basic_info, processed_data['knowledge_name'],
processed_data['truncate_files'][0], output_folder,
processed_data['clause_path']),
'review_standards': executor.submit(fetch_review_standards, processed_data['truncate_files'][1],
processed_data['truncate_files'][4],
processed_data['knowledge_name'], processed_data['truncate0_jsonpath'],
processed_data['clause_path']),
'evaluation_standards': executor.submit(fetch_evaluation_standards, processed_data['truncate_files'][1]),
'invalid_requirements': executor.submit(fetch_invalid_requirements, processed_data['invalid_docpath'],
output_folder, processed_data['truncate0_jsonpath'],
processed_data['clause_path'], processed_data['truncate_files'][4]),
'bidding_documents_requirements': executor.submit(fetch_bidding_documents_requirements,
processed_data['clause_path']),
'opening_bid': executor.submit(fetch_bid_opening, processed_data['clause_path'])
}
# 逐个提交任务每次提交间隔1秒
for task_name, func, args in futures:
futures[task_name] = executor.submit(func, *args)
time.sleep(1) # 每提交一个任务后暂停1秒
# 根据任务完成的顺序处理和返回结果
for future in concurrent.futures.as_completed(futures.values()):
key = next(key for key, val in futures.items() if val == future) # 获取完成任务的key
try:
result = future.result()
modified_result = transform_json_values({key: result})
yield f"data: {json.dumps(modified_result)}\n\n"
except Exception as exc:
global_logger.info(f"Error processing {key}: {exc}")
yield f"data: {json.dumps({'error': f'Error processing {key}: {str(exc)}'})}\n\n"
deleteKnowledge(processed_data['knowledge_index'])
#TODO:近三年业绩可能是按照投标文件的来的/分模块返回结果/对于上传docx文件进行优化
if __name__ == "__main__":
output_folder = "C:\\Users\\Administrator\\Desktop\\招标文件\\test"
# truncate0 = os.path.join(output_folder, "ztb_tobidders_notice_table.pdf")
# truncate1=os.path.join(output_folder,"ztb_evaluation_method.pdf")
# clause_path = convert_clause_to_json(truncate1, output_folder)
# truncate0_jsonpath = os.path.join(output_folder, "truncate_output3.json")
start_time = time.time()
file_type=2
input_file = "C:\\Users\\Administrator\\Desktop\\招标文件\\test\\zbtest12.docx"
file_path=main_processing(output_folder,input_file,1,"uuidzyzy11")
end_time = time.time()
elapsed_time = end_time - start_time # 计算耗时
print(f"Function execution took {elapsed_time} seconds.")