From ee16d5e4b4e95cb36ce4a98eedcc2762c3d3e7eb Mon Sep 17 00:00:00 2001 From: zy123 <646228430@qq.com> Date: Tue, 3 Sep 2024 09:36:18 +0800 Subject: [PATCH] 9.3 --- flask_app/main/docx截取docx.py | 50 +++++++++++++++++++ flask_app/main/format_change.py | 2 +- flask_app/main/ttt.py | 45 +++++++++++------ flask_app/main/截取pdf.py | 31 ++++++------ .../main/投标人须知正文条款提取成json文件.py | 9 ++-- flask_app/main/招标文件解析.py | 39 +++++++++------ flask_app/main/无效标和废标和禁止投标整合.py | 30 ++++++----- flask_app/main/禁止投标情形.py | 29 ++++++----- flask_app/main/资格审查模块.py | 4 +- flask_app/main/转化格式/pydocx_p2d.py | 0 flask_app/货物标/test.py | 31 ++++++++++++ 11 files changed, 194 insertions(+), 76 deletions(-) create mode 100644 flask_app/main/docx截取docx.py create mode 100644 flask_app/main/转化格式/pydocx_p2d.py create mode 100644 flask_app/货物标/test.py diff --git a/flask_app/main/docx截取docx.py b/flask_app/main/docx截取docx.py new file mode 100644 index 0000000..ed06b20 --- /dev/null +++ b/flask_app/main/docx截取docx.py @@ -0,0 +1,50 @@ +from docx import Document +import re +import os + + +def copy_docx(source_path): + doc = Document(source_path) # 打开源文档 + output_folder = os.path.dirname(source_path) + + # 获取原文件名并添加后缀 + original_file_name = os.path.basename(source_path) + file_name_without_ext, file_ext = os.path.splitext(original_file_name) + modified_file_name = file_name_without_ext + "_invalid" + file_ext + destination_path = os.path.join(output_folder, modified_file_name) + + new_doc = Document() # 创建新文档 + + # 定义正则表达式模式 + begin_pattern = re.compile(r'第[一二三四五六七八九十]+章\s*招标公告|第一卷') + end_pattern = re.compile(r'第[一二三四五六七八九十]+章\s*合同|:清标报告|:清标报告') + + # 寻找最后一个begin_pattern的位置 + last_begin_index = -1 + for i, paragraph in enumerate(doc.paragraphs): + if begin_pattern.search(paragraph.text): + last_begin_index = i + + # 从最后一个匹配的begin_pattern开始复制,直到end_pattern + if last_begin_index != -1: + for i, paragraph in enumerate(doc.paragraphs[last_begin_index:], start=last_begin_index): + new_para = new_doc.add_paragraph(style=paragraph.style) + for run in paragraph.runs: + new_run = new_para.add_run(run.text) + new_run.bold = run.bold + new_run.italic = run.italic + new_run.underline = run.underline + if run.font.color: + new_run.font.color.rgb = run.font.color.rgb + new_run.font.size = run.font.size + + if end_pattern.search(paragraph.text): + break + + new_doc.save(destination_path) # 保存新文档 + + +# 调用函数 +if __name__ == '__main__': + source_path = "C:\\Users\\Administrator\\Desktop\\货物标\\output\\zbtest13.docx" + copy_docx(source_path) diff --git a/flask_app/main/format_change.py b/flask_app/main/format_change.py index fe7eb44..b13b88b 100644 --- a/flask_app/main/format_change.py +++ b/flask_app/main/format_change.py @@ -58,7 +58,7 @@ def docx2pdf(local_path_in): if __name__ == '__main__': # 替换为你的文件路径和API URL - local_path_in="C:\\Users\\Administrator\\Desktop\\招标文件\\zbtest16_invalid.docx" + local_path_in="C:\\Users\\Administrator\\Desktop\\招标文件\\output1\\test111.pdf" # pdf2docx(local_path_in) downloaded_file=docx2pdf(local_path_in) print(downloaded_file) diff --git a/flask_app/main/ttt.py b/flask_app/main/ttt.py index eb3d9d5..59aa7fd 100644 --- a/flask_app/main/ttt.py +++ b/flask_app/main/ttt.py @@ -1,18 +1,35 @@ import re +import ast -# 正则表达式 -pattern = re.compile(r'第[一二三四五六七八九十百千]+章.*?(?:项目|服务|商务).*?要求') +def process_string_list(string_list): + # 使用正则表达式匹配方括号内的内容 + match = re.search(r'\[(.*?)\]', string_list) + if match: + # 获取匹配的内容,即方括号内的部分 + content_inside_brackets = match.group(1) + if content_inside_brackets: # 检查内容是否为空 + # 检查内容是否是数字列表 + if all(item.strip().isdigit() for item in content_inside_brackets.split(',')): + # 如果是数字,不用加引号,直接保留数字 + formatted_list = '[' + ', '.join(item.strip() for item in content_inside_brackets.split(',') if item.strip()) + ']' + else: + # 如果不全是数字,按字符串处理 + formatted_list = '[' + ', '.join(f"'{item.strip()}'" for item in content_inside_brackets.split(',') if item.strip()) + ']' + else: + return [] # 直接返回空列表如果内容为空 -# 示例文本进行测试 -text = """ -第一章项目技术、服务及商务要求 -第二章 服务细节要求 -第三章 商务处理要求 -第四章 项目安排要求 -第五章 安全要求 -""" + # 使用 ast.literal_eval 来解析格式化后的字符串 + try: + actual_list = ast.literal_eval(formatted_list) + return actual_list + except SyntaxError as e: + print(f"Error parsing list: {e}") + return [] + else: + # 如果没有匹配到内容,返回空列表 + return [] -# 查找所有匹配 -matches = pattern.findall(text) -for match in matches: - print(match) +# 测试代码 +test_string = "[1,2,哈哈]" +result = process_string_list(test_string) +print(result) # 现在应该输出: [1, 2, 4, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22] diff --git a/flask_app/main/截取pdf.py b/flask_app/main/截取pdf.py index 5b5591a..f551b82 100644 --- a/flask_app/main/截取pdf.py +++ b/flask_app/main/截取pdf.py @@ -9,8 +9,9 @@ def clean_page_numbers(text): # 删除结尾的页码 cleaned_text = re.sub(r'\s+\d+\s*$', '', cleaned_text) # 删除形如 /129 的页码 - cleaned_text = re.sub(r'\s*\/\s*\d+\s*', '', cleaned_text) + cleaned_text = re.sub(r'\s*\/\s*\d+\\s*', '', cleaned_text) return cleaned_text + def extract_pages(pdf_path, output_folder, chapter_pattern, begin_page, end_phrase_pattern, output_suffix): # 打开PDF文件 pdf_document = PdfReader(pdf_path) @@ -38,7 +39,6 @@ def extract_pages(pdf_path, output_folder, chapter_pattern, begin_page, end_phra end_page = i break - # 确保找到了起始和结束页面 if start_page is None or end_page is None: print(f"未找到起始或结束页在文件 {pdf_path} 中!") @@ -111,6 +111,14 @@ def truncate_pdf_main(input_path, output_folder, selection): ] output_suffix = "tobidders_notice" elif selection==4: + # 配置用于 "资格审查条件" 的正则表达式模式和短语 + appendix_pattern = r'^附录(?:一)?[::]|^附件(?:一)?[::]|^附表(?:一)?[::]' + pattern = re.compile(appendix_pattern) + begin_page = 5 + end_phrases = [r'评标办法正文', r'评标办法', appendix_pattern] + output_suffix = "qualification" + elif selection == 5: + # 配置用于 "无效标" 的正则表达式模式和短语 pattern = re.compile(r'第[一二三四五六七八九十]+章\s*招标公告|第一卷|招标编号:|招标编号:') begin_page = 0 end_phrases = [ @@ -118,13 +126,7 @@ def truncate_pdf_main(input_path, output_folder, selection): r':清标报告',# 添加了新的匹配项 r':清标报告' ] - output_suffix="invalid" - elif selection==5: - appendix_pattern = r'^附录(?:一)?[::]|^附件(?:一)?[::]|^附表(?:一)?[::]' - pattern = re.compile(appendix_pattern) - begin_page=5 - end_phrases = [r'评标办法正文', r'评标办法',appendix_pattern] - output_suffix="qualification" + output_suffix = "invalid" else: print("无效的选择") return None @@ -134,16 +136,15 @@ def truncate_pdf_main(input_path, output_folder, selection): def truncate_pdf_multiple(input_path, output_folder): truncate_files = [] - for selection in range(1, 6): + for selection in range(1, 5): files = truncate_pdf_main(input_path, output_folder, selection) truncate_files.extend(files) return truncate_files if __name__ == "__main__": - input_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest5.pdf" + input_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标03.pdf" output_folder = "C:\\Users\\Administrator\\Desktop\\招标文件\\test" - truncate_pdf_multiple(input_path,output_folder) - # selection = 5 # 例如:1 - 投标人须知前附表, 2 - 评标办法, 3 - 投标人须知正文 4-招标公告-合同条款前 - # generated_files = truncate_pdf_main(input_path, output_folder, selection) + # truncate_pdf_multiple(input_path,output_folder) + selection = 5 # 例如:1 - 投标人须知前附表, 2 - 评标办法, 3 - 投标人须知正文 4-资格审查条件 + generated_files = truncate_pdf_main(input_path, output_folder, selection) # print("生成的文件:", generated_files) - diff --git a/flask_app/main/投标人须知正文条款提取成json文件.py b/flask_app/main/投标人须知正文条款提取成json文件.py index 90e5475..1b1e472 100644 --- a/flask_app/main/投标人须知正文条款提取成json文件.py +++ b/flask_app/main/投标人须知正文条款提取成json文件.py @@ -33,7 +33,8 @@ def extract_section(text, start_keyword, end_phrases): end_index = len(text) for phrase in end_phrases: - match = re.search(phrase, text[start_index:]) + # Use multiline mode with `re.MULTILINE` + match = re.search(phrase, text[start_index:], re.MULTILINE) #Hello, world!\nWelcome to OpenAI. 在多行字符串多,要 re.MULTILINE以匹配每一行的开头,否则只会匹配字符串的开头。 if match: end_index = start_index + match.start() break @@ -118,10 +119,8 @@ def convert_to_json(file_path, start_word, end_phrases): text = extract_text_from_pdf(file_path) else: raise ValueError("Unsupported file format") - # 提取从 start_word 开始到 end_phrases 结束的内容 text = extract_section(text, start_word, end_phrases) - parsed_data = parse_text_by_heading(text) return parsed_data @@ -138,13 +137,13 @@ def convert_clause_to_json(input_path,output_folder): return output_path if __name__ == "__main__": - file_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\temp6\\48e650ff-70eb-48df-874c-66c8abbcd89d\\ztbfile_tobidders_notice.pdf' + file_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\temp6\\713022ff-27d9-43e3-9cc9-2752effbfd66\\ztbfile_tobidders_notice.pdf' start_word = "投标人须知正文" end_phrases = [ r'^第[一二三四五六七八九十]+章\s+评标办法', r'^评标办法前附表', r'^附录:', r'^附录一:', r'^附件:', r'^附件一:', r'^附表:', r'^附表一:', r'^附录:', r'^附录一:', r'^附件:', r'^附件一:', r'^附表:', r'^附表一:', ] - output_folder = 'C:\\Users\\Administrator\\Desktop\\招标文件' + output_folder = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\temp6\\713022ff-27d9-43e3-9cc9-2752effbfd66' try: output_path = convert_clause_to_json(file_path,output_folder) print(f"Final JSON result saved to: {output_path}") diff --git a/flask_app/main/招标文件解析.py b/flask_app/main/招标文件解析.py index 00cb75f..7cf49e6 100644 --- a/flask_app/main/招标文件解析.py +++ b/flask_app/main/招标文件解析.py @@ -15,6 +15,7 @@ from flask_app.main.基础信息整合 import project_basic_info from flask_app.main.资格审查模块 import combine_review_standards from flask_app.main.商务标技术标整合 import combine_evaluation_standards from flask_app.main.format_change import pdf2docx,docx2pdf +from flask_app.main.docx截取docx import copy_docx global_logger=None def get_global_logger(unique_id): @@ -40,20 +41,30 @@ def preprocess_files(output_folder, downloaded_file_path, file_type, unique_id): index = addfileToKnowledge(docx_path, knowledge_name) # 调用截取PDF多次 - truncate_files = truncate_pdf_multiple(pdf_path, output_folder) # [前附表, 评标办法, 须知正文, 无效标] + 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 # 无效标 + + if(file_type==1): + invalid_docpath=copy_docx(downloaded_file_path) + else: + invalid_docpath = pdf2docx(truncate_files[4]) # 无效标 truncate0_jsonpath = os.path.join(output_folder, "truncate_output.json") extract_tables_main(truncate0_docpath, truncate0_jsonpath) # 投标人须知前附表docx->json,从表格提取数据 + truncate0=truncate_files[0] + truncate1=truncate_files[1] + truncate3=truncate_files[3] clause_path = convert_clause_to_json(truncate_files[2], output_folder) # 投标人须知正文条款pdf->json return { + 'truncate0':truncate0, + 'truncate1':truncate1, + 'truncate3':truncate3, 'knowledge_index': index, 'knowledge_name':knowledge_name, - 'truncate_files': truncate_files, 'truncate0_jsonpath': truncate0_jsonpath, 'clause_path': clause_path, 'invalid_docpath': invalid_docpath @@ -66,9 +77,9 @@ def fetch_project_basic_info(knowledge_name,truncate0,output_folder,clause_path) return basic_res #形式、响应、资格评审 -def fetch_review_standards(truncate1,truncate4,knowledge_name,truncate0_jsonpath,clause_path): +def fetch_review_standards(truncate1,truncate3,knowledge_name,truncate0_jsonpath,clause_path): global_logger.info("starting资格审查...") - review_standards_res=combine_review_standards(truncate1,truncate4,knowledge_name,truncate0_jsonpath,clause_path) + review_standards_res=combine_review_standards(truncate1,truncate3,knowledge_name,truncate0_jsonpath,clause_path) global_logger.info("资格审查done") return review_standards_res @@ -80,10 +91,10 @@ def fetch_evaluation_standards(truncate1): #评标办法前附表 return evaluation_standards_res #无效、废标项解析 -def fetch_invalid_requirements(invalid_docpath,output_folder,truncate0_jsonpath,clause_path,truncate4): +def fetch_invalid_requirements(invalid_docpath,output_folder,truncate0_jsonpath,clause_path,truncate3): #废标项要求:千问 global_logger.info("starting无效标与废标...") - find_invalid_res=combine_find_invalid(invalid_docpath, output_folder, truncate0_jsonpath,clause_path,truncate4) + find_invalid_res=combine_find_invalid(invalid_docpath, output_folder, truncate0_jsonpath,clause_path,truncate3) global_logger.info("无效标与废标done...") return find_invalid_res @@ -104,6 +115,8 @@ def fetch_bid_opening(clause_path): global_logger.info("开评定标流程done...") return qualify_nested_res + +#TODO:如果用户上传pdf,python-docx转为docx,处理无效投标 表格地方单独提取 表格里的'废标'无法提取 docx截取docx出错 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) @@ -115,14 +128,14 @@ def main_processing(output_folder,downloaded_file_path,file_type,unique_id): # # 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['truncate0'], output_folder, processed_data['clause_path']), - 'review_standards': executor.submit(fetch_review_standards, processed_data['truncate_files'][1],processed_data['truncate_files'][4], + 'review_standards': executor.submit(fetch_review_standards, processed_data['truncate1'],processed_data['truncate3'], processed_data['knowledge_name'], processed_data['truncate0_jsonpath'], processed_data['clause_path']), - 'evaluation_standards': executor.submit(fetch_evaluation_standards, processed_data['truncate_files'][1]), + 'evaluation_standards': executor.submit(fetch_evaluation_standards, processed_data['truncate1']), '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]), + output_folder, processed_data['truncate0_jsonpath'],processed_data['clause_path'],processed_data['truncate3']), 'bidding_documents_requirements': executor.submit(fetch_bidding_documents_requirements, processed_data['clause_path']), 'opening_bid': executor.submit(fetch_bid_opening, processed_data['clause_path']) @@ -148,7 +161,7 @@ def main_processing(output_folder,downloaded_file_path,file_type,unique_id): # deleteKnowledge(processed_data['knowledge_index']) return final_result_path -#TODO:{ + #目前返回结果: # "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日。\"
}
}
}" #} @@ -194,8 +207,6 @@ def main_processing(output_folder,downloaded_file_path,file_type,unique_id): # # # deleteKnowledge(processed_data['knowledge_index']) - -#TODO:近三年业绩可能是按照投标文件的来的/分模块返回结果/对于上传docx文件进行优化 if __name__ == "__main__": output_folder = "C:\\Users\\Administrator\\Desktop\\招标文件\\test" diff --git a/flask_app/main/无效标和废标和禁止投标整合.py b/flask_app/main/无效标和废标和禁止投标整合.py index 877ba85..dbc2b9e 100644 --- a/flask_app/main/无效标和废标和禁止投标整合.py +++ b/flask_app/main/无效标和废标和禁止投标整合.py @@ -7,6 +7,7 @@ from flask_app.main.json_utils import combine_json_results, nest_json_under_key from flask_app.main.通义千问long import upload_file, qianwen_long from concurrent.futures import ThreadPoolExecutor from flask_app.main.禁止投标情形 import find_forbidden +from 禁止投标情形 import process_string_list #如果当前段落有序号,则向下匹配直接遇到相同的序号样式 #如果当前段落无序号,则向下匹配序号,把若干同类的序号都摘出来。 @@ -85,7 +86,7 @@ def clean_dict_datas(extracted_contents, keywords,excludes): #让正则表达 if any(exclude in data for exclude in excludes): continue # 如果包含任何排除字符串,跳过这个数据 # 去掉开头的序号,包括字母+数字的格式 以及括号+数字 - pattern = r'^\s*([((]\d+[))]|[A-Za-z]?\d+(\.\d+)*(\s|\.|、)?)' + pattern = r'^\s*([((]\d+[))]|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、)?)' data = re.sub(pattern, '', data).strip() keyword_match = re.search(keywords, data) if keyword_match: @@ -106,14 +107,14 @@ def clean_dict_datas(extracted_contents, keywords,excludes): #让正则表达 all_texts1.append(cleaned_text) # 将处理后的文本添加到结果列表 else: - pattern = r'^\s*([((]\d+[))]|[A-Za-z]?\d+(\.\d+)*(\s|\.|、)?)' + pattern = r'^\s*([((]\d+[))]|[A-Za-z]?\d+\s*(\.\s*\d+)*(\s|\.|、)?)' data = re.sub(pattern, '', text_list[0]).strip() # 将修改后的第一个元素和剩余的元素连接起来 text_list[0] = data # 更新列表中的第一个元素 joined_text = "\n".join(text_list) # 如果列表中有多个元素,则连接它们 all_texts2.append(joined_text) # 将每个列表的内容添加到 all_texts 中 - return all_texts1,all_texts2 + return all_texts1,all_texts2 #all_texts1要额外用gpt all_text2直接返回结果 def find_sentences_with_keywords(data, keywords, follow_up_keywords): """递归查找并返回包含关键词的句子列表,并根据是否存在后续关键词分别存储到两个列表中。""" sentences1 = [] # 保存没有后续关键词的情况 @@ -236,8 +237,9 @@ def handle_query(file_path, user_query, output_file, result_key, keywords, trunc print("starting qianwen-long...") qianwen_ans = qianwen_long(file_id, user_query) selected_contents = [] - num_list = json.loads(qianwen_ans) + num_list = process_string_list(qianwen_ans) print(num_list) + for index in num_list: if index - 1 < len(qianwen_txt): content = qianwen_txt[index - 1] # 转换序号为索引(假设序号从1开始) @@ -253,14 +255,14 @@ def handle_query(file_path, user_query, output_file, result_key, keywords, trunc res = {result_key: ""} # Set the response to empty if no contents were extracted return res -def combine_find_invalid(file_path, output_dir, truncate_json_path,clause_path,truncate4): +def combine_find_invalid(file_path, output_dir, truncate_json_path,clause_path,truncate3): print("starting无效标与废标...") queries = [ (r'否\s*决|无\s*效\s*投\s*标|被\s*拒\s*绝|予\s*以\s*拒\s*绝|投\s*标\s*失\s*效|投\s*标\s*无\s*效', - "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号。", + "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", os.path.join(output_dir, "temp1.txt"), "否决和无效投标情形"), (r'废\s*标', - "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:废标项的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号。", + "以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:废标项的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号,若情况不存在,返回[]。", os.path.join(output_dir, "temp2.txt"), "废标项") ] results = [] @@ -277,7 +279,9 @@ def combine_find_invalid(file_path, output_dir, truncate_json_path,clause_path,t for future in futures: results.append(future.result()) - forbidden_res = find_forbidden(truncate_json_path, clause_path, truncate4) + #禁止投标 + print("starting不得存在的情形...") + forbidden_res = find_forbidden(truncate_json_path, clause_path, truncate3) results.append(forbidden_res) combined_dict = {} @@ -288,15 +292,15 @@ def combine_find_invalid(file_path, output_dir, truncate_json_path,clause_path,t return nest_json_under_key(combined_dict, "无效标与废标项") -#TODO:1.运行时间约80s,如果成为短板需要优化多线程 2.没有提取评标办法前附表中的表格 3.提取表格时根据中文的句号分割 4.qianwen-long存在bug +#TODO:1.运行时间约80s,如果成为短板需要优化多线程 if __name__ == '__main__': start_time = time.time() truncate_json_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\truncate_output.json" clause_path="C:\\Users\\Administrator\\Desktop\\招标文件\\clause.json" - truncate4="C:\\Users\\Administrator\\Desktop\\招标文件\\zbtest12_qualification.pdf" - output_dir = "C:\\Users\\Administrator\\Desktop\\招标文件" - doc_path = 'C:\\Users\\Administrator\\Desktop\\招标文件\\招标03.docx' - results = combine_find_invalid(doc_path, output_dir,truncate_json_path,clause_path,truncate4) + truncate3="C:\\Users\\Administrator\\Desktop\\招标文件\\zbtest12_qualification.pdf" + output_dir = "C:\\Users\\Administrator\\Desktop\\货物标\\output" + doc_path = 'C:\\Users\\Administrator\\Desktop\\货物标\\output\\zbtest11_invalid.docx' + results = combine_find_invalid(doc_path, output_dir,truncate_json_path,clause_path,truncate3) end_time = time.time() print("Elapsed time:", str(end_time - start_time)) print("Results:", results) diff --git a/flask_app/main/禁止投标情形.py b/flask_app/main/禁止投标情形.py index 436477c..235be36 100644 --- a/flask_app/main/禁止投标情形.py +++ b/flask_app/main/禁止投标情形.py @@ -105,10 +105,16 @@ def process_string_list(string_list): # 获取匹配的内容,即方括号内的部分 content_inside_brackets = match.group(1) if content_inside_brackets: # 检查内容是否为空 - # 将每个项目用引号包裹,并确保适当的空格和逗号处理 - formatted_list = '[' + ', '.join(f"'{item.strip()}'" for item in content_inside_brackets.split(',') if item.strip()) + ']' + # 检查内容是否是数字列表 + if all(item.strip().isdigit() for item in content_inside_brackets.split(',')): + # 如果是数字,不用加引号,直接保留数字 + formatted_list = '[' + ', '.join(item.strip() for item in content_inside_brackets.split(',') if item.strip()) + ']' + else: + # 如果不全是数字,按字符串处理 + formatted_list = '[' + ', '.join(f"'{item.strip()}'" for item in content_inside_brackets.split(',') if item.strip()) + ']' else: return [] # 直接返回空列表如果内容为空 + # 使用 ast.literal_eval 来解析格式化后的字符串 try: actual_list = ast.literal_eval(formatted_list) @@ -123,18 +129,17 @@ def find_forbidden(truncate_json_path,clause_path,truncate4): #投标人须 # output_filename="merged.pdf" # paths=[truncate1,truncate4] # merged_filepath=merge_pdfs(paths,output_filename) #暂时废弃,评分前附表中的在'否决投标'中摘录了。 + file_id=upload_file(truncate4) - #user_query_forbidden = "该招标文件规定的投标人不得存在的其他情形有哪些,请按json列表格式给我提供信息,键名为'不得存在的其他情形',请你不要回答有关\"信誉要求\"的内容,若文件中未说明,请在键值中填'未知'。" + # user_query_forbidden = "该招标文件规定的投标人不得存在的其他情形有哪些,请按json列表格式给我提供信息,键名为'不得存在的其他情形',请你不要回答有关\"信誉要求\"的内容,若文件中未说明,请在键值中填'未知'。" user_query_forbidden = "该招标文件规定的投标人不得存在的其他情形有哪些,请以列表给我提供信息,形如[xx,xx,...],请你不要回答有关\"信誉要求\"的内容,若原文未提及,返回[]。" qianwen_forbidden_str = qianwen_long(file_id, user_query_forbidden) - print(qianwen_forbidden_str) - actual_list=process_string_list(qianwen_forbidden_str) - print(actual_list) + actual_list=process_string_list(qianwen_forbidden_str) #提取出字符串列表 ["xxx","xx"] includes = ["不得存在", "禁止投标"] forbidden_results = extract_and_format_from_paths([truncate_json_path, clause_path], includes) processed_results = extract_unique_items_from_texts(forbidden_results) - print(processed_results) + # print(processed_results) merged_forbidden_list = list(dict.fromkeys(actual_list + processed_results)) forbidden_dict={'不得存在的其他情形':merged_forbidden_list} @@ -142,9 +147,9 @@ def find_forbidden(truncate_json_path,clause_path,truncate4): #投标人须 if __name__ == '__main__': - truncate_json_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\truncate_output.json" - clause_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\clause.json" - truncate4 = "C:\\Users\\Administrator\\Desktop\\招标文件\\zbtest12_qualification.pdf" - output_dir = "C:\\Users\\Administrator\\Desktop\\招标文件" - doc_path = 'C:\\Users\\Administrator\\Desktop\\招标文件\\招标03.docx' + truncate_json_path = "C:\\Users\\Administrator\\Desktop\\fsdownload\\temp7\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\truncate_output.json" + clause_path = "C:\\Users\\Administrator\\Desktop\\fsdownload\\temp7\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\clause.json" + truncate4 = "C:\\Users\\Administrator\\Desktop\\fsdownload\\temp7\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\ztbfile_qualification.pdf" + output_dir = "C:\\Users\\Administrator\\Desktop\\fsdownload\\temp7\\3abb6e16-19db-42ad-9504-53bf1072dfe7" + doc_path = 'C:\\Users\\Administrator\\Desktop\\fsdownload\\temp7\\3abb6e16-19db-42ad-9504-53bf1072dfe7\\ztbfile.docx' find_forbidden(truncate_json_path,clause_path,truncate4) \ No newline at end of file diff --git a/flask_app/main/资格审查模块.py b/flask_app/main/资格审查模块.py index 784ddc5..34ca78f 100644 --- a/flask_app/main/资格审查模块.py +++ b/flask_app/main/资格审查模块.py @@ -8,7 +8,7 @@ from flask_app.main.资格评审 import process_qualification from flask_app.main.通义千问long import upload_file, qianwen_long -def combine_review_standards(truncate1,truncate4,knowledge_name,truncate0_jsonpath,clause_path): #评标办法前附表 +def combine_review_standards(truncate1,truncate3,knowledge_name,truncate0_jsonpath,clause_path): #评标办法前附表 # 形式评审、响应评审:千问 print("starting形式响应评审...") file_id=upload_file(truncate1) #评标办法前附表 @@ -16,7 +16,7 @@ def combine_review_standards(truncate1,truncate4,knowledge_name,truncate0_jsonpa results = qianwen_long(file_id, user_query_1) original_dict_data = extract_content_from_json(results) qualification_review = original_dict_data.pop('资格评审标准', '默认值或None') - final_qualify_json=process_qualification(qualification_review,truncate4,knowledge_name) + 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") form_response_dict.update(final_qualify_json) diff --git a/flask_app/main/转化格式/pydocx_p2d.py b/flask_app/main/转化格式/pydocx_p2d.py new file mode 100644 index 0000000..e69de29 diff --git a/flask_app/货物标/test.py b/flask_app/货物标/test.py new file mode 100644 index 0000000..5581704 --- /dev/null +++ b/flask_app/货物标/test.py @@ -0,0 +1,31 @@ +import os + +from docx import Document +from docxcompose.composer import Composer + + +def combine_docx(master, sub,index): + if not os.path.exists(sub): # 待合并文件必须存在 + return False + + if not master.endswith('.docx') or not sub.endswith('.docx'): # 主文件必须是docx格式(可以不存在) + return False + + if os.path.exists(master): + doc_master = Document(master) + doc_master.add_page_break() + cp = Composer(doc_master) + cp.append(Document(sub)) + else: + # master不存在,则sub直接给master + doc_master = Document(sub) + + doc_master.save(master) + return True + + +if __name__ == '__main__': + master = 'C:\\Users\\Administrator\\Desktop\\招标文件\\output1\\test.docx' + sub = 'C:\\Users\\Administrator\\Desktop\\招标文件\\output1\\zbfile.docx' + index = 2 # 假设你要在第二个元素位置插入 + combine_docx(master, sub,index) \ No newline at end of file