Parcourir la source

优化包号、角色金额;修复kv大纲下面是表格内容异常

lsm il y a 4 jours
Parent
commit
4e94649a0e

+ 6 - 2
BiddingKG/dl/common/Utils.py

@@ -1030,9 +1030,12 @@ def find_package(content):
         elif re.search('[1-9]\d{2,}$|\d{4,}|^[1-9]\d{2,}|合同包[A-Za-z]{2,}', iter.group(0)):
             # print('过滤掉错误包号5:', iter.group(0))
             continue
-        elif re.search('单位:包|1包\d|[张箱]', content[max(0, iter.start()-3): iter.end()+2]): # 处理 463166661 包号错误 钢丝,单位:包X10根。
+        elif re.search('单位:包|1包\d|[张箱]|数量:', content[max(0, iter.start()-3): iter.end()+2]): # 处理 463166661 包号错误 钢丝,单位:包X10根。  633043665 购买数量:5包,控制金额(元):75.00
             # print('过滤掉错误包号,单位:包|1包', iter.group(0))
             continue
+        elif re.search('(\d+|一)包', iter.group(0)) and re.search('(品牌|规格|参数)\w{,2}:', content[:iter.start()]) and re.search('标[段包]|包号', content[:iter.start()]) == None: # 633426404 参数要求:核心参数要求:商品类目:抽纸;维达抽纸:4D压花3层加厚.20包一件;
+            # print('过滤掉错误包:', iter.group())
+            continue
         elif iter.group(0) == '劳务分包': # 20241203 修复562534840劳务分包作包号
             continue
         packages.append(iter)
@@ -1170,6 +1173,7 @@ def get_money_entity(sentence_text, found_yeji=0, in_attachment=False):
     list_money_pattern["front_m"]))
 
     # sentence_text = re.sub('\d+[年月日]', '', sentence_text) # 修复560180018 中标价(元):3年投标报价(元)含税6299700.00 3年作为金额
+    sentence_text = re.sub(r"(金额|价格|限价)[:为]?(\d+),(\d+)", r"\1\2\3", sentence_text) # 修复 634020637 附件分行金额被逗号分隔情况 其中不含税金额128,7255.42元
 
     # if re.search('业绩(公示|汇总|及|报告|\w{,2}(内容|情况|信息)|[^\w])', sentence_text):
     #     found_yeji += 1
@@ -1273,7 +1277,7 @@ def get_money_entity(sentence_text, found_yeji=0, in_attachment=False):
                     if re.search('^[\d,,.]+$', entity_text) and float(re.sub('[,,]', '', entity_text))<500 and re.search('万元', sentence_text):
                         unit = '万元'
                         # print('金额较小且句子中有万元的,补充单位为万元')
-                    elif re.search('^[\d,,.]+$', entity_text) and float(re.sub('[,,]', '', entity_text))<100 and re.search('单位:%', sentence_text):
+                    elif re.search('^[\d,,.]+$', entity_text) and float(re.sub('[,,]', '', entity_text))<100 and re.search('单位:%|费率|下浮率|[%%‰折]|优惠率', sentence_text):
                         continue
                     elif re.search('^\d{1,3}\.\d{4,6}$', entity_text) and re.search('0000$', entity_text) == None:
                         unit = '万元'

+ 6 - 1
BiddingKG/dl/interface/Preprocessing.py

@@ -3437,9 +3437,14 @@ def get_preprocessed_article(articles,cost_time = dict(),useselffool=True):
             if re.search('寻源方案:[^,。]{4,},供应商名称:[^,。]{4,},公示截止时间:', article_processed):
                 article_processed = '中标公告,中标结果公示,' + article_processed
         if web_source_no.startswith('31151-') and len(re.split('[,。]', article_processed))<5: # 修复 629493676
-            match = re.search(',(?P<company>[\u4e00-\u9fa5()]{5,25}):(?P<money>[\d,\.]{2,15}万?元)', article_processed)
+            match = re.search(',(?P<company>[\u4e00-\u9fa5()]{5,25})(-\w{2,15})?:(?P<money>[\d,\.]{2,15}万?元)', article_processed)
             if match:
                 article_processed = article_processed.replace(match.group(0), ',供应商:%s,投标金额:%s'%(match.group('company'), match.group('money')))
