diff --git a/flask_app/main/table_content_extraction.py b/flask_app/main/table_content_extraction.py index b67ed5b..a678448 100644 --- a/flask_app/main/table_content_extraction.py +++ b/flask_app/main/table_content_extraction.py @@ -1,18 +1,31 @@ from docx import Document import json + def read_tables_from_docx(file_path): """读取DOCX文件中的表格数据,并以嵌套字典的形式返回.""" doc = Document(file_path) table_list = {} cur_title = [] + header = None for table in doc.tables: for i, row in enumerate(table.rows): + cell_texts = [cell.text.strip() for cell in row.cells] + + # 检查是否是表头 + if header is None: + header = cell_texts + continue # 跳过第一个表头行 + + # 如果遇到与第一个表头相同的行,跳过 + if cell_texts == header: + continue + cur_level = table_list temp_title = [] for j, cell in enumerate(row.cells): - text_str = cell.text.strip().replace(' ', '').replace('\n', '') # 移除键中的换行符 + text_str = cell.text.strip().replace(' ', '').replace('\n', '') if j < len(row.cells) - 1: if text_str == "": text_str = cur_title[j] if j < len(cur_title) else "<未识别到上级标题>" @@ -28,16 +41,16 @@ def read_tables_from_docx(file_path): if isinstance(cur_level[last_key], dict): cur_level[last_key] = f"\n{cell_text}" else: - cur_level[last_key] += f"\n{cell_text}" # 追加值到已有键 + cur_level[last_key] += f"\n{cell_text}" else: - cur_level[last_key] = cell_text # 初始化键的值 + cur_level[last_key] = cell_text else: last_key = f"第{i}行内容" if last_key in cur_level: if isinstance(cur_level[last_key], dict): cur_level[last_key] = f"\n{cell_text}" else: - cur_level[last_key] += f"\n{cell_text}" # 追加值到'第i行内容' + cur_level[last_key] += f"\n{cell_text}" else: cur_level[last_key] = cell_text cur_title = temp_title[:] @@ -82,6 +95,6 @@ def extract_tables_main(path, output_filename): print(f"The data has been processed and saved to '{output_filename}'.") if __name__ == "__main__": - path = 'C:\\Users\\Administrator\\Desktop\\招标文件\\招标03_tobidders_notice_table.docx' - output_filename = "C:\\Users\\Administrator\\Desktop\\招标文件\\truncate_output.json" # 前附表json文件 + path = 'C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest20\\zbtest20_17-22.docx' + output_filename = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest20\\truncate_output.json" # 前附表json文件 extract_tables_main(path, output_filename) diff --git a/flask_app/main/ttt.py b/flask_app/main/ttt.py index 3d7bad1..9bd994c 100644 --- a/flask_app/main/ttt.py +++ b/flask_app/main/ttt.py @@ -1,34 +1,23 @@ -import re +def add_entry_level1(data, combined_value): + # 假设这里我们基于某些逻辑添加了一个条目 + combined_value.append("Level 1 data: " + data) + add_entry_level2("Data from Level 1", combined_value) -def extract_key_value_from_line(line): - # 更新正则表达式以处理带序号和不带序号的情况 - pattern = r'^((?:\d+(?:\.\d+)*\.?\s*)?)(.+?)\s*/\s*$' - match = re.match(pattern, line) +def add_entry_level2(data, combined_value): + # 在第二层,我们再添加一个条目 + combined_value.append("Level 2 data: " + data) + add_entry_level3("Data from Level 2", combined_value) - if match: - # 获取键名,并去除多余的空格 - key = match.group(2).strip() - # 设置值为"无" - value = "无" - return {key: value} - return {} +def add_entry_level3(data, combined_value): + # 在第三层,我们添加最后一个条目 + combined_value.append("Level 3 data: " + data) -# 测试函数 -def test_extraction(): - test_cases = [ - "1.1.6 设计人 /", - "3.4 某个键名:/", - "5. 另一个键名 /", - "1.2.3.4 复杂序号键名:/", - "6.简单键名/", - "不带序号的行:/", - "纯文本/", - ] +# 主函数或测试代码 +def main(): + combined_value = [] + initial_data = "Initial Entry" + add_entry_level1(initial_data, combined_value) + print("Combined values after processing:", combined_value) - for case in test_cases: - result = extract_key_value_from_line(case) - print(f"Input: {case}") - print(f"Result: {result}\n") - -# 运行测试 -test_extraction() \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/flask_app/main/基础信息整合.py b/flask_app/main/基础信息整合.py index c831efe..5f720d7 100644 --- a/flask_app/main/基础信息整合.py +++ b/flask_app/main/基础信息整合.py @@ -1,3 +1,5 @@ +import json + from flask_app.main.json_utils import clean_json_string, nest_json_under_key,rename_outer_key, combine_json_results from flask_app.main.投标人须知正文提取指定内容 import extract_from_notice from flask_app.main.判断是否分包等 import judge_whether_main, read_questions_from_judge @@ -7,7 +9,7 @@ def combine_basic_info(baseinfo_list): combined_baseinfo_list = [] key_groups = { "招标人/代理信息": ["招标人","招标人联系方式", "招标代理机构","招标代理机构联系方式"], - "项目信息": ["工程名称", "招标编号","工程概况","招标范围","招标控制价","投标竞争下浮率","是否接受联合体投标"], + "项目信息": ["工程名称", "招标编号","工程概况","招标范围","招标控制价","投标竞争下浮率"], "关键时间/内容":["投标文件递交截止日期","递交方式","投标人要求澄清招标文件的截止时间","投标有效期","评标结果公示媒介"], "保证金相关":['质量保证金','退还投标保证金'], "其他信息":["重新招标、不再招标和终止招标","是否退还投标文件","费用承担"] @@ -45,6 +47,8 @@ def dynamic_key_handling(key_groups, detected_keys): for key in detected_keys: if "投标保证金" in key or "履约保证金" in key: key_groups["保证金相关"].append(key) + elif "是否接受联合体" in key: + key_groups["项目信息"].append(key) elif "联合体投标要求" in key: key_groups["项目信息"].append(key) elif "分包" in key: @@ -56,13 +60,23 @@ def dynamic_key_handling(key_groups, detected_keys): elif "偏离" in key: key_groups["其他信息"].append(key) + def judge_consortium_bidding(baseinfo_list): + updated_list = [] + accept_bidding = False for baseinfo in baseinfo_list: json_data = clean_json_string(baseinfo) # 检查 "是否接受联合体投标" 键是否存在且其值为 "是" - if json_data.get("是否接受联合体投标") == "是": - return True - return False + if "是否接受联合体投标" in json_data and json_data["是否接受联合体投标"] == "是": + accept_bidding = True + # 从字典中移除特定键值对 + json_data.pop("是否接受联合体投标", None) + # 将修改后的 json 数据转换回 JSON 字符串(如果需要) + updated_info = json.dumps(json_data) + updated_list.append(updated_info) + # 更新原始列表,如果你想保留修改 + baseinfo_list[:] = updated_list + return accept_bidding def project_basic_info(knowledge_name,truncate0,output_folder,clause_path): #投标人须知前附表 # 调用大模型回答项目基础信息 print("starting基础信息...") @@ -90,7 +104,7 @@ def project_basic_info(knowledge_name,truncate0,output_folder,clause_path): # judge_consortium = judge_consortium_bidding(baseinfo_list) #通过招标公告判断是否接受联合体投标 if judge_consortium: - judge_consortium_question = "该招标文件对于联合体投标的要求是怎样的,请按json格式给我提供信息,外层键名为'联合体投标要求'。" + judge_consortium_question = "该招标文件对于联合体投标的要求是怎样的,请按json格式给我提供信息,外层键名为'联合体投标要求',其中有一个嵌套键值对为:\"是否接受联合体投标\":\"是\"" judge_questions.append(judge_consortium_question) file_id=upload_file(truncate0) @@ -124,9 +138,9 @@ def project_basic_info(knowledge_name,truncate0,output_folder,clause_path): # if __name__ == "__main__": knowledge_name = "ztb" - output_folder="C:\\Users\\Administrator\\Desktop\\招标文件\\test2" - truncate0="C:\\Users\\Administrator\\Desktop\\招标文件\\test2\\zbtest10_tobidders_notice_table.pdf" - clause_path="C:\\Users\\Administrator\\Desktop\\招标文件\\output1\\clause1.json" + output_folder="C:\\Users\\Administrator\\Desktop\\fsdownload\\temp8\\3abb6e16-19db-42ad-9504-53bf1072dfe7" + truncate0="C:\\Users\\Administrator\\Desktop\\fsdownload\\temp8\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\ztbfile_tobidders_notice_table.pdf" + clause_path="C:\\Users\\Administrator\\Desktop\\fsdownload\\temp8\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\clause.json" res=project_basic_info(knowledge_name,truncate0,output_folder,clause_path) print(res) diff --git a/flask_app/main/形式响应评审.py b/flask_app/main/形式响应评审.py index a1505fc..c53308d 100644 --- a/flask_app/main/形式响应评审.py +++ b/flask_app/main/形式响应评审.py @@ -26,6 +26,7 @@ prompt = """ 请注意,上述技能执行时将直接利用并参考${document1}的具体内容,以确保所有产出紧密相关且高质量。 """ + def extract_matching_keys(json_data): # 函数首先检查输入 json_data 是否为字符串类型。如果是,它会使用 json.loads() 将字符串解析为字典。 if isinstance(json_data, str): @@ -151,7 +152,7 @@ def process_reviews(original_dict_data,knowledge_name, truncate0_jsonpath,clause print(f"Error processing response for query index {_}: {e}") # Assume JSON file paths are defined or configured correctly - combined_results = process_and_merge_entries(entries_with_numbers, truncate0_jsonpath, clause_json_path) #脚本提取的要求 + combined_results = process_and_merge_entries(entries_with_numbers, truncate0_jsonpath, clause_json_path) #脚本提取的要求 [{'xxx': '3.7.4(5)'}] updated_json = update_json_data(original_dict_data, combined_results, second_response_list) return updated_json diff --git a/flask_app/main/截取pdf.py b/flask_app/main/截取pdf.py index f551b82..aca9210 100644 --- a/flask_app/main/截取pdf.py +++ b/flask_app/main/截取pdf.py @@ -142,9 +142,9 @@ def truncate_pdf_multiple(input_path, output_folder): return truncate_files if __name__ == "__main__": - input_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标03.pdf" - output_folder = "C:\\Users\\Administrator\\Desktop\\招标文件\\test" + input_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹" + output_folder = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹" # truncate_pdf_multiple(input_path,output_folder) - selection = 5 # 例如:1 - 投标人须知前附表, 2 - 评标办法, 3 - 投标人须知正文 4-资格审查条件 + selection = 4 # 例如:1 - 投标人须知前附表, 2 - 评标办法, 3 - 投标人须知正文 4-资格审查条件 5-无效标 generated_files = truncate_pdf_main(input_path, output_folder, selection) # print("生成的文件:", generated_files) diff --git a/flask_app/main/招标文件解析.py b/flask_app/main/招标文件解析.py index 9795361..de7b71f 100644 --- a/flask_app/main/招标文件解析.py +++ b/flask_app/main/招标文件解析.py @@ -207,7 +207,8 @@ def main_processing(output_folder,downloaded_file_path,file_type,unique_id): # # # deleteKnowledge(processed_data['knowledge_index']) -#TODO:如果上传的是pdf转过的docx文件,那么提取打勾符号就会有问题 + +#TODO:如果上传的是pdf转过的docx文件,那么提取打勾符号就会有问题 zbtest20 跳转涉及二级跳转 对于跳转到第一章 招标公告的要做额外处理 资格审查位置在第一章后面。如果未截取成功,需要作额外处理 logger不能保存控制台输出 if __name__ == "__main__": output_folder = "C:\\Users\\Administrator\\Desktop\\招标文件\\test" diff --git a/flask_app/main/根据条款号整合json.py b/flask_app/main/根据条款号整合json.py index d8b6fd3..a4f4b9c 100644 --- a/flask_app/main/根据条款号整合json.py +++ b/flask_app/main/根据条款号整合json.py @@ -1,4 +1,16 @@ import json +import re + + +def extract_content_after_special_chars(content): + """ + 提取特定符号后的内容,直到遇到结束符号。 + """ + pattern = r'[\x01\x02☑√团]([^□]+)' + match = re.search(pattern, content) + if match: + return match.group(1).strip() # 提取匹配的内容,并去除多余空格 + return content # 如果没有找到匹配,返回原内容 def load_json(file_path): """ @@ -36,7 +48,6 @@ def find_entries_in_jsons(entries, json_primary, json_secondary): found_in_primary = process_json_with_subentries(json_primary, value, combined_value) if not found_in_primary: process_json_with_subentries(json_secondary, value, combined_value) - if combined_value: results[key] = "\n".join(combined_value) return results @@ -45,9 +56,13 @@ def process_json_with_subentries(json_data, value, combined_value): """ 处理JSON数据,寻找指定的条目,考虑全角和半角括号。 """ - value = standardize_brackets(value) - if "(" in value and ")" in value: - base_key, subentry_key = value.split("(") + value = standardize_brackets(value) #将1.11(1)->1.11(1) + if "(" in value and ")" in value: #存在()的情况 + first_content=get_values_only(json_data.get(value)) + if first_content: + combined_value.append(first_content) + return True + base_key, subentry_key = value.split("(") #base_key:1.11 subentry_key:(1) subentry_key = "(" + subentry_key content = json_data.get(base_key.strip()) if content: @@ -59,18 +74,19 @@ def process_json_with_subentries(json_data, value, combined_value): else: return False else: - return process_json(json_data, value, combined_value) + return extarct_normal(json_data, value, combined_value) -def process_json(json_data, value, combined_value): +def extarct_normal(json_data, value, combined_value): found_subentries = check_and_collect_subentries(json_data, value, combined_value) - if not found_subentries: - content = json_data.get(value, "") + if not found_subentries: #若无子条目,直接查找 + content = json_data.get(value, "") #从一个字典 json_data 中获取与键名 value 相关联的值,默认值为"" if content: combined_value.append(get_values_only(content)) return True return found_subentries +#用于查找和处理由主条目派生的子条目 eg:如果 value 是 "1.1",它将查找所有像 "1.1.1", "1.1.2" 等以 "1.1." 开头的键,不会匹配仅为 "1.1" 的键。 def check_and_collect_subentries(json_data, value, combined_value): found_subentries = False subentry_index = 1 @@ -82,6 +98,7 @@ def check_and_collect_subentries(json_data, value, combined_value): found_subentries = True return found_subentries +#针对于有子序号()的情况 def extract_specific_subentry(content, subentry_key): """ 提取指定的子条目文本,考虑全角和半角括号。 @@ -101,9 +118,24 @@ def extract_specific_subentry(content, subentry_key): def get_values_only(content): if isinstance(content, dict): - return " / ".join(content.values()) - return content + # 如果内容是字典,首先将字典的值转换为字符串 + content = " / ".join(content.values()) + # 检查并处理特殊字符 + return extract_content_after_special_chars(content) + +def extract_content_after_special_chars(content): + """ + 提取特定符号后的内容,直到遇到结束符号或内容末尾。 + """ + # 定义搜索特殊字符的正则表达式 + pattern = r'[\x01\x02☑√团]([^□]*)' + match = re.search(pattern, content) + if match: + # 如果找到匹配,返回从特殊字符之后到下一个□或到内容末尾的部分 + return match.group(1).strip() # 去除多余空白字符 + # 如果没有找到特殊字符,返回原始内容 + return content def standardize_brackets(value): """ 将输入中的所有半角括号转换为全角括号。 @@ -119,9 +151,9 @@ def process_and_merge_entries(entries_with_numbers, primary_json_path, secondary if __name__ == "__main__": # Hypothetical entries and file paths for testing # entries_with_numbers = [{'形式评审标准.投标文件签字盖章': '3.7.3(3)'}, {'形式评审标准.多标段投标': '10.1'}, {'形式评审标准.“技术暗标”': '3.7.4(5)'}, {'响应性评审标准.投标内容': '1.3.1'}, {'响应性评审标准.工期': '1.3.2'}, {'响应性评审标准.工程质量': '1.3.3'}, {'响应性评审标准.投标有效期': '3.3.1'}, {'响应性评审标准.投标保证金': '3.4.1'}, {'响应性评审标准.分包计划': '1.11'}] - entries_with_numbers=[{'xxx': '3.7.4(5)'}] - primary_json_path = 'C:\\Users\\Administrator\\Desktop\\招标文件\\output1\\truncate_output3.json' - secondary_json_path = 'C:\\Users\\Administrator\\Desktop\\招标文件\\output1\\clause3.json' + entries_with_numbers=[{'xxx': '3.4.1'}] + primary_json_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\temp8\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\truncate_output.json' + secondary_json_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\temp8\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\clause.json' # Since this is just a test block, make sure these paths point to actual JSON files with the appropriate structure try: diff --git a/flask_app/main/资格审查模块.py b/flask_app/main/资格审查模块.py index 34ca78f..7c1b78a 100644 --- a/flask_app/main/资格审查模块.py +++ b/flask_app/main/资格审查模块.py @@ -15,7 +15,7 @@ def combine_review_standards(truncate1,truncate3,knowledge_name,truncate0_jsonpa user_query_1 = "根据该文档中的评标办法前附表,请你列出该文件中的形式评审标准和响应性评审标准和资格评审标准,请以json格式返回,外层键名为'形式评审标准'和'响应性评审标准'和'资格评审标准',嵌套键名为'评审因素'中的内容,相应的键值为对应'评审标准'中的内容。" results = qianwen_long(file_id, user_query_1) original_dict_data = extract_content_from_json(results) - qualification_review = original_dict_data.pop('资格评审标准', '默认值或None') + qualification_review = original_dict_data.pop('资格评审标准', '默认值或None') #qianwen-long有关资格评审的内容 final_qualify_json=process_qualification(qualification_review,truncate3,knowledge_name) form_response_dict=process_reviews(original_dict_data, knowledge_name, truncate0_jsonpath, clause_path) print("形式响应评审done") diff --git a/flask_app/main/资格评审.py b/flask_app/main/资格评审.py index 5ed586c..dbccfba 100644 --- a/flask_app/main/资格评审.py +++ b/flask_app/main/资格评审.py @@ -53,11 +53,11 @@ def extract_matching_keys_qual(dict_data): non_matching_keys[key] = value return matching_keys,non_matching_keys #matching:['资质条件', '财务状况'] non_matching_keys:{'营业执照': '具备有效的营业执照', '施工机械设备': '具备完善的施工设备'} -def process_qualification(qualification_review,truncate4,knowledge_name): +def process_qualification(qualification_review,truncate3,knowledge_name): # 资格评审 matching_keys_list, non_matching_dict = extract_matching_keys_qual(qualification_review) - user_querys = generate_qual_question(matching_keys_list) # 生成提问->附件:资格审查 - file_id2 = upload_file(truncate4) + user_querys = generate_qual_question(matching_keys_list) # 生成提问->‘附件:资格审查’ + file_id2 = upload_file(truncate3) results2 = multi_threading(user_querys, "", file_id2, 2) # 资格评审表 res_list = [] if not results2: diff --git a/flask_app/static/提示词/前两章提问总结.txt b/flask_app/static/提示词/前两章提问总结.txt index ab917c1..081e1e8 100644 --- a/flask_app/static/提示词/前两章提问总结.txt +++ b/flask_app/static/提示词/前两章提问总结.txt @@ -3,7 +3,7 @@ #该招标文件的工程概况(或项目概况)是?招标范围是?招标控制价(可指代投标限价、投资概算金额、工程概算金额、合同估算价,但非监理费用)是?该项目的计划工期(监理服务期)是?该项目是否接受联合体投标?请按json格式给我提供信息,键名分别为'工程概况','招标范围','招标控制价','计划工期','是否接受联合体投标',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知','是否接受联合体投标'的键值仅限于'是'、'否'、'未知'。 2.该招标文件的工程概况(或项目概况)是?招标范围是?请按json格式给我提供信息,键名分别为'工程概况','招标范围',若存在嵌套信息,嵌套内容键名以文件中对应字段命名,若存在未知信息,在对应的键值中填'未知'。 -3.该招标文件的招标控制价(可指代投标限价、投资概算金额、工程概算金额、合同估算价,但非监理费用)是?该项目是否接受联合体投标?请按json格式给我提供信息,键名分别为'招标控制价','是否接受联合体投标',若存在未知信息,在对应的键值中填'未知','是否接受联合体投标'的键值仅限于'是'、'否'、'未知'。 +3.该招标文件的招标控制价(可指代投标限价、投资概算金额、工程概算金额、合同估算价,但非监理费用)是?请按json格式给我提供信息,键名为'招标控制价',若存在未知信息,在对应的键值中填'未知'。 4.投标文件递交截止日期是?递交方式是?请按json格式给我提供信息,键名分别是'投标文件递交截止日期','递交方式',若存在未知信息,在对应的键值中填'未知'。 @@ -29,3 +29,5 @@ 10.求澄清的招标文件截止时间是?请以json的格式给我提供信息,键名是'投标人要求澄清招标文件的截止时间',若存在未知信息,在对应的键值中填'未知'。 11.该文档要求扣留的质量保证金百分比是多少,请以json格式给我提供信息,键名为'质量保证金',如果没有则以'未知'填充。 + +12.该项目是否接受联合体投标?请按json格式给我提供信息,键名为'是否接受联合体投标','是否接受联合体投标'的键值仅限于'是'、'否'、'未知'。