Browse Source

优化处理反馈的角色、多中标人、金额、channnel、行业问题

lsm 10 months ago
parent
commit
a40373c77c

+ 4 - 2
BiddingKG/dl/entityLink/entityLink.py

@@ -308,7 +308,9 @@ def get_nlp_enterprise(list_entity):
                 credit_code = dic.get('credit_code', '')
                 in_text = 0 if entity.in_attachment else 1
                 if entity.label in [0,1,2,3,4] or len(dict_enterprise)<=max_num:
-                    dict_enterprise[entity.entity_text] = {'credit_code': credit_code, 'in_text': in_text}
+                    dict_enterprise[entity.entity_text] = {'in_text': in_text}
+                    if credit_code != "":
+                        dict_enterprise[entity.entity_text]['credit_code'] = credit_code
             else:
                 in_text = 0 if entity.in_attachment else 1
                 if in_text != dict_enterprise[entity.entity_text]['in_text']:
@@ -615,7 +617,7 @@ def calibrateEnterprise(list_articles,list_sentences,list_entitys):
                         find_flag = True
                         # 判断是否是多个公司
                         if re.search('[分支](公司|中心|监狱|部|行)|^\w{4,15}公司\w{2,3}公司$'
-                                     '|(大学|学院)\w{,2}附属\w{,6}医院$|(\w{2,5}办事处\w{2,6}$'
+                                     '|(大学|学院)\w{,2}附属\w{,6}医院$|(\w{2,5}办事处\w{2,6}$|^\w{2,6}银行\w{2,10}[分支]行$'
                                      '|\w{2,4}[省市县]\w{2,14}村)(股份)?经济(合作|联合)社$|国家税务总局\w{2,10}税务局$',
                                      p_entity.entity_text):
                             continue

+ 9 - 2
BiddingKG/dl/interface/Preprocessing.py

@@ -1101,11 +1101,13 @@ def tableToText(soup, docid=None):
                                         text_line += head+cell["text"]+","
                                 text_set.add(str(head+cell["text"]))
                                 last_text = cell['text']
-
+                        tr_text = pack_text+rank_text+entity_text+money_text+text_line
                         text += pack_text+rank_text+entity_text+money_text+text_line
                         # text = text[:-1] + "。" if len(text) > 0 else text
                         if len(text_set-set([' ']))==1 and head == '' and len(last_text)< 25: # 修复367694716分两行表达
                             text = text if re.search('\w$', text[:-1]) else text[:-1]
+                        elif (width == 2 or len(text_set)==1) and head != '' and len(tr_text)<50: # 修复494731937只有两行的,分句不合理
+                            text = text if re.search('\w$', text[:-1]) else text[:-1]
                         else:
                             text = text[:-1] + "。"
 
@@ -3020,6 +3022,9 @@ def get_preprocessed_article(articles,cost_time = dict(),useselffool=True):
         if ser:
             article_processed = article_processed.replace(ser.group(0), '竞得人:') # 修复类似 368120777 关键词角色被编号隔开情况
         article_processed = re.sub("流出方信息:。", "流出方信息:", article_processed) # 修复 367520674 产权批量表格问题
+        idx = article_processed.find('供应商报名、缴纳保证金、下载采购文件流程.docx。##attachment##。') # 修复404230599 E交易站源批量附件中标人错误
+        if idx > 1000:
+            article_processed = article_processed[:idx]
 
         '''去除业绩内容'''
         article_processed = del_achievement(article_processed)
@@ -3338,6 +3343,8 @@ def get_money_entity(sentence_text, found_yeji, in_attachment=False):
                 elif re.search('^[-—]+[\d,.]+万元', sentence_text[end_index:]):
                     # print('两个金额连接后面的有单位,用后面单位')
                     unit = '万元'
+                elif re.search('^,?(价格币种:\w{2,3},)?价格单位:万元', sentence_text[end_index:]): # 修复494731937金额单位缺漏 中标价格:39501.094425,价格币种:人民币,价格单位:万元,
+                    unit = '万元'
                 elif re.search('([单报标限总造]价款?|金额|租金|(中标|成交|合同|承租|投资|控制|拦标))?[价额]|价格|预算(金额)?|(监理|设计|勘察)(服务)?费)(小写)?[::为]*-?$', text_beforeMoney.strip()) and re.search('^0|1[3|4|5|6|7|8|9]\d{9}', entity_text) == None:  # 修复
                     if re.search('^[\d,,.]+$', entity_text) and float(re.sub('[,,]', '', entity_text))<500 and re.search('万元', sentence_text):
                         unit = '万元'
@@ -3462,7 +3469,7 @@ 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 = []

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

@@ -359,7 +359,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': '2024-07-08'}
+    version_date = {'version_date': '2024-07-18'}
     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:
