diff --git a/flask_app/main/start_up.py b/flask_app/main/start_up.py
index e2c8869..f4a3764 100644
--- a/flask_app/main/start_up.py
+++ b/flask_app/main/start_up.py
@@ -56,74 +56,76 @@ def create_logger():
g.logger = logger
+# @app.route('/upload', methods=['POST'])
+# def zbparse():
+# logger=g.logger
+# file_url = validate_request()
+# if isinstance(file_url, tuple): # Check if the returned value is an error response
+# return file_url
+# try:
+# logger.info("starting parsing url:" + file_url)
+# final_json_path, output_folder= download_and_process_file(file_url)
+# if not final_json_path:
+# return jsonify({'error': 'File processing failed'}), 500
+# response = generate_response(final_json_path) # 先获取响应内容
+# # remove_directory(output_folder) # 然后删除文件夹
+# return response # 最后返回获取的响应
+# except Exception as e:
+# logger.error('Exception occurred: ' + str(e)) # 使用全局 logger 记录
+# return jsonify({'error': str(e)}), 500
+
+
+# 流式
@app.route('/upload', methods=['POST'])
def zbparse():
- logger=g.logger
+ logger = g.logger
file_url = validate_request()
if isinstance(file_url, tuple): # Check if the returned value is an error response
return file_url
try:
logger.info("starting parsing url:" + file_url)
- final_json_path, output_folder= download_and_process_file(file_url)
- if not final_json_path:
- return jsonify({'error': 'File processing failed'}), 500
- response = generate_response(final_json_path) # 先获取响应内容
- # remove_directory(output_folder) # 然后删除文件夹
- return response # 最后返回获取的响应
+ return Response(stream_with_context(process_and_stream(file_url)), content_type='text/event-stream')
except Exception as e:
- logger.error('Exception occurred: ' + str(e)) # 使用全局 logger 记录
+ logger.error('Exception occurred: ' + str(e))
return jsonify({'error': str(e)}), 500
-# 流式
-# def zbparse():
-# file_url = validate_request()
-# if isinstance(file_url, tuple): # Check if the returned value is an error response
-# return file_url
-# try:
-# app.logger.info("starting parsing url:" + file_url)
-# return Response(stream_with_context(process_and_stream(file_url)), content_type='text/event-stream')
-# except Exception as e:
-# app.logger.error('Exception occurred: ' + str(e))
-# return jsonify({'error': str(e)}), 500
+#分段返回
+def process_and_stream(file_url):
+ logger = g.logger
+ unique_id = g.unique_id
+ output_folder = f"flask_app/static/output/{unique_id}" # 直接使用全局 unique_id 构建路径
+ filename = "ztbfile"
+ downloaded_filename = os.path.join(output_folder, filename)
+ downloaded_filepath, file_type = download_file(file_url, downloaded_filename)
-# 分段返回
-# def process_and_stream(file_url):
-# logger = g.logger
-# unique_id = g.unique_id
-# output_folder = f"flask_app/static/output/{unique_id}" # 直接使用全局 unique_id 构建路径
-# filename = "ztbfile"
-# downloaded_filename = os.path.join(output_folder, filename)
-#
-# downloaded_filepath, file_type = download_file(file_url, downloaded_filename)
-#
-# if downloaded_filepath is None or file_type == 3:
-# logger.error("Unsupported file type or failed to download file")
-# error_response = {
-# 'message': 'File processing failed',
-# 'filename': None,
-# 'data': json.dumps({'error': 'File processing failed'})
-# }
-# yield f"data: {json.dumps(error_response)}\n\n"
-# return
-#
-# logger.info("Local file path: " + downloaded_filepath)
-#
-# for data in main_processing(output_folder, downloaded_filepath, file_type, unique_id):
-# response = {
-# 'message': 'Processing',
-# 'filename': os.path.basename(downloaded_filepath),
-# 'data': data
-# }
-# yield f"data: {json.dumps(response)}\n\n"
-#
-# final_response = {
-# 'message': 'File uploaded and processed successfully',
-# 'filename': os.path.basename(downloaded_filepath),
-# 'data': 'END'
-# }
-# yield f"data: {json.dumps(final_response)}\n\n"
+ if downloaded_filepath is None or file_type == 3:
+ logger.error("Unsupported file type or failed to download file")
+ error_response = {
+ 'message': 'File processing failed',
+ 'filename': None,
+ 'data': json.dumps({'error': 'File processing failed'})
+ }
+ yield f"data: {json.dumps(error_response)}\n\n"
+ return
+
+ logger.info("Local file path: " + downloaded_filepath)
+
+ for data in main_processing(output_folder, downloaded_filepath, file_type, unique_id):
+ response = {
+ 'message': 'Processing',
+ 'filename': os.path.basename(downloaded_filepath),
+ 'data': data
+ }
+ yield f"data: {json.dumps(response)}\n\n"
+
+ final_response = {
+ 'message': 'File uploaded and processed successfully',
+ 'filename': os.path.basename(downloaded_filepath),
+ 'data': 'END'
+ }
+ yield f"data: {json.dumps(final_response)}\n\n"
def validate_request():
@@ -164,13 +166,90 @@ def test_zbparse():
def test_process_and_stream():
- # 模拟五段数据
+ # 模拟七段数据,每段包含指定的中文键名和更多详细数据
data_segments = [
- {"base_info": {"project_name": "测试项目1", "project_code": "TP001"}},
- {"review_standards": ["标准1", "标准2", "标准3"]},
- {"evaluation_standards": ["评估标准A", "评估标准B"]},
- {"invalid_requirements": ["无效要求X", "无效要求Y"]},
- {"bidding_documents_requirements": ["文件要求1", "文件要求2"]}
+ {
+ "base_info": {
+ "基础信息": {
+ "project_name": "测试项目1",
+ "project_code": "TP001",
+ "project_manager": "张三",
+ "start_date": "2024-01-10",
+ "end_date": "2024-12-31"
+ }
+ }
+ },
+ {
+ "qualification_review": {
+ "资格审查": {
+ "review_criteria": ["公司资质", "过往业绩", "财务报表"],
+ "required_documents": ["营业执照", "资质证书", "近三年财务报告"],
+ "minimum_requirements": {
+ "company_age": "至少5年",
+ "past_projects": "至少3个大型项目"
+ }
+ }
+ }
+ },
+ {
+ "technical_standards": {
+ "技术标": {
+ "technical_requirements": ["设备质量要求", "施工工艺", "安全标准"],
+ "materials_list": ["钢筋", "水泥", "电缆"],
+ "equipment_specs": {
+ "excavator": "型号X123",
+ "concrete_mixer": "型号Y456"
+ }
+ }
+ }
+ },
+ {
+ "commercial_standards": {
+ "商务标": {
+ "pricing_method": "固定总价",
+ "payment_schedule": ["30%合同签订", "40%中期支付", "30%项目完成支付"],
+ "contract_conditions": {
+ "warranty_period": "2年",
+ "penalty_clauses": "延期每周罚款5%"
+ }
+ }
+ }
+ },
+ {
+ "invalid_requirements": {
+ "无效标与废标项": {
+ "common_issues": ["未按要求提交保证金", "技术标不达标"],
+ "invalidation_reasons": {
+ "missing_documents": "缺少必要文件",
+ "unqualified_technical_specs": "技术规格不合要求"
+ }
+ }
+ }
+ },
+ {
+ "bidding_documents_requirements": {
+ "投标文件要求": {
+ "file_format": "PDF",
+ "submission_deadline": "2024-08-01 17:00",
+ "submission_location": "北京市某某大厦5楼",
+ "required_sections": ["公司简介", "技术方案", "商务报价"]
+ }
+ }
+ },
+ {
+ "opening_bid": {
+ "开评定标流程": {
+ "bid_opening_time": "2024-09-01 10:00",
+ "location": "会议室A",
+ "evaluation_criteria": ["价格", "技术能力", "项目经验"],
+ "evaluation_process": {
+ "first_round": "资格审查",
+ "second_round": "技术评分",
+ "final_round": "商务报价评定"
+ }
+ }
+ }
+ }
]
filename = "test_file.pdf"
@@ -181,8 +260,8 @@ def test_process_and_stream():
'filename': filename,
'data': data
}
- yield f"data: {json.dumps(response)}\n\n"
- time.sleep(5) # 每隔2秒发送一段数据
+ yield f"data: {json.dumps(response, ensure_ascii=False)}\n\n"
+ time.sleep(5) # 每隔5秒发送一段数据
# 发送结束信号
final_response = {
@@ -190,7 +269,7 @@ def test_process_and_stream():
'filename': filename,
'data': 'END'
}
- yield f"data: {json.dumps(final_response)}\n\n"
+ yield f"data: {json.dumps(final_response, ensure_ascii=False)}\n\n"
def generate_response(final_json_path):
diff --git a/flask_app/main/招标文件解析.py b/flask_app/main/招标文件解析.py
index d2461e6..ed345b2 100644
--- a/flask_app/main/招标文件解析.py
+++ b/flask_app/main/招标文件解析.py
@@ -101,7 +101,7 @@ def fetch_project_basic_info(knowledge_name, truncate0, output_folder, clause_pa
# 形式、响应、资格评审
-def fetch_review_standards(truncate1, truncate3, knowledge_name, truncate0_jsonpath, clause_path,input_file,output_folder):
+def fetch_qualification_review(truncate1, truncate3, knowledge_name, truncate0_jsonpath, clause_path,input_file,output_folder):
logger.info("starting资格审查...")
review_standards_res = combine_review_standards(truncate1, truncate3, knowledge_name, truncate0_jsonpath,
clause_path,input_file,output_folder)
@@ -158,7 +158,7 @@ def main_processing(output_folder, downloaded_file_path, file_type, unique_id):
'base_info': executor.submit(fetch_project_basic_info, processed_data['knowledge_name'],
processed_data['truncate0'], output_folder,
processed_data['clause_path']),
- 'review_standards': executor.submit(fetch_review_standards, processed_data['truncate1'],
+ 'qualification_review': executor.submit(fetch_qualification_review, processed_data['truncate1'],
processed_data['truncate3'],
processed_data['knowledge_name'], processed_data['truncate0_jsonpath'],
processed_data['clause_path'],processed_data['input_file_path'],processed_data['output_folder']),
@@ -173,7 +173,7 @@ def main_processing(output_folder, downloaded_file_path, file_type, unique_id):
comprehensive_responses = []
# Collect results in the defined order
- for key in ['base_info', 'review_standards', 'evaluation_standards', 'invalid_requirements',
+ for key in ['base_info', 'qualification_review', 'evaluation_standards', 'invalid_requirements',
'bidding_documents_requirements', 'opening_bid']:
try:
# Wait for the future to complete and get the result
@@ -199,46 +199,58 @@ def main_processing(output_folder, downloaded_file_path, file_type, unique_id):
# "opening_bid": "{
\"开评定标流程\": {
\"开标\": {
\"开标时间和地点\": [
\"招标人在本章第4.2.1项规定的投标截止时间(开标时间)在“电子交易平台”上公开进行开标,所有投标人均应当准时在线参加开标。\",
\"招标人通过互联网在投标人须知前附表规定的地点组织开标,并在投标截止时间30分钟前,使用CA数字证书登录“电子交易平台”,进入“开标室”选择相应标段作在线开标的准备工作。\",
\"投标人应当在能够保证设施设备可靠、互联网畅通的任意地点,通过互联网在线参加开标。在投标截止时间前,使用加密其投标文件的CA数字证书登录“电子交易平台”,进入“开标室”选择所投标段进行签到,并实时在线关注招标人的操作情况。5.2开标程序\",
\"主持人按下列程序在“电子交易平台”的“开标室”进行在线开标:(1)宣布开标纪律;(2)公布主持人、招标人代表、监标人等有关人员姓名;(3)公布在投标截止时间前投标文件的递交情况;(4)公布投标保证金递交情况;(5)按照投标人须知前附表规定抽取评标基准价下浮值(如有);规定最高投标限价计算方法的,计算并公布最高投标限价(如适用),当众公布后记录在案;(6)读取已解密的投标文件的内容;(7)公布投标人名称、标段名称、投标保证金的递交情况、投标报价、项目经理姓名及其他内容,并生成开标记录;(8)开标结束。\",
\"在本章第5.2.1(6)目规定的时间内,非因“电子交易平台”原因造成投标文件未解密的,视为投标人撤回投标文件。已解密的投标文件少于三个的,招标失败;已解密的投标文件不少于三个,开标继续进行。\"
],
\"开标异议\": [
\"投标人对开标有异议的,应当在开标过程中提出;招标人当场对异议作出答复,并记入开标记录。异议与答复应通过“开标室”在“异议与答复”菜单以书面形式进行。本处所称异议是指投标人在开标过程中对投标文件提交、投标截止时间、开标程序、开标记录以及投标人和招标人或者投标人相互之间存在利益冲突的情形等提出的质疑。\",
\"投标人异议成立的,招标人将及时采取纠正措施,或者提交评标委员会评审确认;投标人异议不成立的,招标人将当场给予解释说明。\"
],
\"特殊情况的处置\": [
\"因“电子交易平台”系统故障导致无法投标的,交易中心及时通知招标人,招标人视情况决定是否顺延投标截止时间。因投标人自身原因导致无法完成投标的,由投标人自行承担后果。\",
\"因“电子交易平台”系统故障导致无法正常开标的,招标人将暂停开标,待系统恢复正常后继续开标。\",
\"“电子交易平台”系统故障是指下列情形:(1)系统服务器发生故障,无法访问或无法使用系统;(2)系统的软件或数据库出现错误,不能进行正常操作;(3)系统发现有安全漏洞,有潜在的泄密危险;(4)出现断电、断网事故;(5)其他无法保证招投标过程正常进行的情形。\"
]
},
\"评标\": {
\"评标委员会\": [
\"评标由招标人依法组建的评标委员会负责。评标委员会由招标人代表以及有关技术、经济等方面的专家组成。评标委员会成员人数以及技术、经济等方面专家的确定方式见投标人须知前附表。\",
\"评标委员会成员有下列情形之一的,应当回避:(1)投标人或投标人主要负责人的近亲属;(2)项目主管部门或者行政监督部门的人员;(3)与投标人有经济利益关系,可能影响对投标公正评审的;(4)曾因在招标、评标以及其他与招标投标有关活动中从事违法行为而受过行政处罚或刑事处罚的。\",
\"定标会招标人原则上应当在定标候选人公示结束后5个工作日内召开定标会,如有特殊情况,最迟应当在定标候选人公示结束后10个工作日内召开定标会,定标会进入公共资源交易中心进行。\",
\"定标流程(1)签到,宣读定标委员会成员名单;(2)监督小组监督员宣读定标纪律;(3)招标人代表或招标代理机构人员向定标委员会介绍定标项目相关情况;(4)定标委员成员会有疑问的,可以向招标人代表进行提问;(5)阅相关资料;(6)投票;(7)招标人代表或招标代理机构人员进行统计;(8)定标委员会组长宣读得分结果和定标结果;定标委员会成员签署定标报告,会议结束。\",
\"定标原则(1)组建定标委员会:由招标人组建定标委员会负责定标工作,按照定标委员会定标法进行定标。定标委员会成员数量为5人,招标人的法定代表人或其授权代表(为领导班子成员之一)应当参加定标会,并推荐担任定标会组长主持定标会,定标委员会其他成员从招标人组建的定标成员库中随机抽取确定。定标委员会成员与定标候选人有利害关系的,应当回避。所有参加定标会的定标委员会成员的意见应有书面记录,并由所有定标委员会成员签字确认。(2)组建定标监督小组:由招标人组建定标监督小组,对定标委员会的定标活动全过程进行监督,定标监督小组由2人组成,一般为招标人本单位或上级单位纪检监察人员,也可由招标人的法定代表人或主要负责人指定骨干成员参加。定标监督小组有权就定标委员会违反定标规则的行为进行质询。评估是否符合内控机制及价值取向,确保定标过程公正、公平。定标前,招标人或者招标代理机构在定标前可以介绍项目情况、招标情况、对投标人或者项目负责人的考察、质询情况;招标人可以邀请评标专家代表介绍评标情况、专家评审意见及评标结论、提醒注意事项。定标委员会成员有疑问的,可以向招标人或者招标代理机构、评标专家提问。\",
\"定标办法(1)定标会成员根据评标委员会提出书面评标报告,结合定标候选人的投标报价、商务标、技术标、市场信誉等,招标人应当按照充分竞争、合理低价的原则,集体讨论后,采用简单多数原则进行票决,在进入投票范围的定标候选人中,以每人投票支持一个定标候选人的方式,得票最多且过半数的定标候选人为中标人。当没有定标候选人得票超过半数,但有2个定标候选人得票较多时,选择得票较多的2个定标候选人(按上一轮得票多少的顺序选择,在选择第2个定标候选人时出现同票的投标人时,所有同票定标候选人一并纳入下一轮的投票范围)作为二次投票的范围,直至出现得票过半数的定标候选人为止。如果没有2个定标候选人得票较多时,重新投票。(2)定标会由招标人或代理机构的工作人员发放选票、定标会成员填写选票(须说明推荐理由并署名),定标过程公开、公平、公正。定标会成员按有关规定及招标文件约定的定标方法确定一名中标人。投票定标选票招标项目名称:支持的投标人支持理由定标委员签名:时间:本项目采用“评定分离”方法实施招投标活动。本项目定标办法详见第三章附件定标办法。\",
\"定标标准(1)择优要素。招标人在定标前应对评标委员会评审结果与实际情况进行实质性审查核实,重点对投标人的企业实力、企业信誉、履约能力的真实性、准确性、一致性进行核实,招标人应如实记录审查核实情况并作为定标参考。在考虑价格因素时,招标人应坚持投标人投标报价和其履约能力、服务质量等与招标项目相匹配的原则。企业实力包括资质等级、近几年营业额、过往业绩(含业绩影响力、难易程度)等方面。企业信誉包括获得各种荣誉、过往业绩履约情况,同时应重点关注近几年的不良信息,包括建设行政主管部门作出的各种不良处罚以及其他失信记录。对拟派团队履约能力与履约水平考核方式,可以考察团队主要负责人类似业绩情况,也可以对拟派项目负责人进行答辩。为确保可追溯性,答辩工作在有录音、录像场所进行。各项考核动作要针对所有投标人统一进行,不宜针对部分投标人进行考核,以体现公平原则。在同等条件下,择优的相对标准有以下几个方面:1)投标报价:各定标候选人的报价结合其履约能力,服务质量等与招标项目相匹配,经综合比较,价格最合理得优;2)工程业绩:综合比较投标人投标人近五年,完成的单项合同额在2000万元以上的装饰装修项目,主要比较项目难易程度和项目造价,工程业绩总造价高且项目难度大的优于工程业绩总造价低且设计难度小的;若总体难度差异不大且造价类似的情况下,业绩数量多的优于业绩数量少的;3)技术方案:对项目理解程度高、与本项目针对性强、技术方案完善且合理性相应程度高的企业优于项目理解程度一般、技术方案基本完善且进度控制一般的企业;4)企业实力:企业财务指标良好(整体营业收入、资产负债率等)的企业优于财务指标一般得企业;以水平相同的情况下,营业收入的优劣为准;5)企业获奖:近五年(指从投标截止日往前推算五年)类似项目获得国家级奖项优于获得省级奖项;6)企业信誉:无不良行为记录企业优于有不良行为记录企业,不良行为记录较轻企业优于不良行为记录较重企业。定标会在评议时优先进行“比优”,无法比优情况下可进行“比劣”,“比劣”可参考以下等要素进行:1)有无串通投标,围标,以行贿等不正当手段谋取中标行为;2)有无挂靠,以他人名义投标,出让或者出租资格、资质证书供他人投标行为;投标人在招标人的项目中有无严重违约或重大工程质量安全问题;投标人在近一年内经查实有以上行为的不确定为中标人。\"
],
\"评标原则\": \"评标活动遵循公平、公正、科学和择优的原则。\",
\"评标\": \"评标委员会按照第三章“评标办法”规定的方法、评审因素、标准和程序对投标文件进行评审。第三章“评标办法”没有规定的方法、评审因素和标准,不作为评标依据。\",
\"评标结果公示\": \"招标人将自收到评标报告之日起3日内,在投标人须知前附表规定的媒介公示中标候选人。公示期不少于3日。投标人或者其他利害关系人对评标结果有异议的,应当在评标结果公示期间提出。招标人自收到异议之日起3日内作出答复;作出答复前,暂停招标投标活动。异议与答复应当通过“电子交易平台”在“异议与答复”菜单以书面形式进行。\",
\"履约能力的审查(如有)\": \"如果中标候选人的经营、财务状况发生较大变化或者存在违法行为,招标人认为可能影响其履约能力的,将在发出中标通知书前报行政监督部门后,召集原评标委员会按照招标文件规定的标准和方法审查确认。\"
},
\"定标\": {
\"评标结果\": \"(1)评标委员会完成评标后,应当向招标人提出书面评标报告,阐明评标委员会对各投标文件的评审和比较意见,并按照招标文件中规定的评标方法,在投标报价合格的基础上,按照最终得分(保留2位小数)由高到低推荐定标候选人。定标候选人不少于3家,不超过5家。投标人的数量少于或者等于10家时,评标委员会推荐的定标候选人数量不超过3家,经评标委员会评审,符合招标文件要求的定标候选人不足3家时,由评标委员会作出是否具备竞争性,如具备竞争性,可继续推荐定标候选人,招标人可继续定标,否则,招标人应重新招标。定标侯选人进入定标程序。(2)经评标委员会评审,符合招标文件要求的定标候选人不足3家时,由评标委员会作出是否具备竞争性,如具备竞争性,可继续推荐定标候选人,招标人可继续定标,否则,招标人应重新招标。(3)招标人应当自收到评标报告之日起3日内公示定标候选人,公示期不少于3日。对评标结果的异议的提出和处理,适用《招标投标法实施条例》第五十四条的规定。评标结果(定标候选人)公示期间,因异议或投诉导致定标候选人少于招标文件规定的数量时,招标人继续定标还是在原评标委员会评审的基础上递补定标候选人由招标人在招标文件中明确。评标结果(定标候选人)公示期间,有定标候选人因异议或投诉并查实被取消中标资格时,若有效定标候选人不少于3家的,不再递补,招标人继续定标;除评标委员会作出具备竞争性情形外,若有效定标候选人少于3家的,按投标人得分高低补足至3家。对于递补的定标候选人需在黄石市公共资源交易信息网公示不少于3日。\"
}
}
}"
# }
-# 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'])
+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)
+ if not processed_data:
+ yield "data: {}\n\n" # 如果处理数据失败,返回空
+
+ 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']),
+ 'qualification_review': executor.submit(fetch_qualification_review, processed_data['truncate_files'][1],
+ processed_data['truncate_files'][4],
+ processed_data['knowledge_name'], processed_data['truncate0_jsonpath'],
+ processed_data['clause_path'], processed_data['input_file_path'],
+ processed_data['output_folder']),
+ '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, future in futures.items():
+ futures[task_name] = executor.submit(future)
+ time.sleep(1) # 任务提交后暂停1秒
+
+ # 按照任务完成的顺序返回结果
+ for future in concurrent.futures.as_completed(futures.values()):
+ # 根据完成的任务获取对应的键
+ key = next(k for k, v in futures.items() if v == future)
+ try:
+ # 获取任务执行结果
+ result = future.result()
+ # 对结果进行JSON转换和修改
+ modified_result = transform_json_values({key: result})
+ # 以流式形式返回结果
+ yield f"data: {json.dumps(modified_result, ensure_ascii=False)}\n\n"
+ except Exception as exc:
+ global_logger.error(f"Error processing {key}: {exc}")
+ # 返回错误信息
+ yield f"data: {json.dumps({'error': f'Error processing {key}: {str(exc)}'})}\n\n"
+
+ # 删除知识索引
+ deleteKnowledge(processed_data['knowledge_index'])
if __name__ == "__main__":
output_folder = "flask_app/static/output/zytest1"
diff --git a/flask_app/货物标/test.py b/flask_app/货物标/test.py
index bf7f23f..4e7c952 100644
--- a/flask_app/货物标/test.py
+++ b/flask_app/货物标/test.py
@@ -11,10 +11,19 @@ def is_numeric_key(key):
#TODO:如果键值中存在数字就不行
#zbtest20也有问题
def contains_number_or_index(key, value):
- return (isinstance(value, (int, float)) or
- (isinstance(value, str) and value.isdigit()) or
- '序号' in key or
- (isinstance(value, str) and re.search(r'\d+', value)))
+ # 判断值是否是数字或数字字符串
+ is_number = isinstance(value, (int, float)) or (isinstance(value, str) and value.isdigit())
+ # 判断键是否包含 "序号"
+ contains_index = '序号' in key
+ # 判断值中是否包含数字
+ contains_digit = isinstance(value, str) and re.search(r'\d+', value)
+ # 判断值中是否包含中文字符
+ contains_chinese = isinstance(value, str) and re.search(r'[\u4e00-\u9fff]', value)
+ # 如果值中包含数字但也有中文字符,则保留(返回 False)
+ if contains_digit and contains_chinese:
+ return False
+ # 如果值是数字或包含数字,且不包含中文字符,或者键包含 "序号",返回 True
+ return is_number or contains_index or contains_digit
def preprocess_dict(data):
if isinstance(data, dict):
@@ -77,15 +86,13 @@ input_data = {
"符合性审查": {
"说明": "评标委员会应当对符合资格的投标人的投标文件进行符合性审查,以确定其是否满足招标文件的实质性要求。",
"审查标准": [
-
-
{
"序号": 9,
"内容": "未按要求提供加盖公章及签字(签章)的;"
},
{
"序号": 1,
- "内容": "符合招标文件第二章“投标人须知”中 39"
+ "内容": "a39"
},
{
"序号": 2,