From 119c7715b550b823aad36825041d407bb9870fb1 Mon Sep 17 00:00:00 2001 From: zy123 <646228430@qq.com> Date: Mon, 16 Dec 2024 13:56:28 +0800 Subject: [PATCH] 12.16 --- flask_app/general/file2markdown.py | 4 +- flask_app/general/post_processing.py | 2 +- flask_app/general/无效标和废标公共代码.py | 63 +++++++++---------- flask_app/routes/get_deviation.py | 2 +- flask_app/routes/little_zbparse.py | 2 +- .../{接口_技术偏离表.py => 偏离表main.py} | 0 .../routes/{接口_小解析.py => 小解析main.py} | 0 .../货物标/技术参数要求提取后处理函数.py | 24 ++++--- 8 files changed, 50 insertions(+), 47 deletions(-) rename flask_app/routes/{接口_技术偏离表.py => 偏离表main.py} (100%) rename flask_app/routes/{接口_小解析.py => 小解析main.py} (100%) diff --git a/flask_app/general/file2markdown.py b/flask_app/general/file2markdown.py index 48a2c22..5732e5a 100644 --- a/flask_app/general/file2markdown.py +++ b/flask_app/general/file2markdown.py @@ -49,7 +49,7 @@ def convert_pdf_to_markdown(file_path): image = get_file_content(file_path) resp = textin.recognize_pdf2md(image, { 'page_start': 0, - 'page_count': 50, # 设置解析页数为50页 + 'page_count': 100, # 设置解析页数为50页 'table_flavor': 'html', # html 按html语法输出表格 'parse_mode': 'auto', # 设置解析模式为scan模式 'page_details': 0, # 不包含页面细节 @@ -75,6 +75,6 @@ def convert_pdf_to_markdown(file_path): if __name__ == "__main__": # file_path=r"C:\Users\Administrator\Desktop\fsdownload\e702f1e6-095d-443d-bb7d-ef2e42037cb1\ztbfile_procurement.pdf" - file_path=r"C:\Users\Administrator\Desktop\货物标\output1\招标文件(实高电子显示屏)_procurement.pdf" + file_path=r"C:\Users\Administrator\Desktop\招标文件-采购类\2024-云南-云南省森林消防总队昆明支队室内体能训练馆及训练场建设项目训练设施及训练器材采购项目.pdf" res=convert_pdf_to_markdown(file_path) print(res) \ No newline at end of file diff --git a/flask_app/general/post_processing.py b/flask_app/general/post_processing.py index 3bef0c1..6c62c94 100644 --- a/flask_app/general/post_processing.py +++ b/flask_app/general/post_processing.py @@ -3,7 +3,7 @@ import json import re from flask_app.general.format_date import format_chinese_date from flask_app.general.format_amout import format_amount -from flask_app.routes.接口_技术偏离表 import extract_matching_keys, prepare_for_zige_info, \ +from flask_app.routes.偏离表main import extract_matching_keys, prepare_for_zige_info, \ process_functions_in_parallel diff --git a/flask_app/general/无效标和废标公共代码.py b/flask_app/general/无效标和废标公共代码.py index 160aa4a..b2c60de 100644 --- a/flask_app/general/无效标和废标公共代码.py +++ b/flask_app/general/无效标和废标公共代码.py @@ -4,8 +4,9 @@ import re import time from concurrent.futures import ThreadPoolExecutor from flask_app.general.doubao import doubao_model, generate_full_user_query -from docx import Document from flask_app.general.通用功能函数 import process_string_list +from collections import OrderedDict +from docx import Document # 只读取前附表中的最后一列(省钱,但容易漏内容) @@ -128,9 +129,6 @@ def preprocess_paragraphs(paragraphs): #如果当前段落有序号,则向下匹配直接遇到相同的序号样式 #如果当前段落无序号,则向下匹配序号,把若干同类的序号都摘出来。 def extract_text_with_keywords(doc_path, keywords, follow_up_keywords): - from collections import OrderedDict - from docx import Document - import re if isinstance(keywords, str): keywords = [keywords] @@ -221,9 +219,9 @@ def extract_text_with_keywords(doc_path, keywords, follow_up_keywords): found_next_number = False current_section_pattern = None - while current_index < len(doc.paragraphs) - 1: + while current_index < len(processed_paragraphs) - 1: current_index += 1 - next_text = doc.paragraphs[current_index].text.strip() + next_text = processed_paragraphs[current_index].strip() if not found_next_number: # 修改后的正则,支持 '数字 、' 格式 next_section_number = re.match(r'^([A-Za-z0-9]+(?:[..][A-Za-z0-9]+)*)|(\(\d+\))|(\d+\s*、)', @@ -264,7 +262,7 @@ new_text_list = ["这是第一句。", "1. 接下来是第二句!", "(3) 最 def preprocess_text_list(text_list): new_text_list = [] # 正则表达式匹配中文字符或标点后的空格,该空格后紧跟字母、数字或带括号的数字 - split_pattern = re.compile(r'(?<=[\u4e00-\u9fff。;!??!;])(?=\s+[a-zA-Z\d]|\s+\([1-9]\d*\)|\s+\([1-9]\d*\))') + split_pattern = re.compile(r'(?<=[\u4e00-\u9fff。;!??!;])(?=\s+[a-zA-Z\d]|\s+\([1-9]\d*\)|\s+\([1-9]\d*\))') #。;!??!; for text in text_list: # 使用正则表达式检查并拆分元素 parts = split_pattern.split(text) @@ -286,7 +284,7 @@ def clean_dict_datas(extracted_contents, keywords, excludes): # 让正则表达 if any(exclude in data for exclude in excludes): continue # 如果包含任何排除字符串,跳过这个数据 # 去掉开头的序号,eg:1 | (1) |(2) | 1. | 2.(全角点)| 3、 | 1.1 | 2.3.4 | A1 | C1.1 | 一、 - pattern = r'^\s*([((]\d+[)))]|[A-Za-z]?\d+\s*(\.\s*\d+)*[\s\.、.)\)]|[一二三四五六七八九十]+、)' + pattern = r'^\s*(?:[((]\d+[)))]|[A-Za-z]?\d+(?:\.\s*\d+)*[\s\.、.)\)]?|[一二三四五六七八九十]+、)' data = re.sub(pattern, '', data).strip() keyword_match = re.search(keywords, data) if keyword_match: @@ -316,7 +314,7 @@ def clean_dict_datas(extracted_contents, keywords, excludes): # 让正则表达 # print("*********") new_text_list = preprocess_text_list(text_list) # 用于处理结构化文本,清理掉不必要的序号,并将分割后的段落合并,最终形成更简洁和格式化的输出。 - pattern = r'^\s*([((]\d+[)))]|[A-Za-z]?\d+\s*(\.\s*\d+)*[\s\.、.)\)]|[一二三四五六七八九十]+、)' + pattern = r'^\s*(?:[((]\d+[)))]|[A-Za-z]?\d+(?:\.\s*\d+)*[\s\.、.)\)]?|[一二三四五六七八九十]+、)' data = re.sub(pattern, '', new_text_list[0]).strip() # 去除序号 # 将修改后的第一个元素和剩余的元素连接起来 @@ -367,10 +365,10 @@ def extract_table_with_keywords(data, keywords, follow_up_keywords): # 2. 分割句子,保证句子完整性(按标点符号和序号分割) split_sentences = re.split( - r'(?<=[。!?!?\?;])|' # 在中文句号、感叹号、问号或分号后面分割 - r'(?=\d+[..]\d+)|' # 在类似1.1的数字序号前分割 - r'(?=\d+\s(?![号条款节章项例页段部步点年月日时分秒个]))|' # 数字后面跟空格且空格后面不是指定关键字时分割 - r'(?=\d+[、..])|' # 在数字后直接跟顿号、半角点号或全角点号时分割 + r'(?<=[。!?!?\?])|' # 在中文句号、感叹号、问号或分号后面分割 + r'(?=\d+(?:[..]\d+)+)(?!\s*[号条款节章项例页段部步点年月日时分秒个元万])|' # 在类似1.1 1.1.1 的数字序号前分割 + r'(?=\d+\s(?!\s*[号条款节章项例页段部步点年月日时分秒个元万]))|' # 数字后面跟空格且空格后面不是指定关键字时分割 + r'(?=\d+[、..])(?!\s*[号条款节章项例页段部步点年月日时分秒个元万])|' # 在数字后直接跟顿号、半角点号或全角点号时分割 r'(?=[A-Za-z][..]\s*)|' # 在字母加点(如A.、a.)前分割 r'(?=[A-Za-z]+\s*\d+\s*(?:[..]\s*\d+)*)|' # 在可选字母加数字或多级编号前分割 r'(?=[一二三四五六七八九十]+、)', # 在中文数字加顿号(如一、二、)前分割 @@ -412,6 +410,7 @@ def extract_table_with_keywords(data, keywords, follow_up_keywords): full_text = ' '.join(split_sentences[start_index:]).strip() # 清洗文本,去除前缀编号等 + 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) # 存储有后续关键词的情况 @@ -468,7 +467,7 @@ def handle_query(file_path, user_query, output_file, result_key, keywords): follow_up_keywords = [r'情\s*形\s*之\s*一', r'情\s*况\s*之\s*一', r'下\s*列', r'以\s*下','其\s*他\s*情\s*形\s*:','其\s*他\s*情\s*形\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_docx_last_column(file_path) #从投标人须知前附表中提取信息生成列表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 @@ -527,22 +526,22 @@ def combine_find_invalid(file_path, output_dir): os.path.join(output_dir, "temp1.txt"), "否决和无效投标情形" ), - ( - r'废\s*标', - """以下是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:废标项的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。 - 文本内容:{full_text} - """, - os.path.join(output_dir, "temp2.txt"), - "废标项" - ), - ( - r'不\s*得|禁\s*止\s*投\s*标', - """以下是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,每条信息规定了各方不得存在的情形,请回答:在这些信息中,投标人或中标人或供应商或联合体投标各方或磋商小组不得存在的情形或禁止投标的情形有哪些?不要返回主语是招标人或采购人或评标委员会的信息,请你筛选所需的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,示例返回为[1,4,6],若情形不存在,返回[]。以下为需要考虑的注意事项:请返回包含实际内容的信息,若信息内容诸如'投标人不得存在的其他关联情形'这样笼统的表格,而未说明具体的情形,则无需添加这条信息。 - 文本内容:{full_text} - """, - os.path.join(output_dir, "temp3.txt"), - "不得存在的情形" - ) + # ( + # r'废\s*标', + # """以下是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:废标项的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。 + # 文本内容:{full_text} + # """, + # os.path.join(output_dir, "temp2.txt"), + # "废标项" + # ), + # ( + # r'不\s*得|禁\s*止\s*投\s*标', + # """以下是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,每条信息规定了各方不得存在的情形,请回答:在这些信息中,投标人或中标人或供应商或联合体投标各方或磋商小组不得存在的情形或禁止投标的情形有哪些?不要返回主语是招标人或采购人或评标委员会的信息,请你筛选所需的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,示例返回为[1,4,6],若情形不存在,返回[]。以下为需要考虑的注意事项:请返回包含实际内容的信息,若信息内容诸如'投标人不得存在的其他关联情形'这样笼统的表格,而未说明具体的情形,则无需添加这条信息。 + # 文本内容:{full_text} + # """, + # os.path.join(output_dir, "temp3.txt"), + # "不得存在的情形" + # ) ] results = [] @@ -582,8 +581,8 @@ if __name__ == '__main__': # 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'C:\Users\Administrator\Desktop\fsdownload\457ee03d-c61c-4672-b959-2bbb35a1de29\ztbfile.docx' - output_dir = r"C:\Users\Administrator\Desktop\fsdownload\457ee03d-c61c-4672-b959-2bbb35a1de29\tmp" + doc_path = r'C:\Users\Administrator\Desktop\fsdownload\140aae9a-0dac-4bba-be57-ea706af069d6\ztbfile_invalid.docx' + output_dir = r"C:\Users\Administrator\Desktop\fsdownload\140aae9a-0dac-4bba-be57-ea706af069d6\tmp" results = combine_find_invalid(doc_path, output_dir) end_time = time.time() print("Results:", json.dumps(results, ensure_ascii=False, indent=4)) diff --git a/flask_app/routes/get_deviation.py b/flask_app/routes/get_deviation.py index 8be5214..2f62b87 100644 --- a/flask_app/routes/get_deviation.py +++ b/flask_app/routes/get_deviation.py @@ -4,7 +4,7 @@ from flask import Blueprint, jsonify, Response, g import json import os from flask_app.general.format_change import download_file -from flask_app.routes.接口_技术偏离表 import get_tech_and_business_deviation +from flask_app.routes.偏离表main import get_tech_and_business_deviation from flask_app.routes.utils import generate_deviation_response, validate_and_setup_logger from flask_app.ConnectionLimiter import require_connection_limit get_deviation_bp = Blueprint('get_deviation', __name__) diff --git a/flask_app/routes/little_zbparse.py b/flask_app/routes/little_zbparse.py index 82dd71c..f26e3fd 100644 --- a/flask_app/routes/little_zbparse.py +++ b/flask_app/routes/little_zbparse.py @@ -6,7 +6,7 @@ from flask import Blueprint, jsonify, g from flask_app.ConnectionLimiter import require_connection_limit from flask_app.general.format_change import download_file -from flask_app.routes.接口_小解析 import little_parse_main +from flask_app.routes.小解析main import little_parse_main from flask_app.routes.utils import validate_and_setup_logger little_zbparse_bp = Blueprint('little_zbparse', __name__) diff --git a/flask_app/routes/接口_技术偏离表.py b/flask_app/routes/偏离表main.py similarity index 100% rename from flask_app/routes/接口_技术偏离表.py rename to flask_app/routes/偏离表main.py diff --git a/flask_app/routes/接口_小解析.py b/flask_app/routes/小解析main.py similarity index 100% rename from flask_app/routes/接口_小解析.py rename to flask_app/routes/小解析main.py diff --git a/flask_app/货物标/技术参数要求提取后处理函数.py b/flask_app/货物标/技术参数要求提取后处理函数.py index 17ed4f2..846f5c0 100644 --- a/flask_app/货物标/技术参数要求提取后处理函数.py +++ b/flask_app/货物标/技术参数要求提取后处理函数.py @@ -1,13 +1,8 @@ import json import re -import string -from collections import OrderedDict from collections import defaultdict #传输技术参数需求的时候后处理 def extract_matching_keys(data, good_list, special_keys=None, parent_key=''): - import re - from collections import defaultdict - def get_suffix(n): """ 根据数字n返回对应的字母后缀。 @@ -52,8 +47,17 @@ def extract_matching_keys(data, good_list, special_keys=None, parent_key=''): if isinstance(value, list): # 处理值为列表的键 if any(pattern.match(clean_key) for pattern in patterns): - new_key = generate_key(clean_key, parent_key, key_counter, suffix_map, special_keys) - filtered_data[new_key] = value + # 检查是否以特殊符号开头 + if clean_key.startswith(('▲', '★','●','■','◆','☆','△','◇','○','□')): + symbol = clean_key[0] + stripped_key = clean_key[1:] + new_key = generate_key(stripped_key, parent_key, key_counter, suffix_map, special_keys) + # 将符号添加到每个字符串的开头 + new_value = [symbol + item for item in value] + filtered_data[new_key] = new_value + else: + new_key = generate_key(clean_key, parent_key, key_counter, suffix_map, special_keys) + filtered_data[new_key] = value elif isinstance(value, dict): # 继续递归处理嵌套字典 new_parent_key = clean_key if parent_key == '' else f"{parent_key}的{clean_key}" @@ -245,8 +249,8 @@ def remove_common_prefixes(string_list, min_occurrence=3): if __name__ == "__main__": # 示例数据 sample_data = { - "交通信号机": [ - "★应采用区域控制信号机,并应与广水市交通信号控制系统兼容,信号机能接入已有系统平台,实现联网优化功能。", + "★交通信号机": [ + "应采用区域控制信号机,并应与广水市交通信号控制系统兼容,信号机能接入已有系统平台,实现联网优化功能。", "1、控制功能:(1)区域协调控制:可对单个孤立交叉口、干道多个交叉口和关联性较强的交叉口群进行综合性地信号控制。", "1、控制功能:(2)线性协调控制:可对干道多个相邻交叉口进行协调控制。", "1、控制功能:(3)多时段控制:可根据交叉口的交通状况,将每天划分为多个不同的时段,每个时段配置不同的控制方案,能设置至少 10个时段、10种以上不同控制方案,能根据不同周日类型对方案进行调整。信号机能够根据内置时钟选择各个时段的控制方案,实现交叉口的合理控制。", @@ -254,7 +258,7 @@ if __name__ == "__main__": "2、采集功能:(2)信号机支持交通信息采集与统计,并支持交通流量共享。", "3、运维功能:(1)信号机能够自动检测地磁故障,若故障,能够自动上传故障信息至监控中心。" ], - "高清视频抓拍像机": [ + "▲高清视频抓拍像机": [ "1:摄像机:有效像素:≥900W像素", "1:摄像机:最低照度:彩色≤0.001lx", "1:摄像机:传感器类型:≥1英寸全局曝光 COMS/GMOS/GS COMS",