@@ -371,6 +371,14 @@ def predict(doc_id,text,title="",page_time="",web_source_no='',web_source_name="
         punish_dic = predictor.getPredictor("punish").get_punish_extracts(list_articles,list_sentences, list_entitys)
         cost_time["punish"] = round(time.time()-start_time,2)
         data_res['punish'] = punish_dic
+        if "Project" in data_res['prem']:
+            for d in data_res['prem']['Project']['roleList']:
+                if d['role_name'] == 'tenderee' and d.get('role_prob', 0.6) < 0.6:  # 处罚公告 去掉低概率招标人
+                    data_res['prem']['Project']['roleList'] = [d for d in data_res['prem']['Project']['roleList'] if d['role_name'] != 'tenderee']
+                    break
+            if len(data_res['prem']['Project']['roleList']) == 0 and data_res['prem']['Project'].get('tendereeMoney', 0) in [0, '0']: # 删除空包
+                data_res['prem'].pop('Project')
+
 
     '''最终检查修正招标、中标金额'''
     getAttributes.limit_maximum_amount(data_res, list_entitys[0])

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

@@ -3922,7 +3922,8 @@ def get_multi_winner_and_money(channel_dic, prem, list_entitys,list_sentences):
                 else:
                     moneys.append(money)
             if ent.entity_type in ['org', 'company'] and ent.label == 2 and ent.values[ent.label]>0.8:
-                multi_winner_l.append(ent.entity_text)
+                if ent.entity_text not in multi_winner_l:
+                    multi_winner_l.append(ent.entity_text)
                 sentence_text = sentences[ent.sentence_index].sentence_text
                 pre_text = sentence_text[max(0, b_idx_fr-10):b_idx_fr]
                 if re.search('入围', pre_text) and re.search('未入围', pre_text)==None and ent.entity_text not in finalists:
@@ -3934,13 +3935,15 @@ def get_multi_winner_and_money(channel_dic, prem, list_entitys,list_sentences):
                     if ent_bh.entity_type in ['org', 'company'] and ent_bh.label == 5 and ent_bh.sentence_index == ent.sentence_index and b_idx_bh-e_idx_fr==1:
                         sentence_text = sentences[ent_bh.sentence_index].sentence_text
                         if sentence_text[e_idx_fr:b_idx_bh] in [';','、','&',','] and (len(sentence_text)==e_idx_bh or sentence_text[e_idx_bh] in [';','、','&', ',', '。']): # 修复多中标人刚好在文末index超出报错,例子 407126558
-                            multi_winner_l.append(ent_bh.entity_text)
+                            if ent_bh.entity_text not in multi_winner_l:
+                                multi_winner_l.append(ent_bh.entity_text)
                             e_idx_fr = e_idx_bh
                             i = j + 1
                         else:
                             break
                     elif ent_bh.entity_type in ['org', 'company'] and ent_bh.label == 5 and ent_bh.sentence_index == ent.sentence_index and b_idx_bh==e_idx_fr:
-                        multi_winner_l.append(ent_bh.entity_text)
+                        if ent_bh.entity_text not in multi_winner_l:
+                            multi_winner_l.append(ent_bh.entity_text)
                         e_idx_fr = e_idx_bh
                         i = j + 1
                     else:
@@ -3952,7 +3955,8 @@ def get_multi_winner_and_money(channel_dic, prem, list_entitys,list_sentences):
                     for v in project.values():
                         for d in v['roleList']:
                             if d.get('role_name', '') == 'win_tenderer' and d.get('role_text', '') == multi_winner_l[0]:
-                                d['multi_winner'] = ','.join(set(multi_winner_l))
+                                # d['multi_winner'] = ','.join(set(multi_winner_l))
+                                d['multi_winner'] = ','.join(multi_winner_l)
                                 break
         if len(finalists)>=2:
             for project in prem[0].values():

+ 61 - 55
BiddingKG/dl/interface/predictor.py

@@ -430,50 +430,50 @@ class CodeNamePredict():
                                                 code_set.add(it)
                                                 # item['code'].append(it)
                                                 if re.search("(项目编号|招标编号):?$", pre_text[h]):
-                                                    item['code'].append((it, 0))
+                                                    item['code'].append((it, 0, sentence.sentence_index))
                                                 elif re.search('采购(计划)?编号:?$', pre_text[h]):
-                                                    item['code'].append((it, 1))
+                                                    item['code'].append((it, 1, sentence.sentence_index))
                                                 elif re.search('(询价|合同)编号:?$', pre_text[h]):
-                                                    item['code'].append((it, 2))
+                                                    item['code'].append((it, 2, sentence.sentence_index))
                                                 else:
-                                                    item['code'].append((it, 3))
+                                                    item['code'].append((it, 3, sentence.sentence_index))
                                         elif len(item['code']) > 0:
                                             new_it = item['code'][-1][0] + re.search(',|/|;|、|,', the_code).group(0) + it
                                             if new_it not in code_set:
                                                 code_set.add(new_it)
                                                 # item['code'][-1] = new_it
                                                 if re.search("(项目编号|招标编号):?$", pre_text[h]):
-                                                    item['code'][-1] = (new_it, 0)
+                                                    item['code'][-1] = (new_it, 0, sentence.sentence_index)
                                                 elif re.search('采购(计划)?编号:?$', pre_text[h]):
-                                                    item['code'][-1] = (new_it, 1)
+                                                    item['code'][-1] = (new_it, 1, sentence.sentence_index)
                                                 elif re.search('(询价|合同)编号:?$', pre_text[h]):
-                                                    item['code'][-1] = (new_it, 2)
+                                                    item['code'][-1] = (new_it, 2, sentence.sentence_index)
                                                 else:
-                                                    item['code'][-1] = (new_it, 3)
+                                                    item['code'][-1] = (new_it, 3, sentence.sentence_index)
                                         else:
                                             if the_code not in code_set:
                                                 code_set.add(the_code)
                                                 # item['code'].append(the_code)
                                                 if re.search("(项目编号|招标编号):?$", pre_text[h]):
-                                                    item['code'].append((the_code, 0))
+                                                    item['code'].append((the_code, 0, sentence.sentence_index))
                                                 elif re.search('采购(计划)?编号:?$', pre_text[h]):
-                                                    item['code'].append((the_code, 1))
+                                                    item['code'].append((the_code, 1, sentence.sentence_index))
                                                 elif re.search('(询价|合同)编号:?$', pre_text[h]):
-                                                    item['code'].append((the_code, 2))
+                                                    item['code'].append((the_code, 2, sentence.sentence_index))
                                                 else:
-                                                    item['code'].append((the_code, 3))
+                                                    item['code'].append((the_code, 3, sentence.sentence_index))
                                             break
                                 elif the_code not in code_set:
                                     code_set.add(the_code)
                                     # item['code'].append(the_code)
                                     if re.search("(项目编号|招标编号):?$", pre_text[h]):
-                                        item['code'].append((the_code, 0))
+                                        item['code'].append((the_code, 0, sentence.sentence_index))
                                     elif re.search('采购(计划)?编号:?$', pre_text[h]):
-                                        item['code'].append((the_code, 1))
+                                        item['code'].append((the_code, 1, sentence.sentence_index))
                                     elif re.search('(询价|合同)编号:?$', pre_text[h]):
-                                        item['code'].append((the_code, 2))
+                                        item['code'].append((the_code, 2, sentence.sentence_index))
                                     else:
-                                        item['code'].append((the_code, 3))
+                                        item['code'].append((the_code, 3, sentence.sentence_index))
 
                                 # if the_code not in code_set:
                                 #     code_set.add(the_code)
@@ -573,18 +573,18 @@ class CodeNamePredict():
                     if othercode != None:
                         # item['code'].append(othercode.group('code'))
                         if re.search("(项目编号|招标编号):?$", othercode.group(0)):
-                            item['code'].append((othercode.group('code'), 0))
+                            item['code'].append((othercode.group('code'), 0, sentence.sentence_index))
                         elif re.search('采购(计划)?编号:?$', othercode.group(0)):
-                            item['code'].append((othercode.group('code'), 1))
+                            item['code'].append((othercode.group('code'), 1, sentence.sentence_index))
                         elif re.search('(询价|合同)编号:?$', othercode.group(0)):
-                            item['code'].append((othercode.group('code'), 2))
+                            item['code'].append((othercode.group('code'), 2, sentence.sentence_index))
                         else:
-                            item['code'].append((othercode.group('code'), 3))
+                            item['code'].append((othercode.group('code'), 3, sentence.sentence_index))
                         # print('规则召回项目编号:', othercode.group('code'))
             # item['code'] = [code for code in item['code'] if len(code)<500]
             # item['code'].sort(key=lambda x:len(x),reverse=True)
             item['code'] = [code for code in item['code'] if len(code[0]) < 500]
-            item['code'].sort(key=lambda x: x[1])
+            item['code'].sort(key=lambda x: [x[1],x[2]])
             item['code'] = [it[0] for it in item['code']]
             result.append(item)
 
@@ -703,7 +703,7 @@ class PREMPredict():
                             text_sen = sentence.sentence_text
                             b = entity.wordOffset_begin
                             e = entity.wordOffset_end
-                            text_list.append((text_sen[max(0, b-13):b], text_sen[b:e], text_sen[e:e+10]))
+                            text_list.append((text_sen[max(0, b-13):b], text_sen[b:e], text_sen[e:e+15]))
                             # item_x = embedding(spanWindow(tokens=sentence.tokens,begin_index=entity.begin_index,end_index=entity.end_index,size=settings.MODEL_ROLE_INPUT_SHAPE[1]),shape=settings.MODEL_ROLE_INPUT_SHAPE)
                             # item_x = self.model_role.encode(tokens=sentence.tokens,begin_index=entity.begin_index,end_index=entity.end_index,entity_text=entity.entity_text)
                             item_x = self.model_role.encode_word(sentence_text=text_sen, begin_index=entity.wordOffset_begin, end_index=entity.wordOffset_end, size=30)
@@ -805,6 +805,9 @@ class PREMPredict():
             # print('模型预测角色:', front, entity.entity_text, behind,label, values)
             # if label in [0, 1, 2, 3, 4]:
             #     self.role_file.write("{0}#split#{1}#split#{2}#split#{3}#split#{4}\n".format(front, entity.entity_text, behind,label, entity.doc_id))
+            if re.search('^以\d+[\d,.]+万?元中标', behind) and label != 2: # 优化244261884预测错误 大连长之琳科技发展有限公司以7.63277万元中标
+                label = 2
+                values[label] = 0.8
             if label in [0, 1, 2, 3, 4] and values[label] < 0.5: # 小于阈值的设为其他,让后面的规则召回重新判断
                 # print(' # 小于阈值的设为其他,让后面的规则召回重新判断', values[label])
                 label = 5
@@ -884,6 +887,9 @@ class PREMPredict():
                     label = 5
                 elif re.search('委托$', front) and re.search('^(抽样|送检|看样)', behind):
                     label = 5
+                elif re.search('推荐入围的招标代理单位:$', front): # 20240709 修复302505502预测错为代理
+                    label = 2
+                    values[label] = 0.501
             elif label in [3,4]:
                 if re.search('第[二三]分(公司|店),中标(人|供应商|单位|公司):$', front):
                     label = 2
@@ -955,16 +961,16 @@ class PREMPredict():
                     values[label] = 0.5
                 elif re.search('[\+=]((中标|成交)(金?额|价格?)|[若如]果?(中标|成交)(金?额|价格?)为?', front): # 处理例如 241561780 如中标金额为 500-1000万元,则代理服务费=100 万元×0.5%+400万元×0.35%+(中标金额-500)万元
                     values[label] = 0.49
-                elif re.search('^(以[上下])?按[\d.%]+收取|^以[上下]|^[()]?[+×*-][\d.%]+', behind):
+                elif re.search('^(以[上下])?按[\d.%]+收取|^及?以[上下]|^[()]?[+×*-][\d.%]+', behind):
                     values[label] = 0.49
                 elif re.search('(含|在|包括|[大小等高低]于|达到)$|[\d.%]+[+×*-]$', front):
                     values[label] = 0.49
                 elif entity.notes == '单价' and float(entity.entity_text)<5000:
                     label = 2
             elif label ==0: # 错误招标金额处理
-                if entity.notes in ["投资", "总投资","工程造价"] or re.search('最低限价:?$', front) or re.search('服务内容:([\d,.]+万?亿?元?-?)$', front):
+                if entity.notes in ["投资", "总投资","工程造价"] or re.search('最低限价:?$|注册资本', front) or re.search('服务内容:([\d,.]+万?亿?元?-?)$', front):
                     values[label] = 0.49
-                elif re.search('^(以[上下])?按[\d.%]+收取|^以[上下]|^[()]?[+×*-][\d.%]+|(含)', behind):
+                elif re.search('^(以[上下])?按[\d.%]+收取|^及?以[上下]|^[()]?[+×*-][\d.%]+|(含)', behind):
                     values[label] = 0.49
                 elif re.search('(含|在|包括|[大小等高低]于|如预算金额为)$|[\d.%]+((含))?[+×*-]$', front):
                     values[label] = 0.49
@@ -1403,7 +1409,7 @@ class RoleRulePredictor():
                "(乙|竞得|受让|买受|签约|施工|供货|供应?|合作|承做|承包|承建|承销|承保|承接|承制|承担|承修|承租((包))?|入围|入选|竞买)(候选|投标)?(人|单位|机构|供应商|方|公司|企业|厂商|商|社会资本方?)(:?单位名称|:?名称|盖章)?[::是为]+$" \
                "|(选定单位|指定的中介服务机构|实施主体|中标银行|中标通知书,致|征集结果|选择中介|选择结果|成交对象|勘察人|(,|审计|处置|勘察|设计)服务单位|受托[人方])[::是为]+$" \
                "|((评审结果|名次|排名|中标结果)[::]*第?[一1]名?)[::是为]+$|成交供应商信息[,:]?(序号1)?:?|供应商名称$" \
-               "|单一来源(采购)?(供应商|供货商|服务商|方式向)$|((中标|成交)(结果|信息))[::是为]+$" \
+               "|单一来源(采购)?(供应商|供货商|服务商|方式向)$|((中标|成交)(结果|信息))[::是为]+$|(中标|成交)供应商、(中标|成交)(金额|价格),$" \
                "|现(公布|宣布|公示)中标单位如下:$|现将中标单位(公布|公示)如下:$|现宣布以下(企业|单位|公司)中标:$|经讨论,决定采用$)"  # 承办单位:不作为中标 83914772
         self.pattern_winTenderer_left_60 = "(?P<winTenderer_left_60>" \
                                            "(,|。|:|^)((中标(投标)?|[拟预]中标|中选|中价|中签|成交)(人|单位|机构|中介(服务)?机构|供应商|客户|方|公司|企业|厂商|商家?|社会资本方?)|(中标候选人)?第?[一1]名|第[一1](中标|中选|成交)?候选人|服务机构)" \
@@ -1414,7 +1420,7 @@ class RoleRulePredictor():
 
         self.pattern_winTenderer_right = "(?P<winTenderer_right>(^[是为](首选)?((采购|中标|成交)(供应商|供货商|服务商)|(第[一1]|预)?(拟?(中标|中选|中价|成交)(候选|排序)?(人|单位|机构|供应商|公司|企业|厂商)))|" \
                                          "^((报价|价格)最低,|以\w{5,10})?(确定|成|作)?为[\w“”()]{3,25}((成交|中选|中标|服务)(人|单位|供应商|企业|公司)|供货单位|供应商|第一中标候选人)[,。]" \
-                                         "|^:贵公司参与|^:?你方于|^(胜出)?中标。|^取得中标(单位)?资格" \
+                                         "|^:贵公司参与|^:?你方于|^(胜出)?中标。|^取得中标(单位)?资格|^以\d+[\d,.]+万?元(中标|成交|中选)" \
                                          "|^通过(挂牌|拍卖)方式(以[\d.,]+万?元)?竞得|^[((](中标|成交|承包)人名?称?[))]))" # 去掉 |\w{,20} 修复 460216955 网上公布的与本次采购项目有关的信息视为已送达各响应供应商。 作为中标
         self.pattern_winTenderer_whole = "(?P<winTenderer_center>(贵公司|由).{,15}以\w{,15}中标|确定[\w()]{5,20}为[^,。;]{5,50}的?中标单位" \
                                          "|选定报价最低的[“”\w()]{5,25}为[^,。;]{5,50}的?(服务|中标|成交)单位" \
@@ -2234,10 +2240,11 @@ class RoleGrade():
         self.winTenderer_left_9 = "(?P<winTenderer_left_9>(中标|中选|中价|成交|竞得)|第[1一]名|排[名序]:1|名次:1)"
         self.winTenderer_left_8 = "(?P<winTenderer_left_8>(入选供应商|供货商|乙方|最[终后]选[择取]))"  # 229435497 最后选择西平,县中原彩印有限公司,作为此项目中标供应商,
         self.winTenderer_left_6 = "(?P<winTenderer_left_6>(入围|承[接建包修做制担租销]))"
+        self.winTenderer_right_9 = "(?P<winTenderer_right_9>^(为(中标|成交|中选)(人|单位|供应商|公司)|以\d+[\d.,]+万?元中标))"
         self.secondTenderer_left_9 = "(?P<secondTenderer_left_9>(第[二2](中标|中选|中价|成交)?候选(人|单位|供应商|公司)|第[二2]名|排[名序]:2|名次:2))"
         self.thirdTenderer_left_9 = "(?P<thirdTenderer_left_9>(第[三3](中标|中选|中价|成交)?候选(人|单位|供应商|公司)|第[三3]名|排[名序]:3|名次:3))"
         self.pattern_list = [self.tenderee_left_9,self.tenderee_center_8, self.tenderee_left_8,self.tenderee_left_6,self.tenderee_left_5,self.agency_left_9,
-                             self.winTenderer_left_6, self.winTenderer_left_9,self.winTenderer_left_8, self.secondTenderer_left_9, self.thirdTenderer_left_9]
+                             self.winTenderer_left_6, self.winTenderer_left_9,self.winTenderer_left_8, self.winTenderer_right_9, self.secondTenderer_left_9, self.thirdTenderer_left_9]
     def predict(self, list_sentences, list_entitys, original_docchannel, span=15, min_prob=0.7):
         '''
         根据规则给角色分配不同等级概率;分三级:0.9-1,0.8-0.9,0.7-0.8;附件0.7-0.8,0.6-0.7,0.5-0.6
@@ -3923,7 +3930,7 @@ class DocChannel():
       self.type_dic = {
           '土地矿产': '供地结果|(土地|用地|宗地|地块|海域|矿)的?(基本信息|基本情况|概况|信息|详情|来源|用途|性质|编号|位置|坐落|使用年限|出让年限)|(土地|山地|农田)(经营权)?(出让|出租|招租|租赁|承包|流转)|流转土地',
           '拍卖出让': '(拍卖|变卖|流拍|竞拍)的?(公告|活动|信息|结果|成交|主体|标的|资产|财产|方式|类型|流程|程序|规则|价格|保证金|时间)|(公开|进行|密封)(拍卖|变卖|竞拍)|第[一二三]次拍卖|(资产|司法|网络)拍卖|交易方式.{,2}拍卖|拍卖会',
-          '产权交易': '(产权|资产|权证)的?(类型|信息|名称|编号|(基本)?情况)|(经营权|承包权|使用权|租赁权|股权|债权|排污权|化学需氧量|储备量)(挂牌|转让|出让)|竞价销售|销售结果|房屋所有权房产|免租期限|交易期限|(受让|转让|承租|出租)(人|方)|(店面|店铺|商铺|铺位?|门面|门市|食堂|饭堂|校舍|车位|停车场|厂?房|仓?库|馆|资产|物业|房产|房屋|场地|农田|鱼?塘)\w{,4}(处置|招租|出租|续租|租赁|转让)|(出租|转让|产权|资产)(项目|中标|成交|流标|废标)|出租(用途|类型)|转让底价|租赁(标的物|情况)|看样(时间|地[点址]|方式)|最小加价|加价幅度',
+          '产权交易': '(产权|资产|权证)的?(类型|类别|用途|性质|状态|信息|名称|编号|(基本)?情况)|(经营权|承包权|使用权|租赁权|股权|债权|排污权|化学需氧量|储备量)(挂牌|转让|出让)|竞价销售|销售结果|房屋所有权房产|免租期限|交易期限|(受让|转让|承租|出租)(人|方)|(店面|店铺|商铺|铺位?|门面|门市|食堂|饭堂|校舍|车位|停车场|厂?房|仓?库|馆|资产|物业|房产|房屋|场地|农田|鱼?塘)\w{,4}(处置|招租|出租|续租|租赁|转让)|(出租|转让|产权|资产)(项目|中标|成交|流标|废标)|出租(用途|类型)|转让底价|租赁(标的物|情况)|看[货](时间|地[点址]|方式|仓库|验货)|最小加价|加价[梯]|交易模式[::\s]*延时竞价销售|挂牌(开始|结束)时间',
           '采招数据': '(采购|招标)(条件|范围|文件|内容)|(申请人|投标人|供应商|报价人|参选人)的?资格要求;|采购需求清单|最低价排序|竞争性采购方式|采购进行公开竞价|竞价模式[::\s]*一次报价|预算金额'  # |变更|答疑|澄清|中标|成交|合同|废标|流标 |(采购|招标|代理)(人|机构|单位)|
       }
 
@@ -3931,7 +3938,7 @@ class DocChannel():
           '土地矿产': '(土地|用地|宗地|荒地|山地|海域|矿)(出让|出租|招租|租赁|承包|流转|使用权|经营权|征收|划拨|中标|成交)|供地结果|矿业权|探矿权|采矿权|(土地|用地|宗地|地块)(使用权)?(终止|中止|网上)?(挂牌|出让|拍卖|招拍|划拨)|征收土地',
           '拍卖出让': '(拍卖|变卖|流拍|竞拍)的?(公告|公示)|拍卖|变卖|流拍|竞拍',
           '产权交易': '经营权|承包权|使用权|租赁权|股权|债权|排污权|化学需氧量|储备量|竞价销售|销售结果|出租|招租|拍租|竞租|续租|挂牌|出让',
-          '采招数据': '(采购|招标|询价|议价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判)的?(公告|公示|中标|成交|结果|$)|工程招标|定点服务|竞价采购|(中标|成交)(结果)?(公告|公示)',
+          '采招数据': '(采购|招标|询价|议价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|征询|调研)的?(公告|公示|中标|成交|结果|$)|工程招标|定点服务|竞价采购|(设备|服务)采购|网上超市采购|定点采购',
           # |竞价 采招/产权都有竞价方式 # 意向|需求|预公?告|报建|总承包|工程|施工|设计|勘察|代理|监理 |变更|答疑|澄清|中标|成交|合同|废标|流标
           '新闻资讯': '(考试|面试|笔试)成绩|成绩的?(公告|公示|公布)|公开招聘|招聘(公告|简章|启事|合同制)|疫情防控\s{,5}(通知|情况|提示)|行政审批结果'
       }
@@ -3939,22 +3946,22 @@ class DocChannel():
           '采购意向': '采购意向|招标意向|选取意向|意向公告|意向公示',
           '采购意向neg': '发布政府采购意向|采购意向公告已于',
           '招标预告': '(预计|计划)(采购|招标)(时间|日期)|采购(计划编号|需求方案|预告|预案)|(预|需求)公示|需求(方案|信息|论证|公告|公示)',
-          '招标公告': '(采购|招标|竞选|报名)条件|报名(时间|流程|方法|要求|\w{,5}材料)[:\s]|[^\w]成交规则|参加竞价采购交易资格|(申请人|投标人|供应商|报价人|参选人)的?资格要求|获取(采购|招标|询价|议价|竞价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|竞谈|应答)文件|(采购|招标|询价|议价|竞价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|竞谈|应答)文件的?(获取|领取)',
+          '招标公告': '(采购|招标|竞选|报名)条件|报名(时间|流程|方法|要求|\w{,5}材料)[:\s]|[^\w]成交规则|参加竞价采购交易资格|(申请人|投标人|供应商|报价人|参选人)的?资格要求|获取(采购|招标|询价|议价|竞价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|竞谈|应答)文件|(采购|招标|询价|议价|竞价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|竞谈|应答)文件的?(获取|领取)|评选方式:?\s*价格最低',
           '资审结果': '资审及业绩公示|资审结果及业绩|资格后审情况报告|资格(后审|预审|审查)结果(公告|公示)|(预审|审查)工作已经?结束|未通过原因', #|资格
           '招标答疑': '现澄清(为|如下)|答疑补遗|澄清内容如下|第[0-9一二三四五]次澄清|答疑澄清|(最高(投标)?限价|控制价|拦标价)公示',  # |异议的回复
           '公告变更': '第[\d一二]次变更|(更正|变更)(公告|公示|信息|内容|事项|原因|理由|日期|时间|如下)|原公告((主要)?(信息|内容)|发布时间)|(变更|更正)[前后]内容|现?在?(变更|更正|修改|更改)(内容)?为|(公告|如下|信息|内容|事项|结果|文件|发布|时间|日期)(更正|变更)',
           '公告变更neg': '履约变更内容',
           '候选人公示': '候选人公示|评标结果公示|中标候选人名单公示|现将中标候选人(进行公示|公[示布]如下)|(中标|中选)候选人(信息|情况)[::\s]',
           '候选人公示neg': '中标候选人公示期|中标候选人公示前',
-          '中标信息': '供地结果信息|采用单源直接采购的?情况说明|[特现]?将\w{,4}(成交|中标|中选|选定结果|选取结果|入围结果|竞价结果)\w{,4}(进行公示|公[示布]如下)|(询价|竞价|遴选)(成交|中标|中选)(公告|公示)|(成交|中标|中选|选定|选取|入围|询价)结果(如下|公告|公示)|(中标|中选)(供应商|承包商|候选人|入围单位)如下|拟定供应商的情况|((中标|中选)(人|成交)|成交)\w{,3}(信息|情况)[::\s]',
-          '中标信息2': '\s(成交|中标|中选)(信息|日期|时间|总?金额|价格)[::\s]|(采购|招标|成交|中标|中选|评标)结果|单一来源(采购|招标)?的?(中标|成交|结果)|项目已结束|中标公示 ', # |单一来源采购原因|拟采取单一来源方式采购|单一来源采购公示
+          '中标信息': '供地结果信息|采用单源直接采购的?情况说明|[特现]?将\w{,4}(成交|中标|中选|选定结果|选取结果|入围结果|竞价结果)\w{,4}(进行公示|公[示布]如下)|(询价|竞价|遴选)?(成交|中标|中选)(公告|公示)|(成交|中标|中选|选定|选取|入围|询价)结果(如下|公告|公示)|(中标|中选)(供应商|承包商|候选人|入围单位)如下|拟定供应商的情况|((中标|中选)(人|成交)|成交)\w{,3}(信息|情况)[::\s]',
+          '中标信息2': '\s(成交|中标|中选)(信息|日期|时间|总?金额|价格)[::\s]|(成交|中标|中选)价格\s*[\d.,]+(?万?元|(采购|招标|成交|中标|中选|评标)结果|单一来源(采购|招标)?的?(中标|成交|结果)|项目已结束|中标公示 ', # |单一来源采购原因|拟采取单一来源方式采购|单一来源采购公示
           '中标信息3': '(中标|中选|成交|拟定|拟选用|最终选定的?|受让)(供应商|供货商|服务商|机构|企业|公司|单位|候选人|人)(信息[,:]?)?(名称)?[::\s]|[、\s](第一名|(拟定|推荐|入围)?(供应商|供货商)|(中选|中标|供货)单位|中选人)[::\s]|确定[\w()]{6,25}为中标人', # |唯一
-          '中标信息neg': '按项目控制价下浮\d%即为成交价|成交原则|不得确定为(中标|成交)|招标人按下列原则选择中标人|评选成交供应商:|拟邀请供应商|除单一来源采购项目外|单一来源除外|(各.{,5}|尊敬的)(供应商|供货商)[:\s]|竞拍起止时间:|询价结果[\s\n::]*不公开|本项目已具备招标条件|现对该项目进行招标公告|发布\w{2}结果后\d天内送达|本次\w{2}结果不对外公示|供应商\s*资格要求|成交情况:\s*[流废]标|中标单位:本次招标拟?中标单位\d家|通知中标单位|影响(成交|中标)结果',
+          '中标信息neg': '按项目控制价下浮\d%即为成交价|成交原则|不得确定为(中标|成交)|招标人按下列原则选择中标人|评选成交供应商:|拟邀请供应商|除单一来源采购项目外|单一来源除外|(各.{,5}|尊敬的)(供应商|供货商)[:\s]|竞拍起止时间:|询价结果[\s\n::]*不公开|本项目已具备招标条件|现对该项目进行招标公告|发布\w{2}结果后\d天内送达|本次\w{2}结果不对外公示|供应商\s*资格要求|成交情况:\s*[流废]标|中标单位:本次招标拟?中标单位\d家|通知中标单位|影响(成交|中标)结果|确定为成交供应商|(成交|中标|中选)公[告示](发布|\w{,2})后|竞价成交后', # 503076535 按照服务方案的优劣 确定为成交供应商
       # |确定成交供应商[:,\s]
           '合同公告': '合同(公告|公示|信息|内容)|合同(编号|名称|主体|基本情况|完成(日期|时间))|(供应商乙方|乙方供应商):|合同总?金额|履约信息',
           '废标公告': '(终止|中止|废标|流标|流采|失败|作废|异常|撤销)(结果)?(公告|公示|招标|采购|竞价)|(谈判结果为|结果类型):?废标|((本|该)(项目|标段|合同|合同包|采购包|次)\w{,5})((失败|终止|流标|废标)|予以废标|(按|做|作)?(流标|废标|废置)处理)|(采购|招标|询价|议价|竞价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|竞谈|应答|项目)(终止|中止|废标|流标|失败|作废|异常|撤销)',
           '废标公告2': '(无效|中止|终止|废标|流标|失败|作废|异常|撤销)的?(原因|理由)|本项目因故取消|本(项目|次)(公开)?\w{2}失败|已终止\s*原因:|(人|人数|供应商|单位)(不足|未达\w{,3}数量)|已终止|不足[3三]家|无(废标)|成交情况:\s*[流废]标|现予以废置',
-          '废标公告neg': '超过此报价将作为[废流]标处理|否则按[废流]标处理|终止规则:|成交规则:|视为流标|竞价失败的一切其他情形|是否废标:否'
+          '废标公告neg': '超过此报价将作为[废流]标处理|否则按[废流]标处理|终止规则:|成交规则:|视为流标|竞价失败的一切其他情形|是否废标:否|若不足三家公司参与|供应商数量:?\s*报名供应商不足三家|有效报价不足三家,\s*系统自动废标' # 503076535 供应商数量: 报名供应商不足三家。
       }
       self.title_life_dic = {
           '采购意向': '采购意向|招标意向|选取意向|意向公告|意向公示|意向公开',
@@ -3963,8 +3970,8 @@ class DocChannel():
           '招标答疑': '质疑|澄清|答疑(文件)?|补遗书?|(最高(投标)?限价|控制价|拦标价)(公示|公告|$)',
           '废标公告': '(终止|中止|废标|废除|废置|流标|失败|作废|异常|撤销|撤回|取消成?交?|流拍)(结果|竞价|项目)?的?(公告|公示|$)|(终止|中止)(采购|招标|询价|议价|竞价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|拍卖|招租|交易|出让)|关于废置',
           '合同公告': '(合同(成交|变更)?)(公告|公示|信息|公式|公开|签订)|合同备案|合同书|合同$', # |(履约|验收)(结果)?
-          '候选人公示': '候选人(变更)?公示|评标(结果)?公示|中标前?公示|中标预公示',
-          '中标信息': '(中标|中选|中价|中租|成交|入选|确认)(候选人|人|供应商|记录|结果|变更)?(公告|公示|结果)|未?入围(公示|公告)|(遴选|采购|招标|竞价|议价|比选|询比?价|评选|谈判|邀标|邀请|洽谈|约谈|评标|发包|遴选|交易)\w{,2}结果|单一来源(采购|招标)?的?(中标|成交|结果)|中标通知书|中标$', # |开标(记录|信息|情况)
+          '候选人公示': '候选人(变更)?公示|评标(结果)?公示|中标前?公示|中标预公示|评审结果',
+          '中标信息': '(中标|中选|中价|中租|成交|入选|确认)(候选人|人|供应商|记录|结果|变更)?(公告|公示|结果)|未?入围(公示|公告)|(遴选|采购|招标|竞价|议价|比选|询比?价|评选|谈判|邀标|邀请|洽谈|约谈|评标|发包|遴选|交易)\w{,2}结果|单一来源(采购|招标)?的?(中标|成交|结果)|中标通知书|中标$|项目中标', # |开标(记录|信息|情况)
           '资审结果': '((资格|资质)(审查|预审|后审|审核)|资审)结果(公告|公示)?|(资质|资格)(预审|后审)公示|资审及业绩公示',
           '招标公告': '(采购|招标|询价|议价|竞价|比价|比选|遴选|邀请|邀标|磋商|洽谈|约谈|谈判|拍卖|招租|交易|出让)的?(公告|公示|$)|公开(采购|招标|招租|拍卖|挂牌|出让)|(资审|预审|后审)公告',
           '开标记录': '开标记录|截标信息|评委名单公示|开标安排|开标数据表|开标信息|开标情况|开标一览表|开标结果|开标会',
@@ -4368,7 +4375,7 @@ class DocChannel():
           elif '验收合同' in life_kw_title:
               return '验收合同', msc
           elif '候选人公示' in life_kw_title or '候选人公示' in life_list:
-              if '招标公告' in life_kw_title and life_score.get('招标公告', 0) > 3:
+              if '招标公告' in life_kw_title and '候选人公示' not in life_kw_title: # and life_score.get('招标公告', 0) > 3
                   return '招标公告', msc
               elif '废标公告' in life_kw_title or life_score.get('废标公告', 0) > 5:
                   return '废标公告', msc
@@ -4381,8 +4388,7 @@ class DocChannel():
               return '合同公告', msc
 
           elif '中标信息' in life_kw_title or '中标信息' in life_list:
-              if '招标公告' in life_kw_title and life_score.get('招标公告',
-                                                            0) > 2:  # (life_score.get('招标公告', 0)>2 or life_score.get('中标信息', 0)<4) 0.7886409793924245
+              if '招标公告' in life_kw_title and '中标信息' not in life_kw_title and life_score.get('招标公告',0) >= life_score.get('中标信息',0):  # (life_score.get('招标公告', 0)>2 or life_score.get('中标信息', 0)<4) 0.7886409793924245
                   return '招标公告', msc
               elif '废标公告' in life_kw_title or life_score.get('废标公告', 0) > 5:
                   return '废标公告', msc
@@ -4504,8 +4510,7 @@ class DocChannel():
 
           if result['docchannel']['doctype'] in ['产权交易', '土地矿产', '拍卖出让'] and origin_dic.get(
                   original_docchannel, '') not in ['产权交易', '土地矿产', '拍卖出让'] \
-                and re.search('产权|转让|受让|招租|招商|出租|承租|资产|挂牌|出让|拍卖|招拍|划拨|销售', title) == None\
-                and re.search('(采购|招投?标|投标)(信息|内容|项目|公告|数量|人|单位|方式)|(建设|工程|服务|施工|监理|勘察|设计)项目|(%s)'%self.type_dic['采招数据'], text):
+                and (re.search(self.title_type_dic['采招数据'], title) or re.search('工程|服务|采购|询价|磋商', title) or re.search('(采购|招投?标|投标)(信息|内容|项目|公告|数量|人|单位|方式)|(建设|工程|服务|施工|监理|勘察|设计)项目|(%s)'%self.type_dic['采招数据'], text)):
               result['docchannel']['doctype'] = '采招数据'
               msc += ' 最终规则修改:预测为非采招数据,原始为采招数据且有招标关键词,返回采招数据'
           elif result['docchannel']['doctype'] in ['土地矿产'] and origin_dic.get(original_docchannel, '') in ['拍卖出让', '产权交易']:
@@ -4561,8 +4566,8 @@ class DocChannel():
           return {'docchannel': {'docchannel': '', 'doctype': not_extract_dic[original_docchannel], 'life_docchannel': origin_dic.get(original_docchannel, '原始类别')}}, '公告类别不在提取范围'
       if web_source_no in ['02104-7', '04733', 'DX007628-6']: # 这些数据源无法识别
           return {'docchannel': {'docchannel': '', 'doctype': '采招数据', 'life_docchannel': origin_dic.get(original_docchannel, '原始类别')}}, '此数据源公告分类不明确,返回数据源类别'
-      if original_docchannel == 303 or (re.search('处罚|投诉|失信', title) and re.search(self.title_type_dic['采招数据'], title)==None):
-          return {'docchannel': {'docchannel': '处罚公告', 'doctype': '处罚公告', 'life_docchannel': '处罚公告'}}, "处罚公告只判断标题和源类别"
+      if original_docchannel == 303:
+          return {'docchannel': {'docchannel': '处罚公告', 'doctype': '处罚公告', 'life_docchannel': '处罚公告'}}, "源类别为处罚公告"
 
       title = re.sub('[^\u4e00-\u9fa5]+|出租车', '', title)
       if len(title) > 50:
@@ -4582,9 +4587,9 @@ class DocChannel():
       # print('channel正则预测结果:', result)
       msc = '正则结果:类型:%s, 关键词:%s, 周期:%s, 关键词:%s'%(doc_type, type_kw,doc_life, life_kw)+'\n'+'模型结果:'
       # print('类型:%s, 关键词:%s, 周期:%s, 关键词:%s'%(doc_type, type_kw,doc_life, life_kw))
-      if doc_type == "" or doc_life == "":
+      if doc_type == "" or doc_life == "" or (doc_type != '采招数据' and origin_dic.get(original_docchannel, '原始类别') in ['招标公告', '中标信息', '招标预告', '采购意向']):
           array_content, array_title, text_len, title_len, content = get_model_inputs(list_sentence)
-          if doc_type =="":
+          if  doc_type =="" or (doc_type != '采招数据' and origin_dic.get(original_docchannel, '原始类别') in ['招标公告', '中标信息', '招标预告', '采购意向']):
               type_id, type_prob = type_model_predict()
               type_model = self.id2type[type_id]
               if type_model == '新闻资讯' and doc_life!='': # 修复bug 78584245 "docchannel": "合同公告", "doctype": "新闻资讯",
@@ -5305,8 +5310,8 @@ class IndustryPredictor():
         text = text.replace('(', '(').replace(')', ')')
         text = re.sub(
             '(废标|终止|综?合?评审|评标|开标|资审|履约|验收|成交|中标人?|中选人?|单一来源|合同|候选人|结果|变更|更正|答疑|澄清|意向|需求|采购|招标|询比?价|磋商|谈判|比选|比价|竞价|议价)的?(公告|预告|公示)?|关于为?|选取|定点|直接|邀请函?|通知书?|备案|公开|公示|公告|记录|竞争性',
-            '', text)
-        text = text.replace(tenderee, '')
+            ' ', text)
+        text = text.replace(tenderee, ' ')
         text = ' ' if text=="" else text
         words_docs_list = selffool.cut(text)
         words_docs_list = [[it for it in l if re.search('^[\u4e00-\u9fa5]+$', it)][-maxSententLen:] for l in words_docs_list]
@@ -5339,6 +5344,7 @@ class IndustryPredictor():
         product = product if product else ''
 
         text_ind = (doctitle + project_name + product).replace(tenderee, '')
+        text_ind = text_ind.replace('墙面粉刷', '墙面 粉刷')
         text_com = win_tenderer
 
         length_ind_text = len(text_ind) + 1
@@ -6788,7 +6794,8 @@ class CandidateExtractor(object):
             "third_tenderer": "第三名|第三(中标|成交)?候选人",
         }
         '''非表格候选人正则'''
-        self.p = '((候选|入围|入选|投标)(供应商库)?的?(人|人?单位|机构|供应商|供货商|服务商|投标人|(中标)?公司|(中标)?企业)|(通过)?名单)(名称|名单|全称|\d)?:$'
+        # self.p = '((候选|入围|入选|投标)(供应商库)?的?(人|人?单位|机构|供应商|供货商|服务商|投标人|(中标)?公司|(中标)?企业|应答人)|(通过)?名单)(名称|名单|全称|\d)?:$'
+        self.p = '((候选|入围|入选|投标|报价|成交|中标|中选|供[货应]|应答)(人|方|人?单位|机构|厂?商|商家|服务商|公司|企业)|(通过|入围)名单)(名称|名单|全称|\d)?:?$'
         self.tb = TableTag2List()
         with open(os.path.dirname(__file__)+'/header_set.pkl', 'rb') as f:
             self.headerset = pickle.load(f)
@@ -6927,7 +6934,7 @@ class CandidateExtractor(object):
             package = uniform_package_name(package) if package !="" else "Project"
             if candidate:
                 if win_or_not and re.search('否|未入围', win_or_not):
-                    pass
+                    candidate_set.add(candidate)
                 elif re.search('^((建议|推荐)(中标|成交)|是)$', win_or_not) and win_sort in ['', '参与投标单位及排名'] and win_tenderer=='':
                     win_sort = '第一名'
                     candidate_set.add(candidate)
@@ -6945,8 +6952,7 @@ class CandidateExtractor(object):
                             if type not in role_dic:
                                 role_dic[type] = dict()
                             role_dic[type]['role_text'] = text
-                            if type in ['second_tenderer', 'third_tenderer']:
-                                candidate_set.add(text)
+                            candidate_set.add(text)
 
                 elif re.search('投标报价|报价$', df.loc[i, 0]) or re.search('投标报价|报价$', df.loc[i, 1]):
                     findmoney = True
@@ -7103,7 +7109,7 @@ class CandidateExtractor(object):
                 e = ent.wordOffset_end
                 if ent.label in [2,3,4]: # 直接加实体预测的候选人, 否则规则检查是否为候选人
                     candidates.add(ent.entity_text)
-                elif isinstance(b, int) and isinstance(e, int):
+                elif isinstance(b, int) and isinstance(e, int) and ent.label in [5]:
                     foreword = text[max(0, b - 10):b]
                     if re.search(self.p, foreword):
                         candidates.add(ent.entity_text)
@@ -7126,8 +7132,8 @@ class CandidateExtractor(object):
         if prem == {} and richText:
             prem, candidate_set = self.get_prem(richText)
             in_attachment = True
-        if prem == {} and candidate_set == set():
-            candidate_set = self.get_candidates_from_text(list_sentences, list_entitys)
+        candidate_set2 = self.get_candidates_from_text(list_sentences, list_entitys)
+        candidate_set.update(candidate_set2)
         return prem, {'candidate': ','.join(candidate_set)}, in_attachment
 
 def role_special_predictor(web_source_name, content, nlp_enterprise):