浏览代码

Merge branch 'master' of http://192.168.2.103:3000/luojiehua/BIDI_ML_INFO_EXTRACTION

 Conflicts:
	BiddingKG/dl/interface/predictor.py
znj 2 天之前
父节点
当前提交
78a3d4be6c

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

@@ -3392,6 +3392,7 @@ def get_preprocessed_article(articles,cost_time = dict(),useselffool=True):
         article_processed = re.sub('金,额', '金额', article_processed)
         article_processed = re.sub('存,款', '存款', article_processed)
         article_processed = re.sub('(适用于采用评定分离评标的项目)|(根据需要可以填写总价、单价、下浮率、费率等)|业绩(响应招标文件的业绩,多项应分别列出)', '', article_processed) # 修复关键词过远导致召回失败 例:579672495
+        article_processed = re.sub('采购商名称:欧冶工业品采购组织', '采购商名称:欧冶工业品股份有限公司', article_processed)
         if web_source_no.startswith('DX002756-'):
             article_processed = re.sub('状态:(进行中|已结束)单位', ',项目单位', article_processed)  # 376225646
         if web_source_no.startswith('DX006116-') and re.search('结果公告如下:.{5,50},单位名称:', article_processed):  # 2023/11/20 特殊处理 381591924 381592533 这种提取不到情况
@@ -3431,6 +3432,14 @@ def get_preprocessed_article(articles,cost_time = dict(),useselffool=True):
             match = re.search(',(\w{5,25}[,。])$', article_processed)
             if match:
                 article_processed = article_processed.replace(match.group(1), '中标候选人:%s'%match.group(1))
+        if web_source_no == 'DX005060-1' and re.search('序号:1,公司名称:', article_processed): # 修复
+            article_processed = re.sub('序号:1,公司名称:', '发布单位:', article_processed, 1)
+            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)
+            if match:
+                article_processed = article_processed.replace(match.group(0), ',供应商:%s,投标金额:%s'%(match.group('company'), match.group('money')))
 
         '''去除业绩内容'''
         article_processed = del_achievement(article_processed)
@@ -3668,7 +3677,8 @@ def get_preprocessed_entitys(list_sentences,useselffool=True,cost_time=dict()):
     '''
 
     list_entitys = []
-    not_extract_roles = ['黄埔军校', '国有资产管理处', '五金建材', '铝合金门窗', '华电XX发电有限公司', '华电XXX发电有限公司', '中标(成交)公司', '贵州茅台', '贵州茅台酒', '陕西省省级国'] # 需要过滤掉的企业单位
+    not_extract_roles = ['黄埔军校', '国有资产管理处', '五金建材', '铝合金门窗', '华电XX发电有限公司', '华电XXX发电有限公司',
+                         '中标(成交)公司', '贵州茅台', '贵州茅台酒', '陕西省省级国', '纪检监察部门', '融安金桔', '海达源组织', '海达源织'] # 需要过滤掉的企业单位
     for list_sentence in list_sentences:
         sentences = []
         list_entitys_temp = []
@@ -3858,6 +3868,21 @@ def get_preprocessed_entitys(list_sentences,useselffool=True,cost_time=dict()):
 
                 entity_text = cut_repeat_name(entity_text) # 20231201 重复名称去重 如:中山大学附属第一医院中山大学附属第一医院中山大学附属第一医院
 
+                short_full_dic = {
+                    '海尔': '海尔集团',
+                    '国电': '国电电力发展股份有限公司',
+                    '国网': '国家电网有限公司',
+                    '中核': '中国核工业集团有限公司',
+                    '中铁': '中国铁路工程集团有限公司',
+                    '中建': '中国建筑集团有限公司',
+                    '华为': '华为技术有限公司',
+                    '中兴': '中兴通讯股份有限公司',
+                    '联想': '联想集团',
+                    '美的': '美的集团',
+                    '格力': '珠海格力电器股份有限公司',
+                }
+                entity_text = short_full_dic.get(entity_text, entity_text) # 简称映射字典
+
                 list_sentence_entitys.append(Entity(doc_id,entity_id,entity_text,entity_type,sentence_index,begin_index,end_index,ner_entity[0],ner_entity[1],in_attachment=in_attachment))
             # 标记文章末尾的"发布人”、“发布时间”实体
             if sentence_index==len(list_sentence)-1 or sentence_index==doctextcon_sentence_len-1:

+ 4 - 1
BiddingKG/dl/interface/extract.py

@@ -513,6 +513,9 @@ def predict(doc_id,text,title="",page_time="",web_source_no='',web_source_name="
     '''最终验证prem'''
     getAttributes.confirm_prem(prem[0]['prem'], channel_dic, deposit_project, prem[0]['total_tendereeMoney'])
 
+    '''规则补充招标无招标人中标无中标人角色'''
+    getAttributes.rule_add_role(doc_id,prem[0]['prem'], channel_dic, list_articles[0].content, web_source_no, nlp_enterprise)
+
     '''通过产品补充标段包名20241203'''
     getAttributes.add_package_name(prem[0]['prem'], list_entitys[0], product_list, name=codeName[0]['name'])
 
@@ -530,7 +533,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-05-15'}
+    version_date = {'version_date': '2025-06-06'}
     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:

+ 74 - 2
BiddingKG/dl/interface/getAttributes.py

@@ -15,6 +15,8 @@ import numpy as np
 import uuid
 import time,calendar
 from datetime import datetime
+import json
+from BiddingKG.dl.entityLink.entityLink import is_enterprise_exist
 
 def getTheRole(entity,role_list):
     '''