+        match = re.search('20\d+元六', article_processed[:1000]) # 修复 637699732 中国船舶重工集团公司第七0五研究所昆明分部+2025052201元六的询价书的询价结果, 金额错误
+        if match:
+            article_processed = article_processed.replace(match.group(0), match.group(0)[:-2])
+        if web_source_no.startswith('27763-') and re.search(',招标方(名称)?,', article_processed):
+            article_processed = re.sub(',招标方(名称)?,', ',招标方名称:', article_processed)
 
         '''去除业绩内容'''
         article_processed = del_achievement(article_processed)

+ 7 - 3
BiddingKG/dl/interface/extract.py

@@ -373,7 +373,7 @@ def predict(doc_id,text,title="",page_time="",web_source_no='',web_source_name="
     # cost_time["product"] = round(time.time()-start_time,2)
 
     start_time = time.time() # 产品相关要素正则提取 单价、数量、品牌规格 ; 项目、需求、预算、时间
-    product_attrs, total_product_money = predictor.getPredictor("product_attrs").predict(doc_id, text, page_time)
+    product_attrs, total_product_money, total_budget = predictor.getPredictor("product_attrs").predict(doc_id, text, page_time)
     log("get product attributes done of doc_id%s"%(doc_id))
     cost_time["product_attrs"] = round(time.time()-start_time,2)
 
@@ -452,7 +452,7 @@ def predict(doc_id,text,title="",page_time="",web_source_no='',web_source_name="
     getAttributes.get_win_joint(prem, list_entitys, list_sentences, list_articles)
 
     '''修正采购公告表格形式多种采购产品中标价格;中标金额小于所有产品总金额则改为总金额'''
-    getAttributes.correct_rolemoney(prem, total_product_money, list_articles)
+    getAttributes.correct_rolemoney(doc_id, prem, total_product_money, total_budget, list_articles)
 
     '''修正channel预测类别为招标公告却有中标人及预测为中标信息却无中标关键词的类别''' # 依赖 prem
     start_time = time.time()
@@ -536,7 +536,7 @@ def predict(doc_id,text,title="",page_time="",web_source_no='',web_source_name="
 
     # data_res = Preprocessing.union_result(Preprocessing.union_result(codeName, prem),list_punish_dic)[0]
     # data_res = Preprocessing.union_result(Preprocessing.union_result(Preprocessing.union_result(codeName, prem),list_punish_dic), list_channel_dic)[0]
-    version_date = {'version_date': '2025-06-16'}
+    version_date = {'version_date': '2025-07-01'}
     data_res = dict(codeName[0], **prem[0], **channel_dic, **product_attrs[0], **product_attrs[1], **payment_way_dic, **fail_reason, **industry, **district, **candidate_dic, **version_date, **all_moneys, **pb_json)
 
     if original_docchannel == 302:
@@ -630,6 +630,10 @@ def predict(doc_id,text,title="",page_time="",web_source_no='',web_source_name="
     data_res['bid_score'] = bid_score # 评标得分
     data_res['time_planned'] = time_dic.get('time_planned', '') # 预计招标时间
     data_res['code_investment'] = code_investment # 投资项目编号
+    if data_res['product_attrs'].get('data', []) == []: # 20250619 为空的直接返回空字典
+        data_res['product_attrs'] = {}
+    if data_res['demand_info'].get('data', []) == []:
+        data_res['demand_info'] = {}
     for k, v in kv_single_dic.items(): # 没获取到的用kv_tree补充
         if data_res.get(k, '') == '':
             data_res[k] = v

+ 13 - 8
BiddingKG/dl/interface/getAttributes.py

@@ -4499,11 +4499,13 @@ def getPREMs(list_sentences,list_entitys,list_articles,list_outlines,page_time,w
         #                       "attachmentTypes":list_article.attachmentTypes, "bidway": list_article.bidway}))
     return result
 
-def correct_rolemoney(prem, total_product_money, list_articles): # 2022/9/26修改为 中标金额小于表格单价数量合计总金额十分之一时替换
+def correct_rolemoney(docid, prem, total_product_money, total_budget, list_articles): # 2022/9/26修改为 中标金额小于表格单价数量合计总金额十分之一时替换
     '''
     最后根据表格提取的单价数量合计对比更新中标金额,或中标金额为0全文只有一个总价或合计时,作为中标金额
+    :param docid: 公告编号
     :param prem: 列表
     :param total_product_money: 表格统计金额
+    :param total_budget: 表格合计预算金额
     :param list_articles: 文章对象
     :return:
     '''
@@ -4513,8 +4515,8 @@ def correct_rolemoney(prem, total_product_money, list_articles): # 2022/9/26修
             content += attachment
     else:
         content = list_articles[0].content
-    if len(re.findall('win_tenderer|second_tenderer|third_tenderer', str(prem[0]['prem'])))==1 and re.search('(中标|成交|合同|投标))?(总?金额|[报总]?价):', content) == None: # 只有一个中标角色且没有明确中标金额表达的
-        if total_product_money>0 and total_product_money<5000000000:
+    if len(re.findall('win_tenderer|second_tenderer|third_tenderer', str(prem[0]['prem'])))==1:# and re.search('(中标|成交|合同|投标))?(总?金额|[报总]?价)((万?元))?:', content) == None: # 只有一个中标角色且没有明确中标金额表达的
+        if total_product_money>0 and total_product_money<2000000:
             for value in prem[0]['prem'].values():
                 ree_money = float(value['tendereeMoney'])
                 for l in value['roleList']:
@@ -4523,9 +4525,9 @@ def correct_rolemoney(prem, total_product_money, list_articles): # 2022/9/26修
                         #     l[2] = total_product_money
                         #     log('修改中标金额为所有产品总金额')
                         # if l["role_name"] == 'win_tenderer' and float(l["role_money"]['money']) == 0 and float(l["role_money"]['money'])<total_product_money/10:
-                        if l["role_name"] == 'win_tenderer' and (float(l["role_money"]['money']) == 0 or (float(l["role_money"]['money'])<ree_money/2 and float(l["role_money"]['money'])<total_product_money<ree_money)): # 改为小于一半招标金额或为0时替换为合计金额
+                        if l["role_name"] == 'win_tenderer' and (float(l["role_money"]['money']) == 0 or(float(l["role_money"]['money'])<total_product_money<500000) or (float(l["role_money"]['money'])<ree_money/2 and float(l["role_money"]['money'])<total_product_money<ree_money)): # 改为小于一半招标金额或为0时替换为合计金额
                             l["role_money"]['money'] = total_product_money
-                            # print('修改中标金额为所有产品总金额')
+                            log('修改中标金额为产品总金额: %s, docid:%s' % (total_product_money, docid))
                     except Exception as e:
                         print('表格产品价格修正中标价格报错:%s'%e)
         elif (len(re.findall('合计', content)) == 1 or len(re.findall('总价', content)) == 1):
@@ -4541,9 +4543,12 @@ def correct_rolemoney(prem, total_product_money, list_articles): # 2022/9/26修
                                 if l["role_name"] == 'win_tenderer' and (float(l["role_money"]['money'])==0 or (float(l["role_money"]['money']) < money / 10 and re.search('(中标|成交|合同)(总?金额|[单报总]?价)', content) == None)):
                                     l["role_money"]['money'] = str(money)
                                     l["role_money"]['money_unit'] = money_unit
-                                    # print('修改中标金额为总价或合计金额')
+                                    log('修改中标金额为总价或合计金额: %s, docid:%s'%(money, docid))
                             except Exception as e:
                                 print('修正中标价格报错:%s' % e)
+    if 0 < total_budget < 2000000 and len(prem[0]['prem']) == 1 and 'Project' in prem[0]['prem'] and float(prem[0]['prem']['Project']["tendereeMoney"]) < total_budget:
+        prem[0]['prem']['Project']["tendereeMoney"] = total_budget
+        log('修改招标金额为表格合计预算: %s, docid:%s' % (total_budget, docid))
 
 def limit_maximum_amount(dic, list_entity):
     '''
@@ -5075,10 +5080,10 @@ def rule_add_role(docid, prem, channel, content, web_source_no, nlp_enterprise):
                 "uuid": str(uuid.uuid4())
             }
     if channel['docchannel']['docchannel'] == '招标公告' and re.search('"role_name": "tenderee"',json.dumps(prem)) == None:
-        match = re.search('(招标|采购|招商)(人|商|单位|部门)(信息[,:]?)?(名称)?((甲方))?:(?P<name>[\w()—-]{4,35})([,。]|$)', content)
+        match = re.search('(招标|采购|招商)(人|方|商|单位|部门)(信息[,:]?)?(名称)?((甲方))?:(?P<name>[\w()—-]{4,35})([,。]|$)', content)
         if match:
             ent_name = match.group('name')
-            if re.search('测试|演示|某|\d号|\*|XX', ent_name)==None and re.search('^\w{1,5}[省市县区][\w()]{2,25}[厂店铺市场行部城室馆中心站处社会狱所园关局司署段厅院队小学]((个体工商户)?|(普通合伙)?)?$',
+            if re.search('测试|演示|某|\d号|\*|XX', ent_name)==None and re.search('^\w{1,5}[省市县区][\w()]{2,25}[厂店铺市场行部城室馆中心站处社会狱所园关局司署段厅院队小学]((个体工商户)?|(普通合伙)?)?$',
                          ent_name):  #  or is_enterprise_exist(ent_name)
                 log('规则补充招标人角色:%s,docid:%s'%(ent_name, docid))
                 add_role(ent_name, "tenderee", prem)

+ 1 - 1
BiddingKG/dl/interface/html_2_kvtree.py

@@ -1214,7 +1214,7 @@ class Html2KVTree():
             _text = standard_product(obj.text)
             if obj.name=="table":
                 _type = "table"
-                _text = standard_product(str(obj))
+                _text = standard_product(str(obj.text))
             _append = False
             sentence_title = None
             sentence_title_text = None

+ 27 - 13
BiddingKG/dl/interface/predictor.py

@@ -881,7 +881,7 @@ class PREMPredict():
             elif label in [2,3,4] and re.search('序号:\d+,\w{,2}候选', front):
                 label = 5
             elif label == 0:
-                if re.search('拟邀请$|受邀谈判方|流入方名称:$|拟选用单位:$', front): # 修复 626700009 二、拟选用单位:海南和泰消防技术服务有限公司。
+                if re.search('拟邀请$|受邀谈判方|流入方名称:$|拟选用单位:$|选择(建设|\w{,2})?服务单位:$', front): # 修复 626700009 二、拟选用单位:海南和泰消防技术服务有限公司。 632486555 选择建设服务单位:四川富吉兴工程管理有限公司,
                     label = 2
                     values[label] = 0.501
                 elif re.search('(发布(人|方|单位|机构|组织|用户|业主|主体|部门|公司|企业)|组织(单位|人|方|机构)?|(采购|招标|发布)机构)(名称)?[是为:]+', front) and is_agency(entity.entity_text):
@@ -895,7 +895,7 @@ class PREMPredict():
                 elif re.search(',单位名称:$', front) and re.search('^,(中标|中选)价格', behind):
                     label = 2
                     values[label] = 0.501
-                elif re.search('选择$|挂牌业务:$|评价单位:$|代建单位:$|报价(人|单位):$|实施主体:$', front): #  修复 92729222 所以我院只能选择 西门子公司  官方维修渠道供货及维修 预测为招标人  75044917 网上挂牌业务:汝阳县公共资源交易中心,联系人:杨先生  评价机构的名称和联系方式 73326818 评价单位:湖南知成环保服务有限公司 119136658 代建单位: 杭州千岛湖房地产开发有限公司 ,招标方式:公开招标  90007873 沟临时改线工程,报价单位: 莆田市涵江区茂发建筑有限公司 。 626694010 实施主体:融安县奇点农业发展有限公司 ,建设内容:1.购买果蔬多功能
+                elif re.search('选择$|挂牌业务:$|评价单位:$|代建单位:$|报价(人|单位):$|实施主体:$', front): #  修复 92729222 所以我院只能选择 西门子公司  官方维修渠道供货及维修 预测为招标人  75044917 网上挂牌业务:汝阳县公共资源交易中心,联系人:杨先生  评价机构的名称和联系方式 73326818 评价单位:湖南知成环保服务有限公司 119136658 代建单位: 杭州千岛湖房地产开发有限公司 ,招标方式:公开招标  90007873 沟临时改线工程,报价单位: 莆田市涵江区茂发建筑有限公司 。 626694010 实施主体:融安县奇点农业发展有限公司 ,建设内容:1.购买果蔬多功能
                     label = 5
                 elif re.search('向\w{,4}$', front) and re.search('^反映', behind): # 修复 113239863 如有异议请于公示结束日期前向采购人 纪检监察部门 反映。
                     label = 5
@@ -935,7 +935,7 @@ class PREMPredict():
                 elif re.search('来源(单位)?:$', front):# and re.search('^,', behind): # 修复 472062585 项目采购-关于定制手机询比价采购中标公告,来源:深圳市网联安瑞网络科技有限公司 预测为中标
                     label = 0
                     values[label] = 0.5
-                elif re.search('合同供方:?$|合同签约单位', front):
+                elif re.search('合同供方:?$|合同签约(单位|方):$|中标单位合同签订主体:$', front): # 632476647 八、合同签约方: 山东港信资本投资有限公司 /安阳钢铁股份有限公司 642339031 中标单位合同签订主体:新乡中新化工有限责任公司。
                     label = 0
                     values[label] = 0.5
                 elif re.search('现由$', front) and re.search('^作为\d个单位的牵头(单位|公司)?', behind): # 修复 469369884 站源批量预测错误 现由第七合同段保利长大工程有限公司作为6个单位的牵头单位,
@@ -950,11 +950,13 @@ class PREMPredict():
                 elif re.search('^为\w{,10}第二(成交|中标)单位', behind): # 中标预测错误,例:601143888 河南省创慧新材料科技有限公司为铸咀采购项目第二成交单位
                     label = 3
                     values[3] = 0.5
-                elif re.search('中标单位,$|被确定为$', front): # 632523961 现通知:贵司被确定为广州地铁传媒有限公司贵阳地铁广告媒体服务项目(2025年)的执行单位。
+                elif re.search('中标单位,$|被确定为$|成交电商:$|如果?我方成功中选$', front): # 632523961 现通知:贵司被确定为广州地铁传媒有限公司贵阳地铁广告媒体服务项目(2025年)的执行单位。 # 609280615 万银政采平台 罗山县政采平台等成交电商都不是中标人  632380222 如我方成功中选 中国人民保险 采购项目,
                     label = 5
                 elif re.search('^为预备中标单位', behind):
                     label = 3
                     values[3] = 0.5
+                elif re.search(',中标人,$', front) and re.search('^\w{5,}', behind): # 修复 629482521  ,中标人,中国电建山东电建三公司山东发展杨庄集风电项目风力发电站GIS设备采,购项目:青岛特锐德电气股份有限公司,
+                    label = 5
             elif re.search('是否中标:是,供应商', front) and label == 5:
                 label = 2
                 values[label] = 0.9
@@ -1077,6 +1079,9 @@ class PREMPredict():
                 elif re.search('招标金额|限价|预算|控制价|拦标价', front) == None and re.search('预计约?为?$',
                                                                                   front):  # 20241206纠正 565894149(预计约2500元)预测为预算
                     label = 2
+
+                elif re.search('挂网价((万?元))?:', front): # 修复 634316533 生产企业:洛阳顺势药业有限公司,挂网价(元):59.76,
+                    label = 2
             elif re.search('报价:预估不?含税总价[为:]$', front) and (label != 1 or values[label]<0.5):
                 label = 1
                 values[label] = 0.8
@@ -1534,7 +1539,7 @@ class RoleRulePredictor():
         self.pattern_thirdTenderer_left = "(?P<thirdTenderer_left>(第[三3]名?(名|((中标|中选|中价|成交|候选)(候选)?(人|单位|机构|供应商|公司|银行))))(名称)?[::是为]+$|((评审结果|名次|排名|排序)[::]第?[三3]名?,?(投标(供应)?商|供应商)(名称)?[::]+$))"
         self.pattern_thirdTenderer_right = "(?P<thirdTenderer_right>^[是为\(]第[三3](名|(中标|中选|中价|成交)(候选)?(人|单位|机构|供应商|公司|银行)))"
 
-        self.candidate_left = "(?P<candidate_left>(((中[标选商]|成交|入围|入选)?候选|投标)(人|单位|机构|中介(服务)?机构|供应商|客户|方|公司|厂商|商家?|社会资本方?|银行)|服务单位|候选企业)(:?单位名称|:?名称|全称|(?盖\w{,5}章)?|如下|:?牵头人|[及与和](成交|中标)金额)?[::是为【]+$)"
+        self.candidate_left = "(?P<candidate_left>(((中[标选商]|成交|入围|入选)?候选|投标)(人|单位|机构|中介(服务)?机构|供应商|客户|方|公司|厂商|商家?|社会资本方?|银行)|服务单位|候选企业)(1.)?(:?单位名称|:?名称|全称|(?盖\w{,5}章)?|如下|:?牵头人|[及与和](成交|中标)金额)?[::是为【]+(1[.、])?$)"
 
         self.pattern_left = [
             self.pattern_tenderee_left_60,
@@ -1563,7 +1568,7 @@ class RoleRulePredictor():
 
         self.SET_NOT_TENDERER = set(["人民政府","人民法院","中华人民共和国","人民检察院","评标委员会","中国政府","中国海关","中华人民共和国政府"])
         
-        self.pattern_money_tenderee = re.compile("投?标?最高限价|采购计划金额|项目预算|招标金额|采购金额|项目金额|投资估算|采购(单位|人)委托价|招标限价|拦标价|预算金额|标底|总计|限额|资金来源,?[为:]+\w{2,4}资金|采购成本价|总费用约?为|(招标|采购)总?(规模|额度|资金)|资金来源|合同价暂定|包合计:$")  # |建安费用 不作为招标金额
+        self.pattern_money_tenderee = re.compile("投?标?最高限价|采购计划金额|项目预算|招标金额|采购金额|项目金额|投资估算|采购(单位|人)委托价|招标限价|拦标价|预算金额|标底|总计|限额|资金来源,?[为:]+\w{2,4}资金|采购成本价|总费用约?为|(招标|采购)总?(规模|额度|资金)|资金来源|合同价暂定|包合计:$")  # |建安费用 不作为招标金额 634252534 包合计:180,000.00 元
         self.pattern_money_tenderer = re.compile("((合同|成交|中标|应付款|交易|投标|验收|订单)[)\)]?(综合)?(总?金额|结果|[单报总]?价))|标的基本情况|承包价|报酬(含税):|经评审的价格|报价不?含税")  # 单写 总价 不能作为中标金额,很多表格有单价、总价
         self.pattern_money_tenderer_whole = re.compile("(以金额.*中标)|中标供应商.*单价|以.*元(报价)?(中标|中选|成交)")
         self.pattern_money_other = re.compile("代理费|服务费")
@@ -1798,7 +1803,7 @@ class RoleRulePredictor():
                                 _label, _prob, _flag, kw = self.rule_predict(before, center, after, entity_text)
 
                                 if _label == 5 and re.search(':(1[.、])?$', before) and re.search('^[、;,&/。]', after) and re.search('(监督|管理)(机构|部门|单位):', before)==None and re.search(
-                                        '(中标|成交|中选))?(人|单位|供应商|银行|合作伙伴)?(公示)?(信息|情况|结果|如下)(公[示告]如下)?:|(遴选|寻源|采购|招标|竞价|议价|比选|委托|询比?价|比价|评选|谈判|邀标|邀请|洽谈|约谈|选取|抽取)结果(如下)(公[示告]如下)?:', list_sentence[s_index].sentence_text[:p_entity.wordOffset_begin]): # 补充召回 例:514053647 标段1:中国建设银行西安南大街支行,标段2:中国农业银行股份有限公司西安分行,
+                                        '(中标|成交|中选))?(人|单位|供应商|银行|合作伙伴)?(公示)?(信息|情况|结果|如下)(公[示告]如下)?:|(遴选|寻源|采购|招标|竞价|议价|比选|委托|询比?价|比价|评选|谈判|邀标|邀请|洽谈|约谈|选取|抽取)结果(如下)(公[示告]如下)?:|,中标人,', list_sentence[s_index].sentence_text[:p_entity.wordOffset_begin]): # 补充召回 例:514053647 标段1:中国建设银行西安南大街支行,标段2:中国农业银行股份有限公司西安分行,
                                     _flag = True
                                     _label = 2
                                     _prob = 0.5
@@ -2568,7 +2573,7 @@ class RoleGrade():
 
 class MoneyGrade():
     def __init__(self):
-        self.tenderee_money_left_9 = "(?P<tenderee_left_9>最高(投标)?限价)|控制价|拦标价"
+        self.tenderee_money_left_9 = "(?P<tenderee_left_9>最高(投标)?限价)|控制(|金额)|拦标价"
         self.tenderee_money_left_8 = "(?P<tenderee_left_8>预算|限价|起始|起拍|底价|标底)"
         self.tenderer_money_left_9 = "(?P<tenderer_left_9>(中标|成交|合同|总报价))"
         self.tenderer_money_left_8 = "(?P<tenderer_left_8>(投标|总价))"
@@ -2602,6 +2607,8 @@ class MoneyGrade():
                             _prob = max(0.5, _prob - 0.2)
                         entity.values[_label] = _prob + entity.values[_label] / 20
                         not_found = 0
+                        if _label == 0 and float(entity.entity_text)<10000 and entity.values[_label] > 0.6: # 20250624 小金额预算概率降低 634252534 包合计才是真正的预算
+                            entity.values[_label] = 0.6
                         # print('规则修改金额概率后:', entity.entity_text, entity.label, entity.values)
                         break
                 if not_found and entity.values[entity.label] > min_prob:
@@ -2981,6 +2988,7 @@ class ProductAttributesPredictor():
             for td in tds:
                 # td_text = re.sub('\s+|…', ' ', td.get_text()).strip()
                 td_text = re.sub('…', '', td.get_text()).strip()
+                td_text = re.sub('\n+|\s+', ' ', td_text)  # 20250626 去掉\n等避免存OTS后去掉转义导致json解析错误
                 td_text = td_text.replace("\x06", "").replace("\x05", "").replace("\x07", "").replace('\\', '/').replace('"', '') # 修复272144312 # 产品单价数量提取结果有特殊符号\  气动执行装置备件\密封组件\NBR+PT
                 td_text = td_text.replace("(", "(").replace(")", ")").replace(':', ':')
                 tr_line.append(td_text)
@@ -3355,6 +3363,7 @@ class ProductAttributesPredictor():
         total_product_money = 0
         unit_price_list = [] # 单价列表,用于判断是否重复单价,避免多个表格重复提取造成合计产品价格错误。
         total_price_list = []  # 总价列表,拥有判断是否为几行产品合计总价
+        budget_list = [] # 预算列表,用于统计所有产品预算
         # print('表格数:', len(tables))
 
         for i in range(len(tables)):  # (len(tables)-1, -1, -1) 由从最后到前改为 前到后
@@ -3516,12 +3525,10 @@ class ProductAttributesPredictor():
                         headers_demand.append('_'.join(header_list2))
                         header_col.append('_'.join(tds))
                     i += 1
-                    # print('表头数量占行列数0.4倍不做内容匹配', set([re.sub('[::]','',td) for td in tds]) & self.header_set, tds)
                     continue
                 elif found_header:
                     if len(tds) > header_colnum or len(tds)-1<max([it for it in header_dic.values() if it!=""]):  # 表头、属性列数不一致跳过
                         i += 1
-                        # print('表头、属性列数不一致跳过', len(tds), header_colnum, tds)
                         continue
                     id0 = header_dic.get('品目', "")
                     id1 = header_dic.get('名称', "")
@@ -3676,6 +3683,8 @@ class ProductAttributesPredictor():
                                             unitPrice = str(unitPrice) if unitPrice != 0 and unitPrice<100000000 else ""
                                         if budget != "":
                                             budget, _money_unit = money_process(budget, header_list2[2])
+                                            if budget > 0:
+                                                budget_list.append(budget)
                                             budget = str(budget) if budget != 0 and budget<50000000000 else ''
                                         if total_price != "":
                                             total_price, _money_unit = money_process(total_price, header_list[6])
@@ -3720,6 +3729,8 @@ class ProductAttributesPredictor():
                                     unitPrice = str(unitPrice) if unitPrice != 0 and unitPrice<100000000 else ""
                                 if budget != "":
                                     budget, _money_unit = money_process(budget, header_list2[2])
+                                    if budget > 0:
+                                        budget_list.append(budget)
                                     budget = str(budget) if budget != 0 and budget<50000000000 else ''
                                 if total_price != "":
                                     total_price, _money_unit = money_process(total_price, header_list[6])
@@ -3768,6 +3779,9 @@ class ProductAttributesPredictor():
                     i += 1
                 else:
                     i += 1
+            if i > 2 and len(set(tds)) in [2, 3] and re.search('订单总价', tds[0]) and re.search('\d+[\d,\.]*', tds[1]):  # 修复 608217698 采购多项东西成交价不同,订单总价才是中标金额
+                money_, unit_ = money_process(tds[1], tds[0])
+                total_product_money = money_ if money_ > total_product_money else total_product_money
         if len(total_price_list)>1 and len(set(total_price_list))/len(total_price_list)<=0.5: # 2023/7/27 总价一半以上重复的为多行一个总价,需去掉
             # print('总价一半以上重复的为多行一个总价,需去掉', total_price_list)
             for link in product_link:  # 预防最后一列总价为所有产品总价,列补全后所有产品总价一样情况
@@ -3787,15 +3801,17 @@ class ProductAttributesPredictor():
         if len(product_link)>0:
             product_link = [{k:v for k,v in d.items() if v!=''} for d in product_link]
             attr_dic = {'product_attrs':{'data':product_link, 'header':headers, 'header_col':header_col}}
+            total_budget = sum(budget_list) if len(budget_list) == len(product_link) else 0
         else:
             attr_dic = {'product_attrs': {'data': [], 'header': [], 'header_col': []}}
+            total_budget = 0
         if len(demand_link)>0:
             demand_link = [{k: v for k, v in d.items() if v != ''} for d in demand_link]
             demand_dic = {'demand_info':{'data':demand_link, 'header':headers_demand, 'header_col':header_col}}
         else:
             demand_dic = {'demand_info':{'data':[], 'header':[], 'header_col':[]}}
         # print('表格产品属性提取:', attr_dic)
-        return [attr_dic, demand_dic], total_product_money
+        return [attr_dic, demand_dic], total_product_money, total_budget
 
     def predict_without_table(self,product_attrs,list_sentences,list_entitys,codeName,prem, html='', page_time=""):
         if len(prem[0]['prem'])==1:
@@ -6306,8 +6322,6 @@ class DistrictPredictor():
         pro_ids, city_ids, dis_ids = self.merge_score(province_l, city_l, district_l, self.full_dic, self.short_dic, self.idx_dic)
         big_area_1, pred_pro_1, pred_city_1, pred_dis_1, prob, max_score = self.get_final_addr(pro_ids, city_ids, dis_ids, self.idx_dic)
         big_area, pred_pro, pred_city, pred_dis = big_area_1, pred_pro_1, pred_city_1, pred_dis_1
-        # print('关键词1:', province_l, city_l, district_l)
-        # print('分数:', pro_ids, city_ids, dis_ids, prob, max_score)
         if pred_city_1 == "" or prob < 0.7 or max_score<2:
             ree, addr = self.get_ree_addr(prem)
             if ree in title: