diff --git a/flask_app/general/无效标和废标公共代码.py b/flask_app/general/无效标和废标公共代码.py index 30d53f1..07640cf 100644 --- a/flask_app/general/无效标和废标公共代码.py +++ b/flask_app/general/无效标和废标公共代码.py @@ -1,8 +1,45 @@ +import json +import os import re +import time +from concurrent.futures import ThreadPoolExecutor from docx import Document + +from flask_app.general.通义千问long import upload_file, qianwen_long_text +from flask_app.general.通用功能函数 import process_string_list + + +# 只读取前附表中的最后一列(省钱,但容易漏内容) +def read_docx_last_column(truncate_file): + # 尝试打开文档 + try: + doc = Document(truncate_file) + except Exception as e: + print(f"Error opening file: {e}") + return [] + + last_column_values = [] + + # 读取文档中的所有表格 + if not doc.tables: + print("No tables found in the document.") + return last_column_values + + # 遍历文档中的每个表格 + for table in doc.tables: + # 获取表格的最后一列 + for row in table.rows: + last_cell = row.cells[-1] # 获取最后一个单元格 + # 去除内容前后空白并删除文本中的所有空格 + cleaned_text = last_cell.text.strip().replace(' ', '') + last_column_values.append(cleaned_text) + + return last_column_values + # 完整读取文件中所有表格(适合pdf转docx价格便宜的情况,优先推荐,内容完整) def read_tables_from_docx(file_path): + print(file_path) # 尝试打开文档 try: doc = Document(file_path) @@ -391,4 +428,161 @@ def extract_table_with_keywords(data, keywords, follow_up_keywords): i += 1 else: i += 1 - return sentences1, sentences2 # 返回两个列表 \ No newline at end of file + return sentences1, sentences2 # 返回两个列表 + +def extract_values_if_contains(data, includes): + """ + 递归检查字典中的值是否包含列表 'includes' 中的内容。 + 如果包含,将这些值添加到一个列表中并返回。 + + 参数: + data (dict): 字典或从 JSON 解析得到的数据。 + includes (list): 包含要检查的关键词的列表。 + + 返回: + list: 包含满足条件的值的列表。 + """ + included_values = [] # 初始化结果列表 + + # 定义递归函数来处理嵌套字典 + def recursive_search(current_data): + if isinstance(current_data, dict): + for key, value in current_data.items(): + if isinstance(value, dict): + # 如果值是字典,递归搜索 + recursive_search(value) + elif isinstance(value, str): + # 如果值是字符串,检查是否包含任何 includes 中的关键词 + if any(include in value for include in includes): + included_values.append(value) + elif isinstance(current_data, list): + for item in current_data: + # 如果是列表,递归每个元素 + recursive_search(item) + + # 开始递归搜索 + recursive_search(data) + + return included_values + +def handle_query(file_path, user_query, output_file, result_key, keywords): + try: + excludes = ["说明表", "重新招标", "否决所有", "否决投标的条件", "备注:", "本人保证:", "我方"] + follow_up_keywords = [r'情\s*形\s*之\s*一', r'情\s*况\s*之\s*一', r'下\s*列', r'以\s*下'] + extracted_contents = extract_text_with_keywords(file_path, [keywords], follow_up_keywords) # 字典结果 + all_texts1, all_texts2 = clean_dict_datas(extracted_contents, keywords, excludes) # 列表 + # table_data_list=read_docx_last_column(truncate_file) #从投标人须知前附表中提取信息生成列表data,每个元素为'一行信息' + table_data_list = read_tables_from_docx(file_path) + all_tables1, all_tables2 = extract_table_with_keywords(table_data_list, keywords, follow_up_keywords) + qianwen_txt = all_texts1 + all_tables1 + # Proceed only if there is content to write + selected_contents = set() # 使用 set 去重 + + if qianwen_txt: + with open(output_file, 'w', encoding='utf-8') as file: + counter = 1 + for content in qianwen_txt: + file.write(f"{counter}. {content}\n") + file.write("..............." + '\n') + counter += 1 + + file_id = upload_file(output_file) + # qianwen_ans = qianwen_long(file_id, user_query) + qianwen_ans = qianwen_long_text(file_id, user_query) + num_list = process_string_list(qianwen_ans) + print(result_key + "选中的序号:" + str(num_list)) + + for index in num_list: + if index - 1 < len(qianwen_txt): + content = qianwen_txt[index - 1] + selected_contents.add(content) + + # 无论 qianwen_txt 是否为空,都添加 all_texts2 和 all_tables2 的内容 + selected_contents.update(all_texts2) + selected_contents.update(all_tables2) + + # 如果 selected_contents 不为空,则返回结果,否则返回空字符串 + if selected_contents: + res = {result_key: list(selected_contents)} + else: + res = {result_key: ""} + return res + except Exception as e: + print(f"handle_query 在处理 {result_key} 时发生异常: {e}") + return {result_key: ""} + +# 你是一个文本助手,文本内的信息以'...............'分割,你负责准确筛选所需的信息并返回,每块信息要求完整,不遗漏,你不得擅自进行总结或删减。 +# 以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号。 +# 以上是原文内容,文本内的信息以'...............'分割,请你根据该信息回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选所需的信息并返回。最终结果以json列表格式返回给我,键名为'否决和无效投标情形',你的回答完全忠于原文内容,且回答内容与原文内容一致,要求完整与准确,不能擅自总结或者概括。", + +#"以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,每条信息规定了各方不得存在的情形或是禁止投标的情形,在这些信息中,我作为投标方,需要关注和我相关的信息,请你筛选主语是投标人或中标人或供应商或联合体投标各方或磋商小组的信息,不要返回主语是招标人或采购人或评标委员会的信息,请你筛选所需的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,示例返回为[1,4,6],若情况不存在,返回[]。", + + +def combine_find_invalid(file_path, output_dir): + os.makedirs(output_dir, exist_ok=True) + queries = [ + ( + r'否\s*决|无\s*效\s*投\s*标|无\s*效\s*文\s*件|文\s*件\s*无\s*效|无\s*效\s*响\s*应|无\s*效\s*报\s*价|无\s*效\s*标|视\s*为\s*无\s*效|被\s*拒\s*绝|予\s*以\s*拒\s*绝|投\s*标\s*失\s*效|投\s*标\s*无\s*效', + "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", + os.path.join(output_dir, "temp1.txt"), + "否决和无效投标情形" + ), + ( + r'废\s*标', + "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:废标项的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", + os.path.join(output_dir, "temp2.txt"), + "废标项" + ), + ( + r'不\s*得|禁\s*止\s*投\s*标', + "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,每条信息规定了各方不得存在的情形,请回答:在这些信息中,主语是投标人或中标人或供应商或联合体投标各方或磋商小组的信息有哪些?不要返回主语是招标人或采购人或评标委员会的信息,请你筛选所需的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,示例返回为[1,4,6],若情况不存在,返回[]。", + os.path.join(output_dir, "temp3.txt"), + "不得存在的情形" + ) + ] + results = [] + + # 使用线程池来并行处理查询 + with ThreadPoolExecutor() as executor: + futures = [] + for keywords, user_query, output_file, result_key in queries: + future = executor.submit(handle_query, file_path, user_query, output_file, result_key, keywords) + futures.append((future, result_key)) # 保持顺序 + time.sleep(0.5) # 暂停0.5秒后再提交下一个任务 + + for future, result_key in futures: + try: + result = future.result() + except Exception as e: + print(f"线程处理 {result_key} 时出错: {e}") + result = {result_key: ""} + results.append(result) + # 禁止投标(find_forbidden)部分 + # try: + # # print("starting不得存在的情形...") + # forbidden_res = find_forbidden(qualification) + # except Exception as e: + # print(f"find_forbidden 处理时出错: {e}") + # forbidden_res = {'不得存在的其他情形': ""} + # results.append(forbidden_res) + combined_dict = {} + for d in results: + combined_dict.update(d) + + print("无效标与废标done...") + return {"无效标与废标项": combined_dict} + + +# TODO:无效标目前以整个docx文档作为输入,可能导致后面两章不必要的信息也导入。 无效投标至少>8个字 +if __name__ == '__main__': + start_time = time.time() + # truncate_json_path = "C:\\Users\\Administrator\\Desktop\\货物标\\output4\\tmp2\\竞争性谈判文件(3)_tobidders_notice_part1\\truncate_output.json" + # truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output4\\招标文件(实高电子显示屏)_tobidders_notice_part1.docx" + # clause_path = "D:\\flask_project\\flask_app\\static\\output\\output1\\77a48c63-f39f-419b-af2a-7b3dbf41b70b\\clause1.json" + # doc_path="C:\\Users\\Administrator\\Desktop\\货物标\\zbfilesdocx\\磋商文件(1).docx" + doc_path = r'D:\flask_project\flask_app\static\output\output1\6189605b-d0d3-4fc5-a5fa-1dba4ffc91f1\ztbfile_invalid.docx' + output_dir = r"D:\flask_project\flask_app\static\output\output1\6189605b-d0d3-4fc5-a5fa-1dba4ffc91f1\tmp" + results = combine_find_invalid(doc_path, output_dir) + end_time = time.time() + print("Results:", json.dumps(results, ensure_ascii=False, indent=4)) + print("Elapsed time:", str(end_time - start_time)) \ No newline at end of file diff --git a/flask_app/main/工程标解析main.py b/flask_app/main/工程标解析main.py index 28d7c41..c156d4b 100644 --- a/flask_app/main/工程标解析main.py +++ b/flask_app/main/工程标解析main.py @@ -11,7 +11,7 @@ from flask_app.general.merge_pdfs import merge_pdfs from flask_app.main.table_content_extraction import extract_tables_main from flask_app.main.提取json工程标版 import convert_clause_to_json from flask_app.general.json_utils import transform_json_values -from flask_app.main.无效标和废标和禁止投标整合 import combine_find_invalid +from flask_app.general.无效标和废标公共代码 import combine_find_invalid from flask_app.main.投标人须知正文提取指定内容 import extract_from_notice import concurrent.futures from flask_app.main.基础信息整合快速版 import combine_basic_info @@ -64,8 +64,8 @@ def preprocess_files(output_folder, downloaded_file_path, file_type,unique_id): qualification = truncate_files[3] #资格审查 invalid_path=truncate_files[5] - invalid_docpath = copy_docx(docx_path) # docx截取无效标部分 - # invalid_docpath=pdf2docx(invalid_path) + # invalid_docpath = copy_docx(docx_path) # docx截取无效标部分 + invalid_docpath=pdf2docx(invalid_path) merged_baseinfo_path=truncate_files[-1] @@ -139,10 +139,10 @@ def fetch_evaluation_standards(invalid_path, evaluation_method): # 无效、废标项解析 -def fetch_invalid_requirements(invalid_docpath, output_folder, qualification): +def fetch_invalid_requirements(invalid_docpath, output_folder): logger.info("starting 无效标与废标...") start_time = time.time() - find_invalid_res = combine_find_invalid(invalid_docpath, output_folder, qualification) + find_invalid_res = combine_find_invalid(invalid_docpath, output_folder) end_time = time.time() logger.info(f"无效标与废标 done,耗时:{end_time - start_time:.2f} 秒") return find_invalid_res @@ -193,8 +193,7 @@ def engineering_bid_main(output_folder, downloaded_file_path, file_type, unique_ processed_data['clause_path'], processed_data['invalid_path'], processed_data['merged_baseinfo_path']), 'evaluation_standards': executor.submit(fetch_evaluation_standards, processed_data['invalid_path'],processed_data['evaluation_method']), - 'invalid_requirements': executor.submit(fetch_invalid_requirements, processed_data['invalid_docpath'], - output_folder, processed_data['qualification']), + 'invalid_requirements': executor.submit(fetch_invalid_requirements, processed_data['invalid_docpath'],output_folder), 'bidding_documents_requirements': executor.submit(fetch_bidding_documents_requirements,processed_data['invalid_path'], processed_data['merged_baseinfo_path_more'],processed_data['clause_path']), 'opening_bid': executor.submit(fetch_bid_opening,processed_data['invalid_path'],processed_data['merged_baseinfo_path_more'], processed_data['clause_path']) } diff --git a/flask_app/main/无效标和废标和禁止投标整合.py b/flask_app/main/无效标和废标和禁止投标整合.py deleted file mode 100644 index 5763144..0000000 --- a/flask_app/main/无效标和废标和禁止投标整合.py +++ /dev/null @@ -1,163 +0,0 @@ -# -*- coding: utf-8 -*- -import json -import os.path -import time -import re - -from flask_app.general.format_change import pdf2docx -from flask_app.general.无效标和废标公共代码 import extract_text_with_keywords, preprocess_text_list, clean_dict_datas, \ - read_tables_from_docx, extract_table_with_keywords -from flask_app.general.通义千问long import upload_file, qianwen_long,qianwen_long_text -from concurrent.futures import ThreadPoolExecutor - -from flask_app.main.table_content_extraction import extract_tables_main -from flask_app.main.禁止投标情形 import find_forbidden, process_string_list - -#处理无效投标 -def extract_values_if_contains(data, includes): - """ - 递归检查字典中的值是否包含列表 'includes' 中的内容。 - 如果包含,将这些值添加到一个列表中并返回。 - - 参数: - data (dict): 字典或从 JSON 解析得到的数据。 - includes (list): 包含要检查的关键词的列表。 - - 返回: - list: 包含满足条件的值的列表。 - """ - included_values = [] # 初始化结果列表 - - # 定义递归函数来处理嵌套字典 - def recursive_search(current_data): - if isinstance(current_data, dict): - for key, value in current_data.items(): - if isinstance(value, dict): - # 如果值是字典,递归搜索 - recursive_search(value) - elif isinstance(value, str): - # 如果值是字符串,检查是否包含任何 includes 中的关键词 - if any(include in value for include in includes): - included_values.append(value) - elif isinstance(current_data, list): - for item in current_data: - # 如果是列表,递归每个元素 - recursive_search(item) - - # 开始递归搜索 - recursive_search(data) - - return included_values - - -#你是一个文本助手,文本内的信息以'...............'分割,你负责准确筛选所需的信息并返回,每块信息要求完整,不遗漏,你不得擅自进行总结或删减。 -#以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号。 -#以上是原文内容,文本内的信息以'...............'分割,请你根据该信息回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选所需的信息并返回。最终结果以json列表格式返回给我,键名为'否决和无效投标情形',你的回答完全忠于原文内容,且回答内容与原文内容一致,要求完整与准确,不能擅自总结或者概括。", - -#TODO:truncate_json_path为空的时候,单独提取表格数据 -def handle_query(file_path, user_query, output_file, result_key, keywords): - try: - excludes = ["说明表", "重新招标", "否决所有", "否决投标的条件", "备注:", "本人保证:","我方"] - follow_up_keywords = [r'情\s*形\s*之\s*一', r'情\s*况\s*之\s*一', r'下\s*列', r'以\s*下'] - extracted_contents = extract_text_with_keywords(file_path, [keywords], follow_up_keywords) # 提取正文(除表格) - all_texts1, all_texts2 = clean_dict_datas(extracted_contents, keywords, excludes) # 列表 - table_data_list = read_tables_from_docx(file_path) - all_tables1, all_tables2 = extract_table_with_keywords(table_data_list, keywords, follow_up_keywords) - qianwen_txt = all_texts1 + all_tables1 - selected_contents = set() # 使用 set 去重 - - if qianwen_txt: - with open(output_file, 'w', encoding='utf-8') as file: - counter = 1 - for content in qianwen_txt: - file.write("..............." + '\n') - file.write(f"{counter}. {content}\n") - counter += 1 - - file_id = upload_file(output_file) - # qianwen_ans = qianwen_long(file_id, user_query) - qianwen_ans = qianwen_long_text(file_id, user_query) - num_list = process_string_list(qianwen_ans) - print(result_key + "选中的序号:" + str(num_list)) - - for index in num_list: - if 1 <= index <= len(qianwen_txt): - content = qianwen_txt[index - 1] - selected_contents.add(content) - - # 无论 qianwen_txt 是否为空,都添加 all_texts2 和 all_tables2 的内容 - selected_contents.update(all_texts2) - selected_contents.update(all_tables2) - - # 如果 selected_contents 不为空,则返回结果,否则返回空字符串 - if selected_contents: - res = {result_key: list(selected_contents)} - else: - res = {result_key: ""} - - return res - except Exception as e: - print(f"handle_query 在处理 {result_key} 时发生异常: {e}") - return {result_key: ""} - -#TODO:增加提取逻辑 else里面的内容 -def combine_find_invalid(file_path, output_folder, qualification): - os.makedirs(output_folder, exist_ok=True) - queries = [ - ( - r'否\s*决|无\s*效\s*投\s*标|无\s*效\s*文\s*件|文\s*件\s*无\s*效|无\s*效\s*响\s*应|无\s*效\s*报\s*价|无\s*效\s*标|视\s*为\s*无\s*效|被\s*拒\s*绝|予\s*以\s*拒\s*绝|投\s*标\s*失\s*效|投\s*标\s*无\s*效', - "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", - os.path.join(output_folder, "temp1.txt"), - "否决和无效投标情形" - ), - ( - r'废\s*标', - "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:废标项的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", - os.path.join(output_folder, "temp2.txt"), - "废标项" - ) - ] - results = [] - # 使用线程池来并行处理查询 - with ThreadPoolExecutor() as executor: - futures = [] - for keywords, user_query, output_file, result_key in queries: - future = executor.submit(handle_query, file_path, user_query, output_file, result_key, keywords) - futures.append((future, result_key)) # 保持顺序 - time.sleep(0.5) # 暂停0.5秒后再提交下一个任务 - - for future, result_key in futures: - try: - result = future.result() - except Exception as e: - print(f"线程处理 {result_key} 时出错: {e}") - result = {result_key: ""} - results.append(result) - - # 禁止投标(find_forbidden)部分 - try: - # print("starting不得存在的情形...") - forbidden_res = find_forbidden(qualification) - except Exception as e: - print(f"find_forbidden 处理时出错: {e}") - forbidden_res = {'不得存在的其他情形': ""} - results.append(forbidden_res) - - combined_dict = {} - for d in results: - combined_dict.update(d) - # print("无效标与废标done...") - return {"无效标与废标项": combined_dict} - -if __name__ == '__main__': - start_time = time.time() - # tobidders_notice_table="" - # clause_path="C:\\Users\\Administrator\\Desktop\\fsdownload\\006decc2-b3b5-4898-9b9f-4b0eab4e173f\\clause1.json" - qualification=r"D:\flask_project\flask_app\static\output\output1\fb297c04-b7ef-4b34-9df8-d97b6af69a03\ztbfile_qualification.pdf" - output_folder = r"D:\flask_project\flask_app\static\output\output1\fb297c04-b7ef-4b34-9df8-d97b6af69a03\tmp" - # doc_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\temp7\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\ztbfile_invalid.docx' - file_path = r'D:\flask_project\flask_app\static\output\output1\fb297c04-b7ef-4b34-9df8-d97b6af69a03\ztbfile.docx' - results = combine_find_invalid(file_path, output_folder,qualification) - end_time = time.time() - print("Elapsed time:", str(end_time - start_time)) - print("Results:", json.dumps(results,ensure_ascii=False,indent=4)) diff --git a/flask_app/main/禁止投标情形.py b/flask_app/old_version/不得存在及禁止投标情形.py similarity index 100% rename from flask_app/main/禁止投标情形.py rename to flask_app/old_version/不得存在及禁止投标情形.py diff --git a/flask_app/old_version/无效标和废标和禁止投标整合.py b/flask_app/old_version/无效标和废标和禁止投标整合.py index e344a1b..f4bfbd2 100644 --- a/flask_app/old_version/无效标和废标和禁止投标整合.py +++ b/flask_app/old_version/无效标和废标和禁止投标整合.py @@ -5,11 +5,11 @@ import time import re from flask_app.general.format_change import pdf2docx -from flask_app.general.通义千问long import upload_file, qianwen_long,qianwen_long_text +from flask_app.general.通义千问long import upload_file, qianwen_long_text from concurrent.futures import ThreadPoolExecutor from flask_app.main.table_content_extraction import extract_tables_main -from flask_app.main.禁止投标情形 import find_forbidden, process_string_list +from flask_app.old_version.不得存在及禁止投标情形 import find_forbidden, process_string_list #处理跨页的段落 diff --git a/flask_app/货物标/无效标和废标和禁止投标整合main.py b/flask_app/货物标/无效标和废标和禁止投标整合main.py deleted file mode 100644 index d17d616..0000000 --- a/flask_app/货物标/无效标和废标和禁止投标整合main.py +++ /dev/null @@ -1,189 +0,0 @@ -# -*- coding: utf-8 -*- -import json -import os.path -import time -import re - -from flask_app.general.无效标和废标公共代码 import read_tables_from_docx, extract_text_with_keywords, \ - preprocess_text_list, clean_dict_datas, extract_table_with_keywords -from flask_app.general.通义千问long import upload_file, qianwen_long,qianwen_long_text -from flask_app.general.通用功能函数 import process_string_list -from docx import Document -from concurrent.futures import ThreadPoolExecutor, as_completed -from collections import OrderedDict - -# 只读取前附表中的最后一列(省钱,但容易漏内容) -def read_docx_last_column(truncate_file): - # 尝试打开文档 - try: - doc = Document(truncate_file) - except Exception as e: - print(f"Error opening file: {e}") - return [] - - last_column_values = [] - - # 读取文档中的所有表格 - if not doc.tables: - print("No tables found in the document.") - return last_column_values - - # 遍历文档中的每个表格 - for table in doc.tables: - # 获取表格的最后一列 - for row in table.rows: - last_cell = row.cells[-1] # 获取最后一个单元格 - # 去除内容前后空白并删除文本中的所有空格 - cleaned_text = last_cell.text.strip().replace(' ', '') - last_column_values.append(cleaned_text) - - return last_column_values - -# 处理无效投标 -def extract_values_if_contains(data, includes): - """ - 递归检查字典中的值是否包含列表 'includes' 中的内容。 - 如果包含,将这些值添加到一个列表中并返回。 - - 参数: - data (dict): 字典或从 JSON 解析得到的数据。 - includes (list): 包含要检查的关键词的列表。 - - 返回: - list: 包含满足条件的值的列表。 - """ - included_values = [] # 初始化结果列表 - - # 定义递归函数来处理嵌套字典 - def recursive_search(current_data): - if isinstance(current_data, dict): - for key, value in current_data.items(): - if isinstance(value, dict): - # 如果值是字典,递归搜索 - recursive_search(value) - elif isinstance(value, str): - # 如果值是字符串,检查是否包含任何 includes 中的关键词 - if any(include in value for include in includes): - included_values.append(value) - elif isinstance(current_data, list): - for item in current_data: - # 如果是列表,递归每个元素 - recursive_search(item) - - # 开始递归搜索 - recursive_search(data) - - return included_values - - -# 你是一个文本助手,文本内的信息以'...............'分割,你负责准确筛选所需的信息并返回,每块信息要求完整,不遗漏,你不得擅自进行总结或删减。 -# 以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号。 -# 以上是原文内容,文本内的信息以'...............'分割,请你根据该信息回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选所需的信息并返回。最终结果以json列表格式返回给我,键名为'否决和无效投标情形',你的回答完全忠于原文内容,且回答内容与原文内容一致,要求完整与准确,不能擅自总结或者概括。", - -def handle_query(file_path, user_query, output_file, result_key, keywords): - try: - excludes = ["说明表", "重新招标", "否决所有", "否决投标的条件", "备注:", "本人保证:", "我方"] - follow_up_keywords = [r'情\s*形\s*之\s*一', r'情\s*况\s*之\s*一', r'下\s*列', r'以\s*下'] - extracted_contents = extract_text_with_keywords(file_path, [keywords], follow_up_keywords) # 字典结果 - all_texts1, all_texts2 = clean_dict_datas(extracted_contents, keywords, excludes) # 列表 - # table_data_list=read_docx_last_column(truncate_file) #从投标人须知前附表中提取信息生成列表data,每个元素为'一行信息' - table_data_list = read_tables_from_docx(file_path) - all_tables1, all_tables2 = extract_table_with_keywords(table_data_list, keywords, follow_up_keywords) - qianwen_txt = all_texts1 + all_tables1 - # Proceed only if there is content to write - selected_contents = set() # 使用 set 去重 - - if qianwen_txt: - with open(output_file, 'w', encoding='utf-8') as file: - counter = 1 - for content in qianwen_txt: - file.write(f"{counter}. {content}\n") - file.write("..............." + '\n') - counter += 1 - - file_id = upload_file(output_file) - # qianwen_ans = qianwen_long(file_id, user_query) - qianwen_ans = qianwen_long_text(file_id, user_query) - num_list = process_string_list(qianwen_ans) - print(result_key + "选中的序号:" + str(num_list)) - - for index in num_list: - if index - 1 < len(qianwen_txt): - content = qianwen_txt[index - 1] - selected_contents.add(content) - - # 无论 qianwen_txt 是否为空,都添加 all_texts2 和 all_tables2 的内容 - selected_contents.update(all_texts2) - selected_contents.update(all_tables2) - - # 如果 selected_contents 不为空,则返回结果,否则返回空字符串 - if selected_contents: - res = {result_key: list(selected_contents)} - else: - res = {result_key: ""} - return res - except Exception as e: - print(f"handle_query 在处理 {result_key} 时发生异常: {e}") - return {result_key: ""} - -def combine_find_invalid(file_path, output_dir): - os.makedirs(output_dir, exist_ok=True) - queries = [ - ( - r'否\s*决|无\s*效\s*投\s*标|无\s*效\s*文\s*件|文\s*件\s*无\s*效|无\s*效\s*响\s*应|无\s*效\s*报\s*价|无\s*效\s*标|视\s*为\s*无\s*效|被\s*拒\s*绝|予\s*以\s*拒\s*绝|投\s*标\s*失\s*效|投\s*标\s*无\s*效', - "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", - os.path.join(output_dir, "temp1.txt"), - "否决和无效投标情形" - ), - ( - r'废\s*标', - "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:废标项的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", - os.path.join(output_dir, "temp2.txt"), - "废标项" - ), - ( - r'不\s*得|禁\s*止\s*投\s*标', - "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,每条信息规定了各方不得存在的情形,请回答:在这些信息中,主语是投标人或中标人或供应商或联合体投标各方或磋商小组的信息有哪些?不要返回主语是招标人或采购人或评标委员会的信息,请你筛选所需的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,示例返回为[1,4,6],若情况不存在,返回[]。", - os.path.join(output_dir, "temp3.txt"), - "不得存在的情形" - ) - ] - results = [] - - # 使用线程池来并行处理查询 - with ThreadPoolExecutor() as executor: - futures = [] - for keywords, user_query, output_file, result_key in queries: - future = executor.submit(handle_query, file_path, user_query, output_file, result_key, keywords) - futures.append((future, result_key)) # 保持顺序 - time.sleep(0.5) # 暂停0.5秒后再提交下一个任务 - - for future, result_key in futures: - try: - result = future.result() - except Exception as e: - print(f"线程处理 {result_key} 时出错: {e}") - result = {result_key: ""} - results.append(result) - - combined_dict = {} - for d in results: - combined_dict.update(d) - - print("无效标与废标done...") - return {"无效标与废标项": combined_dict} - - -# TODO:无效标目前以整个docx文档作为输入,可能导致后面两章不必要的信息也导入。 无效投标至少>8个字 -if __name__ == '__main__': - start_time = time.time() - # truncate_json_path = "C:\\Users\\Administrator\\Desktop\\货物标\\output4\\tmp2\\竞争性谈判文件(3)_tobidders_notice_part1\\truncate_output.json" - # truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output4\\招标文件(实高电子显示屏)_tobidders_notice_part1.docx" - clause_path = "D:\\flask_project\\flask_app\\static\\output\\output1\\77a48c63-f39f-419b-af2a-7b3dbf41b70b\\clause1.json" - # doc_path="C:\\Users\\Administrator\\Desktop\\货物标\\zbfilesdocx\\磋商文件(1).docx" - doc_path = 'D:\\flask_project\\flask_app\\static\\output\\output1\\e7dda5cb-10ba-47a8-b989-d2993d34bb89\\ztbfile.docx' - output_dir = "D:\\flask_project\\flask_app\\static\\output\\output1\\e7dda5cb-10ba-47a8-b989-d2993d34bb89\\tmp" - results = combine_find_invalid(doc_path, output_dir) - end_time = time.time() - print("Elapsed time:", str(end_time - start_time)) - print("Results:", json.dumps(results, ensure_ascii=False, indent=4)) diff --git a/flask_app/货物标/货物标解析main.py b/flask_app/货物标/货物标解析main.py index 2442e87..f33b704 100644 --- a/flask_app/货物标/货物标解析main.py +++ b/flask_app/货物标/货物标解析main.py @@ -9,7 +9,7 @@ from flask_app.货物标.截取pdf货物标版 import truncate_pdf_multiple from concurrent.futures import ThreadPoolExecutor import concurrent.futures from flask_app.货物标.提取json货物标版 import convert_clause_to_json -from flask_app.货物标.无效标和废标和禁止投标整合main import combine_find_invalid +from flask_app.general.无效标和废标公共代码 import combine_find_invalid from flask_app.货物标.资格审查main import combine_qualification_review from flask_app.货物标.评分标准提取main import combine_evaluation_standards import logging