@@ -4910,8 +4912,8 @@ def update_prem(old_prem, new_prem, in_attachment=False):
     :return:
     '''
     if len(new_prem) >= 1 :
-        '''如果表格提取的包大于2,原来的包比表格提取的包多则删除原来多余的包,以表格的为准'''
-        if len(new_prem) >= 2 and (len(new_prem)<len(old_prem) <= len(new_prem)*2 or set(old_prem)&set(new_prem)==set()): # 修复类似443925411 标的+标包才算标段号
+        '''如果表格提取的包大于2,原来的包比表格提取的包多则删除原来多余的包,以表格的为准 20250528补充条件表格提取需有中标人避免 626464296 替换错误'''
+        if len(new_prem) >= 2 and 'win_tenderer' in json.dumps(new_prem) and (len(new_prem)<len(old_prem) <= len(new_prem)*2 or set(old_prem)&set(new_prem)==set()): # 修复类似443925411 标的+标包才算标段号
             del_k = []
             for k in old_prem:
                 if k not in new_prem and k != 'Project':
@@ -5023,6 +5025,76 @@ def update_prem(old_prem, new_prem, in_attachment=False):
 
     # return old_prem
 
+def rule_add_role(docid, prem, channel, content, web_source_no, nlp_enterprise):
+    def add_role(ent_name, role_type, prem):
+        if 'Project' in prem:
+            prem['Project']['roleList'].append(
+                {
+                    "address": "",
+                    "linklist": [],
+                    "role_money": {
+                        "discount_ratio": "",
+                        "downward_floating_ratio": "",
+                        "floating_ratio": "",
+                        "money": 0,
+                        "money_unit": ""
+                    },
+                    "role_name": role_type,
+                    "role_prob": 0.6,
+                    "role_text": ent_name,
+                    "rule_add_role": True,
+                    "serviceTime": ""
+                }
+            )
+        else:
+            prem['Project'] = {
+                "code": "",
+                "name": "",
+                "roleList": [
+                    {
+                        "address": "",
+                        "linklist": [
+
+                        ],
+                        "role_money": {
+                            "discount_ratio": "",
+                            "downward_floating_ratio": "",
+                            "floating_ratio": "",
+                            "money": 0,
+                            "money_unit": ""
+                        },
+                        "role_name": role_type,
+                        "role_prob": 0.6,
+                        "role_text": ent_name,
+                        "rule_add_role": True,
+                        "serviceTime": ""
+                    }
+                ],
+                "tendereeMoney": 0,
+                "tendereeMoneyUnit": "",
+                "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)
+        if match:
+            ent_name = match.group('name')
+            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)
+        elif web_source_no == 'DX000752' and len(nlp_enterprise)==1 and re.search('更多信息点击报价地址', content): # 修复 628311260
+            ent_name = nlp_enterprise[0]
+            add_role(ent_name, "tenderee", prem)
+    elif channel['docchannel']['docchannel'] == '中标信息' and re.search('"role_name": "win_tenderer"',json.dumps(prem)) == None:
+        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}[厂店铺市场行部城室馆中心站处社会狱所园关局司署段厅院队小学]((个体工商户)?|(普通合伙)?)?$',
+                         ent_name):  #  or is_enterprise_exist(ent_name)
+                log('规则补充中标人角色:%s,docid:%s'%(ent_name, docid))
+                add_role(ent_name, "win_tenderer", prem)
+
+
 def confirm_prem(prem, channel_dic, is_deposit_project=False, total_tendereeMoney=0):
     '''
     规则检查纠正prem,如果Project包中标人在其他包中标人,去掉project包中标角色;如果有其他包中标人,去掉roleList为空的包;

+ 1 - 0
BiddingKG/dl/interface/modelFactory.py

@@ -108,6 +108,7 @@ class Model_role_classify_word():
         text = re.sub('电子签章', '', text) # 20240924 修复 529923459 电子签名:投标人名称(电子签章:西君兰信息科技有限公司,2024年9月7日 预测为中标
         text = re.sub('采购方式', 'xxxx', text) # 修复 499096797 招标人预测错误
         text = re.sub('中标人\d名称', '中标人名称', text) # 修复 499096797 中标人预测错误
+        text = re.sub('\|候选人', '、候选人', text) # 修复 626660259 排名:1|候选人:库尔勒海南广电工程有限责任公司
         return text.replace('(', '(').replace(')', ')').replace('單', '单').replace('稱','承').replace('標', '标').replace('採購', '采购').replace('機構', '机构')
 
     def encode_word(self, sentence_text, begin_index, end_index, size=20, **kwargs):

+ 41 - 24
BiddingKG/dl/interface/predictor.py

@@ -74,10 +74,13 @@ def get_role(text, nlp_enterprise):
     roles = []
     if ners:
         for ner in ners[0]:
+            entity_text= ner[3]
+            if text[ner[1]:] == '(个体工商户)': # 修复 肇州县一胜建材经销处(个体工商户)这种类型
+                entity_text = ner[3] + '(个体工商户)'
             if ner[2] in ['org', 'company']:
-                roles.append(ner[3])
-            elif ner[2] in ['location'] and re.search('^\w{3,10}(海关|殡仪馆|店|村委会|纪念馆|监狱|管教所|修养所|社区|农场|林场|羊场|猪场|石场)$', ner[3]):
-                roles.append(ner[3])
+                roles.append(entity_text)
+            elif ner[2] in ['location'] and re.search('^\w{3,10}(海关|殡仪馆|店|村委会|纪念馆|监狱|管教所|修养所|社区|农场|林场|羊场|猪场|石场|经营部|经销处)$', ner[3]):
+                roles.append(entity_text)
     if roles and len(''.join(roles)) > len(text)*0.8:
         entity = clean_company(roles[0])
         return entity
@@ -875,7 +878,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):
+                if re.search('拟邀请$|受邀谈判方|流入方名称:$|拟选用单位:$', front): # 修复 626700009 二、拟选用单位:海南和泰消防技术服务有限公司。
                     label = 2
                     values[label] = 0.501
                 elif re.search('(发布(人|方|单位|机构|组织|用户|业主|主体|部门|公司|企业)|组织(单位|人|方|机构)?|(采购|招标|发布)机构)(名称)?[是为:]+', front) and is_agency(entity.entity_text):
@@ -889,6 +892,14 @@ 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.购买果蔬多功能
+                    label = 5
+                elif re.search('向\w{,4}$', front) and re.search('^反映', behind): # 修复 113239863 如有异议请于公示结束日期前向采购人 纪检监察部门 反映。
+                    label = 5
+                elif re.search('^(以下简称', behind) and re.search('招标|采购|询价|建设单位|甲方', behind[:10])==None: # 修复 90145222 需对南京公用水务有限公司(以下简称“公用水务”)名下土地进行资产评估
+                    label = 5
+                elif re.search('交易单位|组织单位|发起组织', front[-10:]):
+                    values[0] = 0.5
             elif label == 2:
                 if re.search('中标单位和.{,25}签订合同', whole):
                     label = 0
@@ -926,7 +937,7 @@ class PREMPredict():
                     values[label] = 0.5
                 elif re.search('现由$', front) and re.search('^作为\d个单位的牵头(单位|公司)?', behind): # 修复 469369884 站源批量预测错误 现由第七合同段保利长大工程有限公司作为6个单位的牵头单位,
                     label = 5
-                elif re.search('(中标|成交)?|结果)?)(人|公告|公示),$|中标人信息:$', front): # 20250227修复中标错误 588005167 现确定贵公司为该项目的中标人,中国二冶集团有限公司,2025年01月26日,
+                elif re.search('\w(中标|成交)?|结果)?)(人|公告|公示),$|中标人信息:$', front): # 20250227修复中标错误 588005167 现确定贵公司为该项目的中标人,中国二冶集团有限公司,2025年01月26日,
                     label = 5
                 elif re.search('确定$', front) and re.search('^\w{,5}(项目|采购|招标)', behind):
                     label = 5
@@ -948,7 +959,7 @@ class PREMPredict():
                 if re.search('委托(单位|人|方)[是为:]+',front) and re.search('受委托(单位|人|方)[是为:]+', front)==None:
                     label = 0
                     values[label] = 0.501
-                elif re.search('([,。:]|^)(第一)?(服务|中选|中标)(中介服务|代理)?(公司|机构)(名称)?', front):
+                elif re.search('([,。:]|^)(第一)?(服务|中选|中标|成交)(中介|采购|招标)?(服务|代理)?(公司|机构)(名称)?', front[-12:]): # 修复 626552262,成交采购代理机构名称:四川乾新招投标代理有限公司
                     label = 2
                     values[label] = 0.501
                 elif re.search('在中介超市委托$', front) and re.search('^负责', behind):
@@ -963,7 +974,7 @@ class PREMPredict():
                     label = 5
                 elif re.search('委托$', front) and re.search('^(抽样|送检|看样)', behind):
                     label = 5
-                elif re.search('推荐入围的招标代理单位:$', front): # 20240709 修复302505502预测错为代理
+                elif re.search('推荐入围的招标代理单位:$|拟确定的招标代理(服务)?(机构|单位)[为:]$', front): # 20240709 修复302505502预测错为代理
                     label = 2
                     values[label] = 0.501
                 elif re.search('代理公司$', front) and re.search('^销售', behind): # 修复 103462671 且该软件平台由博导前程软件公司在浙江的官方代理公司杭州楚沩教育科技有限公司销售。
@@ -984,6 +995,9 @@ class PREMPredict():
                 elif re.search('第一名:$|[二三23][,、](中标|成交|中选)(单位|人|供应商):$|成交供应商为$', front): # 108505049 一,招租单位:井都神山经联社。二,成交单位:广东新潮建设有限公司。
                     label = 2
                     values[label] = 0.7
+                elif re.search('确定原第二(中标|成交|中选)?候选人', front) and re.search('^为\w{,15}中标人', behind):
+                    label = 2
+                    values[label] = 0.501
             elif re.search('(中标|成交)通知书[,:]$', front) and re.search('^:', behind) and label != 2:
                 label = 2
                 values[label] = 0.8
@@ -1484,14 +1498,14 @@ class RoleRulePredictor():
                                      "(人|方|单位|组织|用户|业主|主体|部门|公司|企业|工厂)|[转流]出方|文章来源|委托机构|产权所有人|承包权人|结算单位|收货地址)" \
                                      "[))]?(信息|联系方式|概况)?[,,::]?([((](1|2|1.1|1.2)[))])?((公司|单位)?名称)?([((](全称|盖章|异议受理部门)[))])?(是|为|:|:|\s*)+$|(采购商|招标人):(\w{2,10}-)?$|实施主体(基本情况,)?名称:$)"
         self.pattern_tenderee_center = "(?P<tenderee_center>(受.{5,20}的?委托|现将[\w()()]{5,20}[\d年月季度至()]+采购意向|尊敬的供应商(伙伴)?:\w{5,20}(以下简称“\w{2,5}”)))"
-        self.pattern_tenderee_right = "(?P<tenderee_right>^(机关)?([((](以下简称)?[,\"“]*((招标|采购)(人|单位|机构)|(服务)?购买方)[,\"”]*[))]|^委托|^将于[\d年月日,::]+进行|^现委托|^的\w{2,10}正在进行|[\d年月季度至]+采购意向|^)?的招标工作已圆满结束)|^([拟须需]|计划)(采购|招标|购置|购买)|^须购[买置]一批|作为(采购|招标)(人|单位)|^关于)"  #|(^[^.。,,::](采购|竞价|招标|施工|监理|中标|物资)(公告|公示|项目|结果|招标))|的.*正在进行询比价)
+        self.pattern_tenderee_right = "(?P<tenderee_right_50>^(机关)?([((](以下简称)?[,\"“]*((招标|采购)(人|单位|机构)|(服务)?购买方)[,\"”]*[))]|^委托|^将于[\d年月日,::]+进行|^现委托|^的\w{2,10}正在进行|[\d年月季度至]+采购意向|^)?的招标工作已圆满结束)|^([拟须需]|计划)(采购|招标|购置|购买)|^须购[买置]一批|作为(采购|招标)(人|单位))"  #|(^[^.。,,::](采购|竞价|招标|施工|监理|中标|物资)(公告|公示|项目|结果|招标))|的.*正在进行询比价) # 20250605去掉 |^关于 根据《广东省自然资源厅关于开展2024年度耕地资源分区分类评价更新工作的通知》
         self.pattern_tendereeORagency_right = "(?P<tendereeORagency_right>(^拟对|^现?就|^现对))"
         self.pattern_agency_left = "(?P<agency_left>((代理|拍卖)(?:人|机构|公司|企业|单位|组织)|专业采购机构|集中采购机构|招标组织机构|交易机构|集采机构|[招议))]+标机构|(采购|招标)代理)(名称|.{,4}名,?称|全称)?(是|为|:|:|[,,]?\s*)$|(受.{5,20}委托,?$))"
         self.pattern_agency_right = "(?P<agency_right>^([((](以下简称)?[,\"“]*(代理)(人|单位|机构)[,\"”]*[))])|^受.{5,20}委托|^受委?托,)"  # |^受托  会与 受托生产等冲突,代理表达一般会在后面有逗号
         # 2020//11/24 大网站规则 中标关键词添加 选定单位|指定的中介服务机构
         self.pattern_winTenderer_left_50 = "(?P<winTenderer_left_51>" \
                "(乙|竞得|受让|买受|签约|供货|供应?|合作|承做|承包|承建|承销|承保|承接|承制|承担|承修|承租((包))?|入围|入选|竞买)(候选|投标)?(人|单位|机构|供应商|方|公司|企业|厂商|商|社会资本方?|银行)(:?单位名称|:?名称|盖章)?[::是为]+$" \
-               "|(选定单位|指定的中介服务机构|实施主体|中标银行|中标通知书,致|征集结果|选择中介|选择结果|成交对象|勘察人|(,|审计|处置|勘察|设计)服务单位|受托[人方])[::是为]+$" \
+               "|(选定单位|指定的中介服务机构|实施主体|中标银行|中标通知书,致|征集结果|选择中介|选择结果|成交对象|勘察人|(,|审计|处置|勘察|设计)服务单位|受托[人方]|直接采购对象)[::是为]+$" \
                "|((评审结果|名次|排名|中标结果)[::]*第?[一1]名?)[::是为]+$|成交供应商信息[,:]?(序号1)?:?|供应商名称$|竞争性选择申请人名称:$" \
                "|单一来源(采购)?(供应商|供货商|服务商|方式向)$|((中标|成交)(结果|信息))[::是为]+$|(中标|成交)供应商、(中标|成交)(金额|价格),$|合作伙伴名称:$|供应商(乙方)-?$|合作单位:$" \
                "|现(公布|宣布|公示)中标单位如下:$|现将中标单位(公布|公示)如下:$|现宣布以下(企业|单位|公司)中标:$|经讨论,决定采用$|第\d+(包件?|标段?)(中标|中选|成交)候选人:$|入围供应商如下(排名不分先后)[,:]$)"  # 承办单位:不作为中标 83914772  |施工 单位不作为中标人 例:386692187
@@ -1500,12 +1514,12 @@ class RoleRulePredictor():
                                            "(:?单位名称|:?名称|盖章)?[,,]?([((]按综合排名排序[))]|:择优选取)?[::,,]$|选取(情况|说明):中选,中介机构名称:$|排名如下:1、$|第[一1]名,?投标(人|单位|银行|公司):$)"  # 解决表头识别不到加逗号情况,需前面为,。空 20240621补充 中选 云南省投资审批中介超市 补充排名如下 南阳师范学院
         self.pattern_winTenderer_left_55 = "(?P<winTenderer_left_55>(中标(投标)?|[拟预]中标|中选|中价|中签|成交|入选)(人|单位|机构|中介(服务)?机构|供应商|客户|方|公司|企业|厂商|商家?|社会资本方?|银行)" \
                                            "(:?单位名称|:?名称|盖章)?([((]按综合排名排序[))]|:择优选取)?[::是为]+$" \
-                                           "|结果公示如下:摇出球号:\d+号,中介机构:$)"  # 取消逗号 并拒绝执行改进计划的供应商,华新水泥将可能终止与其合作关系  # 中标候选人不能作为中标   # |直购企业:$不能作为中标人,看到有些公告会又多个公司,然后还会发布中选结果的公告,其中一个公司中标
+                                           "|结果公示如下:摇出球号:\d+号,中介机构:$|(中标|成交|中选)(单位|人|供应商)及\w{,2}金额:$)"  # 取消逗号 并拒绝执行改进计划的供应商,华新水泥将可能终止与其合作关系  # 中标候选人不能作为中标   # |直购企业:$不能作为中标人,看到有些公告会又多个公司,然后还会发布中选结果的公告,其中一个公司中标
 
         self.pattern_winTenderer_right = "(?P<winTenderer_right>(^[是为](首选)?((采购|中标|成交)(供应商|供货商|服务商)|(第[一1]|预)?(拟?(中标|中选|中价|成交)(候选|排序)?(人|单位|机构|供应商|公司|企业|厂商|银行)))|" \
                                          "^((报价|价格)最低,|以\w{5,10})?(确定|成|作)?为[\w“”()]{3,25}((成交|中选|中标|服务)(人|单位|供应商|企业|公司)|供货单位|供应商|第一中标候选人)[,。]" \
-                                         "|^:贵公司参与|^:?你方于|^(胜出)?(中标|成交)[,。]|^取得中标(单位)?资格|^以\d+[\d,.]+万?元(中标|成交|中选)" \
-                                         "|^通过(挂牌|拍卖)方式(以[\d.,]+万?元)?竞得|^[((](中标|成交|承包)人名?称?[))])|^确定为(中标|成交|中选)人)" # 去掉 |\w{,20} 修复 460216955 网上公布的与本次采购项目有关的信息视为已送达各响应供应商。 作为中标
+                                         "|^:贵公司参与|^:?你方于|^(胜出)?(中标|成交)[,。]|^取得中标(单位)?资格|^以\d+[\d,.]+万?元(中标|成交|中选)|^(公司)?:恭喜您中标" \
+                                         "|^通过(挂牌|拍卖)方式(以[\d.,]+万?元)?竞得|^[((](中标|成交|承包)人名?称?[))])|^确定为(中标|成交|中选)人)" # 去掉 |\w{,20} 修复 460216955 网上公布的与本次采购项目有关的信息视为已送达各响应供应商。 作为中标 # 633061180 尊敬的如皋市中正机械有限公司公司:恭喜您中标荆州建华张拉套筒询价
         self.pattern_winTenderer_whole = "(?P<winTenderer_center>(贵公司|由).{,15}以\w{,15}中标|确定[\w()]{5,20}为[^,。;]{5,50}的?中标单位" \
                                          "|选定报价最低的[“”\w()]{5,25}为[^,。;]{5,50}的?(服务|中标|成交)单位" \
                                          "|拟邀请[\w()]{5,20}(进行)?单一来源谈判|(承办单位|报价人|投标人|中介机构)(名称)?:[\w()]{5,20},(中标|承办|中选)(价格|金额)" \
@@ -1517,7 +1531,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>(((中[标选商]|成交|入围|入选)候选|投标)(人|单位|机构|中介(服务)?机构|供应商|客户|方|公司|厂商|商家?|社会资本方?|银行)|服务单位|候选企业)(:?单位名称|:?名称|全称|(?盖\w{,5}章)?|如下|:?牵头人|[及与和](成交|中标)金额)?[::是为]+$)"
 
         self.pattern_left = [
             self.pattern_tenderee_left_60,
@@ -2926,7 +2940,7 @@ class ProductAttributesPredictor():
         elif len(table.find_all(['table'])) >= 1:
             # print('过滤表格:包含多个表格的为假表格')
             inner_table_num = len(table.find_all(['table']))
-            text_num = 0 # 表格只有一格作文本框的数量,docid:631910513
+            text_num = 0  # 表格只有一格作文本框的数量,docid:631910513
             for inner_table in table.find_all(['table']):
                 if len(inner_table.find_all(['tr']))==0 or (len(inner_table.find_all(['tr']))==1 and len(inner_table.find_all(['tr'])[0].find_all(['td']))<=1):
                     text_num += 1
@@ -6197,7 +6211,7 @@ class DistrictPredictor():
 
             # addr = content
             # ree = ''
-            province_l2, city_l2, district_l2 = self.find_whole_areas('%s %s %s %s' % (ree, addr, addr_contact, addr_delivery), self.pettern, self.area_variance_dic, self.full_dic, weight=0.8)
+            province_l2, city_l2, district_l2 = self.find_whole_areas('%s %s %s' % (ree, addr, addr_delivery), self.pettern, self.area_variance_dic, self.full_dic, weight=0.8)
             province_l.extend(province_l2)
             city_l.extend(city_l2)
             district_l.extend(district_l2)
@@ -6210,7 +6224,7 @@ class DistrictPredictor():
                 not_sure = False
                 big_area, pred_pro, pred_city, pred_dis = big_area_1, pred_pro_1, pred_city_1, pred_dis_1
             if not_sure and (pred_city_2 == "" or prob < 0.7 or max_score<2):
-                province_l3, city_l3, district_l3 = self.find_whole_areas('%s %s'%(addr_bidopen, addr_bidsend), self.pettern, self.area_variance_dic, self.full_dic, weight=0.6)
+                province_l3, city_l3, district_l3 = self.find_whole_areas('%s %s %s'%(addr_contact, addr_bidopen, addr_bidsend), self.pettern, self.area_variance_dic, self.full_dic, weight=0.6)
                 province_l.extend(province_l3)
                 city_l.extend(city_l3)
                 district_l.extend(district_l3)
@@ -6743,13 +6757,15 @@ class TableTag2List():
 
                     # insert into self._output
                     try:
+                        if 'title' in cell.attrs and cell.get_text().replace(' ', '').endswith(
+                                '...') and cell.attrs['title'].replace(' ', '').startswith(cell.get_text().replace(' ', '')[:-3]):
+                            cell.string = cell.attrs['title']  # 修复 类似 215597851 215597851 省略号隐藏内容
                         if text_process != None:
                             # text = [re.sub('\xa0', '', text_process(cell, final=False)), 0]
                             # td_text = re.sub('\xa0', '', text_process(cell, final=False))
                             td_text = re.sub('\s|\xa0', '', str(cell.get_text()))  # 修复 370835008 td 内公司被p标签拆分为两半情况
-                            if 'title' in cell.attrs and cell.get_text().strip().endswith('...') and cell.get_text().strip()[:-3] in cell.attrs['title']:
-                                td_text = cell.attrs['title']  # 修复 类似 215597851 省略号隐藏内容
-                            elif len(td_text)>30:
+
+                            if len(td_text)>30:
                                 if return_kv:
                                     td_text = cell.get_text().strip()
                                 else:
@@ -6762,6 +6778,7 @@ class TableTag2List():
                                 td_text = cell.get_text().strip()
                             else:
                                 td_text = str(cell.get_text()).strip().replace("\x06", "").replace("\x05", "").replace("\x07", "").replace('\\', '').replace("(", "(").replace(')', ')').replace('?', '').replace('&nbsp', '')
+                                td_text = re.sub('\s+', ' ', td_text)
                             text = td_text
 
                             # text = str(cell.get_text()).strip().replace("\x06", "").replace("\x05", "").replace("\x07", "").replace('\\', '').replace("(", "(").replace(')', ')').replace('?', '')
@@ -7400,7 +7417,7 @@ class CandidateExtractor(object):
             "project_name": "(包[段组件]|标[段包的项]|标段(包)|分[包标]|采购|项目|工程|货物|商品|产品|设备|通用|主要标的|^包)(名称?|内容)|^标的$",
             "win_sort": "排名|排序|名次|推荐顺序",
             'win_or_not': '是否(建议|推荐)?(中标|成交)|是否入围|是否入库|入围结论|^选择设备$', # 补充站源特别表达:例:577351909 选择设备 1 为中标 0 非中标
-            "candidate": "((候选|入围|入选|投标|应答|响应)(供应商库)?的?(人|人?单位|机构|供应商|供货商|服务商|投标人|(中标)?公司|(中标)?企业|银行)|(通过)?名单|中标候选人)(名称|名单|全称|\d)?$|^供应商(名称|信息)?$|投标个人/单位|^公司名称$|供应商单位名称$", #补充 368295593 投标个人/单位 提取
+            "candidate": "((候选|入围|入选|投标|应答|响应|参选|比选)(供应商库)?的?(人|人?单位|机构|供应商|供货商|服务商|投标人|(中标)?公司|(中标)?企业|银行)|(通过)?名单|中标候选人)(名称|名单|全称|\d)?$|^供应商(名称|信息)?$|投标个人/单位|^公司名称$|供应商单位名称$", #补充 368295593 投标个人/单位 提取
             "bid_amount": "投标[报总]?价|报价(总?金额|总价|总额)|总报价|^\w{,5}报价(([\w、/]{1,15}))?$|(中标|成交|合同))?([金总]额|[报均总]价|价[格款]?)|承包价|含税价|经评审的价格",
             "win_tenderer": "第一名|第一(中标|成交)?候选人",
             "second_tenderer": "第二名|第二(中标|成交)?候选人",
@@ -7630,15 +7647,15 @@ class CandidateExtractor(object):
                         break
             elif candidate and win_sort:
                 role_type = ""
-                if re.search('第[一1]|^[一1]$', win_sort):
+                if re.search('第[一1]|^([一1]|01)$', win_sort):
                     role_type = "win_tenderer"
                     if win_or_not in ['否', '未中标', '0']: # 修复特别站源表达 577351909 选择设备:0 不是中标
                         role_type = ''
-                elif re.search('第[二2]|^[二2]$', win_sort):
+                elif re.search('第[二2]|^([二2]|02)$', win_sort):
                     role_type = "second_tenderer"
                     if win_or_not in ['是', '1']:
                         role_type = "win_tenderer"
-                elif re.search('第[三3]|^[三3]$', win_sort):
+                elif re.search('第[三3]|^([三3]|03)$', win_sort):
                     role_type = "third_tenderer"
                 if role_type != "":
                     if package not in prem_dic:
@@ -8635,7 +8652,7 @@ class EntityTypeRulePredictor():
         self.pattern_addr_bidopen = '([开评]标|开启|评选|比选|磋商|遴选|寻源|采购|招标|竞价|议价|委托|询比?价|比价|谈判|邀标|邀请|洽谈|约谈|选取|抽取|抽选))?(会议)?地[点址区]([((]网址[))])?[:为]'
         self.pattern_addr_bidsend = '((\w{,4}文件)?(提交|递交)(\w{,4}文件)?|投标)地[点址区]([((]网址[))])?[:为]'
         self.pattern_addr_delivery = '(交货|交付|收货|提货|交接|送货(安装)?|送达|到货|供货|卸货)((期|时间)[及和、])?)?(地[点址区]?|区域)[:为]'
-        self.pattern_addr_project = '(项目|施工|实施|建设|工程|服务|展示|看样|拍卖)(实施|服务|现场)?(地[点址区]|位置|所在地区?)(位于)?[:为]|项目位于|[^\w]所[属在](区域|地区?):|存放地[点址]?[:为]' # 银行所属区域:北京市西城区 不作项目地址
+        self.pattern_addr_project = '(项目|施工|实施|建设|工程|服务|展示|看样|拍卖|标的物)(实施|服务|现场)?(地[点址区]|位置|所在地区?)(位于)?[:为]|项目位于|[^\w]所[属在](区域|地区?):|存放地[点址]?[:为]' # 银行所属区域:北京市西城区 不作项目地址
         self.pattern_addr_contact = '((联系|收件人?|邮寄)地[点址区]|行政区)[:为]'
         self.pattern_time_planned = '(计划|预计|预期)(招标|采购|发标|发包)时间|招标(公告|文件)(预计|预期|计划)发布时间'
         self.pattern_code_investment = '投资(审批)?项目[编代]码[:为]'