| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- # -*- coding: utf-8 -*-
- """
- Phase 6 migration from interface/getAttributes.py.
- This module groups the post-processing helpers that refine the assembled
- ``prem`` structure (package-role-money) after the initial extraction:
- correcting role money against table totals, limiting amounts by industry
- thresholds, merging table-extracted prem into the existing one, rule-based
- role supplementation, conflict confirmation, package naming, single-source
- fixups, demand-driven prem expansion and candidate manager enrichment.
- Migrated functions:
- getPREMs, correct_rolemoney, limit_maximum_amount, update_prem,
- rule_add_role, confirm_prem, add_package_name, fix_single_source,
- demand_to_prem, add_manager
- """
- from __future__ import absolute_import
- import re
- import copy
- import json
- import uuid
- from decimal import Decimal
- from BiddingKG.dl.common.Utils import log, money_process, get_money_entity, getCurrent_date
- def getPREMs(list_sentences,list_entitys,list_articles,list_outlines,page_time,winter_scope):
- '''
- @param:
- list_sentence:所有文章的句子list
- list_entity:所有文章的实体list
- @return:list of dict which include文章的包-角色-实体名称-金额-联系人-联系电话
- '''
- from BiddingKG.dl.assembly.role_money import getPackageRoleMoney
- from BiddingKG.dl.assembly.time_attrs import getTimeAttributes
- from BiddingKG.dl.assembly.contacts import getProjectContacts
- result = []
- for list_sentence,list_entity,list_article,list_outline in zip(list_sentences,list_entitys,list_articles,list_outlines):
- RoleList = getPackageRoleMoney(list_sentence,list_entity,list_outline,winter_scope)
- result.append(dict({"prem": RoleList, "docid": list_article.doc_id},
- **getTimeAttributes(list_entity, list_sentence,page_time),
- **getProjectContacts(list_entity, list_sentence),
- **{"fingerprint": list_article.fingerprint,
- "match_enterprise": list_article.match_enterprise,
- "match_enterprise_type": list_article.match_enterprise_type,
- "process_time": getCurrent_date(),
- "attachmentTypes": list_article.attachmentTypes, "bidway": list_article.bidway}))
- # result.append(dict({"prem":RoleList,"docid":list_article.doc_id},**getOtherAttributes(list_entity),**getTimeAttributes(list_entity,list_sentence),
- # **{"fingerprint":list_article.fingerprint,"match_enterprise":list_article.match_enterprise,
- # "match_enterprise_type":list_article.match_enterprise_type,"process_time":getCurrent_date(),
- # "attachmentTypes":list_article.attachmentTypes, "bidway": list_article.bidway}))
- return result
- 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:
- '''
- if '##attachment##' in list_articles[0].content:
- content, attachment = list_articles[0].content.split('##attachment##')
- if len(content) < 200:
- 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<2000000:
- for value in prem[0]['prem'].values():
- ree_money = float(value['tendereeMoney'])
- for l in value['roleList']:
- try:
- # if l[0] == 'win_tenderer' and float(l[2])<total_product_money:
- # 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'])*2<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'] = round(total_product_money, 2)
- log('修改中标金额为产品总金额: %s, docid:%s' % (round(total_product_money, 2), docid))
- except Exception as e:
- print('表格产品价格修正中标价格报错:%s'%e)
- elif (len(re.findall('合计', content)) == 1 or len(re.findall('总价', content)) == 1):
- ser = re.search('(?P<header>合计((万?元))?:)(?P<money>[\d,.]+(万?元)?)', content) if len(re.findall('合计', content)) == 1 else re.search('(?P<header>总价((万?元))?:)(?P<money>[\d,.]+(万?元)?)', content)
- if ser:
- money_text = ser.group('money')
- header = ser.group('header')
- money, money_unit = money_process(money_text, header)
- if 100<money<8000000:
- for value in prem[0]['prem'].values():
- for l in value['roleList']:
- try: # 如果原中标金额为0 或 金额小于合计金额0.1倍且正文没中标金额关键词 替换为 合计金额
- 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
- log('修改中标金额为总价或合计金额: %s, docid:%s'%(money, docid))
- except Exception as e:
- print('修正中标价格报错:%s' % e)
- if 100 < 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"] = round(total_budget, 2)
- log('修改招标金额为表格合计预算: %s, docid:%s' % (round(total_budget, 2), docid))
- def limit_maximum_amount(dic, list_entity):
- '''
- 通过关键词、行业、公告类别等设置最高最低角色金额
- :param dic: 最终返回所有字段结果字典
- :param list_entity: 实体列表
- :return:
- '''
- indu_amount = {
- '计算机设备': 200000000,
- '办公设备': 100000000,
- '家具用具': 500000000,
- '办公消耗用品及类似物品': 100000000,
- '日杂用品': 100000000,
- '餐饮业': 1000000000,
- '物业管理': 1000000000,
- '工程技术与设计服务': 1000000000,
- '工程评价服务': 100000000,
- '其他工程服务': 100000000,
- '工程监理服务': 100000000,
- '工程造价服务': 100000000,
- '会计、审计及税务服务': 100000000,
- '其他专业咨询与调查': 100000000
- }
- title = dic.get('doctitle_refine', '')
- name = dic.get('name', '')
- product = ','.join(dic.get('product', []))
- text = "%s;%s;%s"%(title, name, product)
- doctype = dic.get('docchannel', {}).get('doctype', '') # 公告类型
- industry = dic['industry'].get('class_name', '')
- category = dic['industry'].get('class', '') # 行业门类
- moneys = [float(it.entity_text) for it in list_entity if it.entity_type=='money' and re.search('^\d+(\.\d+)?', it.entity_text) and 5000<float(it.entity_text)<5000000]
- maximum_amount = 10000000000
- minximum_amount = 100
- if re.search('监理|造价咨询|设计|勘察|招标代理中介服务|工程审计', text) and re.search('施工|总承包|ppp|PPP', text.replace('施工监理', '监理'))==None:
- # print('监理设计等限额')
- maximum_amount = 1000000000
- minximum_amount = 200
- elif re.search('施工|总承包|ppp|PPP|公路|道路|桥梁|铁路|土地使用权|地块|棚改|征地拆迁|棚户区改造|土地征收|建设用地|社会保险', text) or category in ['金融业', '建筑业'] or doctype == '土地矿产':
- # print('施工、铁路等限额')
- if industry in ['科研、医疗、教育用房', '住宅、商业用房', '场馆、站港用房','工业、生产用房','专业施工']:
- maximum_amount = 20000000000
- minximum_amount = 200
- elif industry in ['修缮工程', '电气安装', '管道和设备安装', '建筑装饰和装修业', '建筑物拆除和场地准备活动']:
- maximum_amount = 10000000000
- minximum_amount = 100
- else:
- maximum_amount = 50000000000
- minximum_amount = 500
- elif re.search('(办公|体育)(用品|设备|器材)|耗材|打印机|复印机|打印纸|粉盒|墨粉|复印纸|网上超市|电子卖场|家电|配电箱采购|配件|备件', text):
- # print('商品采购限额')
- maximum_amount = 80000000
- minximum_amount = 10
- elif re.search('修理|维修|(安保|保安|安全|保洁|物业|后勤|管理|代理|中介|印刷)服务', text):
- # print('维修限额')
- maximum_amount = 50000000
- elif re.search('(速递|快递|邮政|邮寄)(物流)?服务', text):
- # print('快递限额')
- maximum_amount = 80000000
- minximum_amount = 10
- elif industry in indu_amount:
- maximum_amount = indu_amount[industry]
- # print('maximum_amount:', maximum_amount)
- for value in dic['prem'].values():
- for l in value['roleList']:
- if l["role_name"] in ['win_tenderer', 'second_tenderer', 'third_tenderer']:
- # date = float(re.search('(\d+)天', l.get('serviceTime', '')).group(1)) if re.search('(\d+)天', l.get('serviceTime', '')) else 0
- serviceTime_dict = l.get('serviceTime', dict())
- serviceTime_dict = serviceTime_dict if serviceTime_dict else dict()
- date = serviceTime_dict.get("service_days",0)
- if 0 < date < 180 and float(l["role_money"]['money']) > 10000000000: # 工期小于180天且金额大于百亿的,错误
- l["role_money"]['money'] = str(Decimal(l["role_money"]['money']) / 10000)
- # print('工期纠正百亿以上金额 ')
- elif float(l["role_money"]['money']) > maximum_amount:
- flag = 1
- for money in moneys:
- if float(l["role_money"]['money'])/money == 10000 and l['role_money']['money_unit'] == '万元':
- l["role_money"]['money'] = str(Decimal(l["role_money"]['money']) / 10000)
- # print('万倍关系纠正连接金额')
- flag = 0
- break
- if flag and l["role_money"]['money_unit'] == '万元' or re.search('^\d{11,}(\.0)?$', str(l["role_money"]['money'])):
- l["role_money"]['money'] = str(Decimal(l["role_money"]['money']) / 10000)
- # print('行业限额纠正连接金额')
- elif industry in ['餐饮业', '物业管理'] and maximum_amount == indu_amount[industry]:
- l["role_money"]['money'] = str(Decimal(l["role_money"]['money']) / 10000)
- # elif flag and l["role_money"]['money_unit'] == '元':
- # l["role_money"]['money'] = 0
- elif 0<float(l["role_money"]['money']) < minximum_amount:
- if l["role_money"]['money_unit'] == '元' and re.search('^\d{1,2}\.\d{4,6}$', str(l["role_money"]['money'])):
- # print('单位元小金额且格式类似万元的乘以万倍')
- l["role_money"]['money'] = str(Decimal(l["role_money"]['money']) * 10000)
- # else: # 20241011 取消小于最低金额改为0 避免小金额不提取 例:520248605
- # # print('中标金额小于限额:%d元 去除' % minximum_amount)
- # l["role_money"]['money'] = 0
- if float(value['tendereeMoney']) > maximum_amount:
- flag = 1
- for money in moneys:
- if float(value['tendereeMoney'])/money == 10000 and l['role_money']['money_unit'] == '万元':
- value['tendereeMoney'] = str(Decimal(value['tendereeMoney'])/10000)
- # print('万倍关系纠正连接金额')
- flag = 0
- break
- if (flag and value['tendereeMoneyUnit'] == '万元' or re.search('^\d{11,}(\.0)?$', str(value['tendereeMoney']))) and float(value['tendereeMoney']) > maximum_amount*100: #2024/5/23 改为单位万元且超过限额100倍才除一万,避免不合理纠正 比如 174255856 项目(系统)一亿变一万
- value['tendereeMoney'] = str(Decimal(value['tendereeMoney']) / 10000)
- # print('行业限额纠正连接金额')
- elif industry in ['餐饮业', '物业管理'] and maximum_amount == indu_amount[industry]:
- value['tendereeMoney'] = str(Decimal(value['tendereeMoney']) / 10000)
- # elif flag and value['tendereeMoneyUnit'] == '元':
- # value['tendereeMoney'] = 0
- elif 0<float(value['tendereeMoney']) < minximum_amount:
- if value['tendereeMoneyUnit'] == '元' and re.search('^\d{1,2}\.\d{4,6}$', str(value['tendereeMoney'])):
- # print('单位元小金额且格式类似万元的乘以万倍')
- value['tendereeMoney'] = str(Decimal(value['tendereeMoney']) * 10000)
- # else: # 20241011 取消小于最低金额改为0 避免小金额不提取 例:520248605
- # # print('招标金额小于限额:%d元 去除' % minximum_amount)
- # value['tendereeMoney'] = 0
- def update_prem(old_prem, new_prem, in_attachment=False):
- '''
- 根据新旧对比,更新数据
- :param old_prem:
- :param new_prem: 表格提取的要素
- :return:
- '''
- if len(new_prem) >= 1 :
- '''如果表格提取的包大于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':
- del_k.append(k)
- for k in del_k:
- old_prem.pop(k)
- if len(old_prem) > len(new_prem) and len(new_prem)>=1 and in_attachment==False: # 如果表格有提取,非表格包数比表格提取多,去掉非表格在附件里提取的包
- del_k = []
- for k in old_prem:
- if 'in_attachment' in old_prem[k] and old_prem[k]['in_attachment'] and k not in new_prem and k != 'Project':
- del_k.append(k)
- for k in del_k:
- old_prem.pop(k)
- if in_attachment and len(new_prem) < len(old_prem)*2: # 附件表格提取的,原来提取有中标人,停止替换
- for v in old_prem.values():
- for d in v['roleList']:
- if d['role_name'] in ['win_tenderer', 'pre_win_tenderer']:
- return 0
- # if len(new_prem) > len(old_prem) and [k for k in new_prem if '自增' not in k] == []: # 如果表格提取包号都为自增编号且包数大于非表格提取,不进行更新 例 244355092 281854766
- # return None
- if len(old_prem) >= 2 and len(new_prem) == 1 and ('Project' in new_prem or set(new_prem)&set(old_prem)==set()): # 如果表格提取包为Project,非表格提取两个包且一个包为Project,把表格提取合并到非Project包
- # k = list(old_prem.keys()-set(['Project']))[0]
- k = sorted((old_prem.keys()-set(['Project'])), key=lambda x: x)[0]
- k_new = list(new_prem.keys())[0]
- new_prem[k] = new_prem.pop(k_new)
- elif len(old_prem) == 1 and len(new_prem) == 1 and 'Project' not in old_prem and set(new_prem)&set(old_prem)==set(): # 如果表格提取包与非表格提取都是一个包且不同,把表格提取包名替换为非表格包名
- k = list(old_prem.keys()-set(['Project']))[0]
- k_new = list(new_prem.keys())[0]
- new_prem[k] = new_prem.pop(k_new)
- if len(new_prem) == len(old_prem) == 1 and 'Project' not in new_prem and 'Project' in old_prem: # 如果表格提取到包号,非表格没提取到,合并到Project
- k = list(new_prem.keys())[0]
- new_prem['Project'] = new_prem.pop(k)
- multi_tendereeMoney = [] # 多包招标金额
- for k, v in new_prem.items():
- if k == 'Project':
- if 'Project' in old_prem:
- tmp_l = [] # 保存新旧同时包含的角色
- if v.get('code', "") != "":
- old_prem['Project']['code'] = v.get('code', "")
- if v.get('name', "") != "":
- old_prem['Project']['name'] = v.get('name', "")
- for d in old_prem['Project']['roleList']:
- for d2 in v['roleList']:
- if d['role_name'] == d2['role_name']: # 同时包含的角色用表格的替换
- tmp_l.append(d2)
- if d2['role_text'] != "":
- d['role_text'] = d2['role_text']
- if d2.get('serviceTime', '') != "":
- d['serviceTime'] = d2['serviceTime']
- if float(d2['role_money']['money']) != 0: # 如果表格提取的金额不为0才替换
- d['role_money']['money'] = d2['role_money']['money']
- d['role_money']['money_unit'] = d2['role_money']['money_unit']
- for k in set(d2)-set(d): # 把表格提取加的属性补充过来,比如:multi_winner other_winner_dic等
- if d2[k]:
- d[k] = d2[k]
- for d2 in v['roleList']:
- if d2 not in tmp_l: # 把新预测有,旧没有的角色添加上去
- old_prem['Project']['roleList'].append(d2)
- if float(old_prem['Project'].get('tendereeMoney', 0)) < float(new_prem['Project'].get('tendereeMoney', 0)) < 100000000:
- old_prem['Project']['tendereeMoney'] = new_prem['Project']['tendereeMoney'] # 20240508 修复 464187225 表格提取纠正招标金额错误
- else:
- old_prem[k] = v
- else:
- if v['tendereeMoney'] != 0:
- multi_tendereeMoney.append(v['tendereeMoney'])
- if k.startswith('自增'): # 表格提取的没找到包号 按行数添加包号,前面加自增,例 自增1
- k = k[2:]
- if k not in old_prem: # 新有旧没有的包直接添加
- old_prem[k] = v
- else:
- tmp_l = [] # 保存新旧同时包含的角色
- if v.get('code', "") != "":
- old_prem[k]['code'] = v.get('code', "")
- if v.get('name', "") != "":
- old_prem[k]['name'] = v.get('name', "")
- for d in old_prem[k]['roleList']:
- for d2 in v['roleList']:
- if d['role_name'] == d2['role_name']:
- tmp_l.append(d2)
- if d2['role_text'] != "":
- d['role_text'] = d2['role_text']
- if d2.get('serviceTime', '') != "":
- d['serviceTime'] = d2['serviceTime']
- if float(d2['role_money']['money']) != 0: # 如果表格提取的金额不为0才替换
- d['role_money']['money'] = d2['role_money']['money']
- d['role_money']['money_unit'] = d2['role_money']['money_unit']
- for k2 in set(d2)-set(d): # 把表格提取加的属性补充过来,比如:multi_winner other_winner_dic等
- if d2[k2]:
- d[k2] = d2[k2]
- for d2 in v['roleList']:
- if d2 not in tmp_l: # 把新预测有,旧没有的角色添加上去
- old_prem[k]['roleList'].append(d2)
- if float(old_prem[k].get('tendereeMoney', 0)) < float(v.get('tendereeMoney', 0)) < 100000000:
- old_prem[k]['tendereeMoney'] = v['tendereeMoney'] # 2024/05/24 使用表格招标金额
- if multi_tendereeMoney and 'Project' in old_prem and float(old_prem['Project']['tendereeMoney'])!=0: # 表格提取到多标段招标金额,去掉Project包招标金额
- old_prem['Project']['tendereeMoney'] = 0
- tenderee_l = [d2['role_text'] for v in old_prem.values() for d2 in v['roleList'] if d2['role_name']=='tenderee']
- winner_l = [d2['role_text'] for v in old_prem.values() for d2 in v['roleList'] if d2['role_name']=='win_tenderer']
- if set(tenderee_l) & set(winner_l): # 删除与中标人冲突的招标人
- for k in old_prem:
- old_prem[k]['roleList'] = [d for d in old_prem[k]['roleList'] if
- not(d['role_name'] == 'tenderee' and d['role_text'] in winner_l)]
- # print('删除与中标人冲突的招标人')
- # return old_prem
- def rule_add_role(docid, prem, channel, content, web_source_no, nlp_enterprise):
- def add_role(ent_name, role_type, prem, money=0):
- if 'Project' in prem:
- prem['Project']['roleList'].append(
- {
- "address": "",
- "linklist": [],
- "role_money": {
- "discount_ratio": "",
- "downward_floating_ratio": "",
- "floating_ratio": "",
- "money": money,
- "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": money,
- "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'] in ['招标公告', '中标信息'] and re.search('"role_name": "tenderee"',json.dumps(prem)) == None:
- match = re.search('(招标|采购|招商)(人|方|商|单位|部门)(信息[,:]?)?(名称)?((甲方))?:(?P<name>[\w()—-]{4,35})([,。]|$)', content)
- match2 = re.search('(招标|采购|招商)(人|方|商|单位|部门)(信息[,:]?)?(名称)?((甲方))?:(?P<name>[\w()—-、]{25,55}(局|人民政府|委员会|执法队))?)([,。]|$)', 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 match2:
- ent_name = match2.group('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)
- if 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)
- b_idx = match.end()
- idx_ = content[b_idx:].find('。')
- e_idx = idx_ if idx_ > 0 else 200
- text = content[b_idx:b_idx+e_idx]
- money_list, _ = get_money_entity(text)
- money = 0
- for money_tup in money_list:
- entity_text, b, e, unit, notes = money_tup
- if re.search('(中标|成交|中选|投标|合同)总?(价格?|金额)', text[max(0, b-10):b]):
- money = float(entity_text)
- break
- log('规则补充中标人角色:%s, 金额:%s,docid:%s'%(ent_name, money, docid))
- add_role(ent_name, "win_tenderer", prem, money)
- def confirm_prem(docid, prem, channel_dic, content, is_deposit_project=False, total_tendereeMoney=0, all_tenderer=set()):
- '''
- 规则检查纠正prem,如果Project包中标人在其他包中标人,去掉project包中标角色;如果有其他包中标人,去掉roleList为空的包;
- :param prem: prem 字段字典
- :return:
- '''
- if len(prem) > 1: # 表格提取到中标人的,去掉project包中标人
- pro_winner = set()
- other_winner = set()
- other_winner_prob = 0
- pro_winner_prob = 0
- empty_roleList = []
- for k in prem:
- prem[k]['uuid'] = str(uuid.uuid4()) # 20240627 每个包都添加uuid
- if prem[k]['roleList'] == []:
- empty_roleList.append(k)
- for d in prem[k]['roleList']:
- if d['role_name'] in ['win_tenderer', 'pre_win_tenderer', 'second_tenderer','third_tenderer']:
- if k == 'Project':
- pro_winner.add(d['role_text'])
- if 'win_tenderer_joint' in d:
- pro_winner.update(set(d['win_tenderer_joint'].split(',')))
- if 'multi_winner' in d:
- pro_winner.update(set(d['multi_winner'].split(',')))
- if d['role_name'] == 'win_tenderer' and d.get('role_prob', 0)>0.6:
- pro_winner_prob = d.get('role_prob', 0)
- else:
- other_winner.add(d['role_text'])
- if 'win_tenderer_joint' in d:
- other_winner.update(set(d['win_tenderer_joint'].split(',')))
- if 'multi_winner' in d:
- other_winner.update(set(d['multi_winner'].split(',')))
- if d['role_name'] == 'win_tenderer' and d.get('role_prob', 0)>0.6:
- other_winner_prob = d.get('role_prob', 0)
- if pro_winner!=set() and (pro_winner & other_winner != set() or other_winner_prob>pro_winner_prob): # 如果默认包与其他包中标人重复或其他包中标人概率比默认包大,删除默认包中标人
- prem['Project']['roleList'] = [d for d in prem['Project']['roleList'] if
- d['role_name'] not in ['win_tenderer', 'second_tenderer',
- 'third_tenderer']]
- elif other_winner_prob<pro_winner_prob and len(prem)==2: # 两个包情况,如果默认包中标人概率比其他包大,删除其他包
- rm_k = [k for k in prem if k != 'Project']
- for k in rm_k:
- prem.pop(k)
- if other_winner and channel_dic['docchannel']['docchannel'] in ['中标信息', '候选人公示', '合同公告']:
- for k in empty_roleList:
- prem.pop(k)
- elif "Project" in prem:
- prem['Project']['uuid'] = str(uuid.uuid4())
- if len(prem) == 2:
- del_k = [k for k,v in prem.items() if v.get('roleList', [])==[] and v.get('tendereeMoney', 0)==0 and v.get('unit_tendereeMoney', 0)==0] # 20250310 删除掉没有角色且招标金额为0 的包
- for k in del_k:
- prem.pop(k)
- # print('删除掉没有角色且招标金额为0 的包', k)
- if is_deposit_project and float(total_tendereeMoney)!=0 and len(prem)==1: #20241107 存款类项目有总投资没招标金额且只有一个标段,把总投资作招标金额
- for k in prem:
- if float(prem[k]['tendereeMoney'])==0:
- prem[k]['tendereeMoney'] = total_tendereeMoney
- # 采购人:某单位 等去掉tenderee
- if "Project" in prem and re.search('(招标|采购|招商)(人|商|单位|部门)(信息[,:]?)?(名称)?((甲方))?:[\u4e00-\u9fa5]{,3}某+(单位|部)', content):
- for d in prem['Project']['roleList']:
- if d['role_name'] == 'tenderee' and d.get('role_prob', 0)<0.7: # 581195772 小于0.7才删除,附件明确表达可能0.7多
- log('规则去除文中包含“采购人:某单位”等概率小于0.7的招标人:%s,docid:%s'%(d.get('role_text', ''), docid))
- prem['Project']['roleList'].remove(d)
- if "Project" in prem and re.search('(招标|采购|招商)(人|商|单位|部门)(信息[,:]?)?(名称)?((甲方)?)?:zycgr\d+', content): # 修复 中国政府采购网 不公布具体名称 例:716907959
- for d in prem['Project']['roleList']:
- if d['role_name'] == 'tenderee' and d.get('role_prob', 0)<0.7: # 581195772 小于0.7才删除,附件明确表达可能0.7多
- log('规则去除文中包含“采购人:zycgr”等概率小于0.7的招标人:%s,docid:%s'%(d.get('role_text', ''), docid))
- prem['Project']['roleList'].remove(d)
- if len(prem) > 10:
- log('prem包数量大于10,当前包数量:%d, docid:%s'%(len(prem), docid))
- # 验证中标人招标人是否冲突
- tenderee = ""
- tenderee_prob = 0.5
- win_channel = True if channel_dic['docchannel']['docchannel'] in ['中标信息', '候选人公示', '合同公告'] else False
- win_tenderer_set = set()
- tenderee_or_agency = set()
- agency = ''
- for k, v in prem.items():
- win_money = 0
- for d in v.get('roleList', []):
- if d.get('role_name', '') == 'tenderee' and k == 'Project':
- tenderee = d['role_text']
- tenderee_prob = d.get('role_prob', 0.5)
- elif d.get('role_name', '') == 'agency' and k == 'Project':
- agency = d['role_text']
- elif d.get('role_name', '') in ['win_tenderer', 'pre_win_tenderer', 'second_tenderer', 'third_tenderer']:
- win_tenderer_set.add(d['role_text'])
- if d.get('role_name', '') in ['win_tenderer', 'pre_win_tenderer'] and float(d.get('role_money', {}).get('money', 0)):
- win_money = float(d.get('role_money', {}).get('money', 0))
- if d.get('role_name') in ['tenderee', 'agency']:
- tenderee_or_agency.add(d['role_text'])
- elif d.get('role_name') in ['win_tenderer', 'pre_win_tenderer', 'second_tenderer', 'third_tenderer']:
- if d['role_text'] not in all_tenderer:
- all_tenderer.add(d['role_text'])
- if 'multi_winner' in d:
- for item in d['multi_winner'].split(','):
- if item not in all_tenderer:
- all_tenderer.add(item)
- if 'win_tenderer_joint' in d:
- for item in d['win_tenderer_joint'].split(','):
- if item not in all_tenderer:
- all_tenderer.add(item)
- # 招标金额小于中标金额的, 去掉招标金额
- if win_money and 0 < float(v.get('tendereeMoney', 0))*1.1 < win_money:
- log('招标金额比中标金额小,去除招标金额:%d, %d ,docid:%s'%(float(v.get('tendereeMoney', 0)), win_money, docid))
- v['tendereeMoney'] = 0
- # 招标单位和代理机构在同一个公告里是一致的,就只保留招标单位
- if tenderee and tenderee == agency:
- prem['Project']['roleList'] = [it for it in prem['Project']['roleList'] if it.get('role_name', '') != 'agency']
- if tenderee != '' and len(win_tenderer_set) > 0 and (tenderee in ' '.join(win_tenderer_set) or [it for it in win_tenderer_set if it in tenderee]):
- conflict_short = [it for it in win_tenderer_set if it in tenderee] # 名称重叠冲突角色
- conflict_long = [it for it in win_tenderer_set if tenderee in it] # 名称重叠冲突角色 解决 751037129 采购人:贵州盘江精煤股份有限公司,承办单位:贵州盘江精煤股份有限公司物资管理分公司,
- remove_packages = [] # 待删除空包
- for k, v in prem.items():
- if v.get('roleList', []):
- if tenderee_prob >= 0.8 or not win_channel:
- v['roleList'] = [it for it in v['roleList'] if it.get('role_text', '') not in conflict_short + conflict_long]
- log('去掉冲突角色的中标角色, 中标人:%s, 招标人:%s, docid:%s'%(' '.join(conflict_short + conflict_long), tenderee, docid))
- elif conflict_short:
- v['roleList'] = [it for it in v['roleList'] if it.get('role_text', '') not in conflict_short]
- log('去掉冲突角色的中标角色, 中标人:%s, 招标人:%s, docid:%s' % (' '.join(conflict_short), tenderee, docid))
- elif conflict_long:
- v['roleList'] = [it for it in v['roleList'] if it.get('role_name', '')!='tenderee']
- log('去掉冲突角色的招标角色, 中标人:%s, 招标人:%s, docid:%s' % (' '.join(conflict_long), tenderee, docid))
- else:
- v['roleList'] = [it for it in v['roleList'] if
- it.get('role_text', '') not in conflict_short + conflict_long and it.get('role_name', '')!='tenderee']
- log('去掉冲突角色的招标中标角色, 中标人:%s, 招标人:%s, docid:%s' % (
- ' '.join(conflict_short + conflict_long), tenderee, docid))
- # if re.search('政府|医院|学院|学校|中学|小学|大学|中心|幼儿园|保健院|党校|研究院|血站|分校|银行|红十字会|防治院|研究所', tenderee):
- # v['roleList'] = [it for it in v['roleList'] if it.get('role_name', '')=='tenderee' and it.get('role_prob', 1) > 0.6 or (
- # it.get('role_name', '')!='tenderee' and tenderee not in it.get('role_text', '') and it.get('role_text', '') not in tenderee)]
- # log('去掉招标中标冲突的中标角色,招标人:%s, docid:%s'%(tenderee, docid))
- # else:
- # v['roleList'] = [it for it in v['roleList'] if it.get('role_name', '') != 'tenderee' or (
- # it.get('role_name', '') == 'tenderee' and tenderee not in it.get('role_text', '') and it.get('role_text', '') not in tenderee)]
- # log('去掉招标中标冲突的招标角色,招标人:%s, docid:%s' % (tenderee, docid))
- if not v.get('roleList', []) and not v.get("tendereeMoney", 0) and not v.get('unit_tendereeMoney', 0):
- remove_packages.append(k)
- for k in remove_packages:
- prem.pop(k)
- return {'candidate': ','.join(all_tenderer-tenderee_or_agency)}
- def add_package_name(prem, list_entity, product_list, name):
- '''
- 通过产品、项目名称,补充各标段包名,如果标段无包名,标段后紧接产品,把产品作为包名;如果标段数少于等于2且包名为空,补充项目名称为包名
- :param prem:
- :param list_entity:
- :param product_list:
- :param name:
- :return:
- '''
- if len(prem)>2 and len(product_list)>2:
- ent_l = []
- for entity in list_entity:
- if entity.entity_type in ['product', 'package']:
- ent_l.append((entity.entity_type, entity.entity_text, entity.sentence_index, entity.wordOffset_begin, entity.wordOffset_end, entity.in_attachment))
- ent_l.sort(key=lambda x: [x[2],x[3]])
- i = 0
- pk_dic = {}
- while i < len(ent_l)-1:
- ty1, ent1, s1, b1, e1, in_att1 = ent_l[i]
- ty2, ent2, s2, b2, e2, in_att2 = ent_l[i+1]
- if in_att1 == in_att2 and ty1 == 'package' and ty2 == 'product' and s1 == s2 and 0<b2-e1<3:
- pk_dic[ent1] = ent2
- i += 1
- if len(pk_dic) > 1:
- for k, v in prem.items():
- if k in pk_dic and v.get('name', '') == '':
- v['name'] = pk_dic[k]
- elif name != '' and len(prem)<=2: # 20241129 小于等于两个包且无包名称,取项目名称
- for k in prem:
- if prem[k].get('name', '') == '':
- prem[k]['name'] = name
- def fix_single_source(prem, channel_dic, original_docchannel):
- if prem.get('bidway', '') == '单一来源' and channel_dic['docchannel']['docchannel'] == '招标公告' and original_docchannel==52:
- for l in prem['prem'].values():
- for d in l['roleList']:
- if d['role_name'] == "win_tenderer":
- d['role_name'] = 'pre_win_tenderer'
- def demand_to_prem(demand, prem):
- if len(demand.get('data', [])) > len(prem):
- i = 1
- for d in demand.get('data', []):
- d['demand_id'] = i
- if d.get('project_name', '') != '' and d.get('budget', '') != '':
- if d.get('project_name', '') not in prem:
- prem[d.get('project_name', '')] = {
- 'demand_id': i,
- 'code': '',
- 'name': d.get('project_name', ''),
- 'roleList': [],
- 'tendereeMoney': d.get('budget', ''),
- 'tendereeMoneyUnit': ""
- }
- else:
- prem[d.get('project_name', '')+'_%d'%i] = {
- 'demand_id': i,
- 'code': '',
- 'name': d.get('project_name', ''),
- 'roleList': [],
- 'tendereeMoney': d.get('budget', ''),
- 'tendereeMoneyUnit': ""
- }
- i += 1
- def add_manager(docid, prem, list_entitys, list_sentences):
- '''
- 补充候选人的 项目负责人、经理
- :param prem:
- :param list_entitys:
- :param list_sentences:
- :return:
- '''
- if 'win_tenderer' in str(prem): # 有中标人才做下面操作
- managers = set() # 项目经理,项目负责人
- roles = set() # 候选人
- ent_list = [] # 实体列表
- role_person_dic = {}
- for entity in list_entitys[0]:
- if entity.entity_type == 'person':
- b = entity.wordOffset_begin
- s_index = entity.sentence_index
- sentance_text = list_sentences[0][s_index].sentence_text
- if re.search('(项目负责人|项目总工)([:,]?姓名)?:$', sentance_text[max(0, b - 12):b]):
- managers.add(entity.entity_text)
- ent_list.append(('person', s_index, b, entity.entity_text, 'leader'))
- elif re.search('(项目经理)([:,]?姓名)?:$', sentance_text[max(0, b - 12):b]):
- managers.add(entity.entity_text)
- ent_list.append(('person', s_index, b, entity.entity_text, 'manager'))
- elif entity.entity_type in ['org', 'company']:
- b = entity.wordOffset_begin
- s_index = entity.sentence_index
- lb = entity.label
- if lb in [2, 3, 4]:
- roles.add(entity.entity_text)
- ent_list.append(('org', s_index, b, entity.entity_text, lb))
- ent_list.sort(key = lambda x: [x[1], x[2]])
- if len(roles) > 0 and len(managers) > 0:
- for i in range(len(ent_list)):
- ent = ent_list[i]
- if ent[0] == 'org':
- for j in range(i + 1, len(ent_list)):
- ent2 = ent_list[j]
- if ent2[0] == 'person' and 0 <= ent2[1] - ent[1] < 4:
- if ent[3] not in role_person_dic:
- role_person_dic[ent[3]] = []
- role_person_dic[ent[3]].append(ent2)
- elif ent2[0] == 'org':
- break
- if role_person_dic:
- for v in prem.values():
- for d in v['roleList']:
- if d['role_name'] in ['win_tenderer', 'pre_win_tenderer', 'second_tenderer', 'third_tenderer']:
- ent = ""
- if d['role_text'] in role_person_dic:
- ent = d['role_text']
- elif'win_tenderer_joint' in d:
- for name in d['win_tenderer_joint'].split(','):
- if name in role_person_dic:
- ent = name
- if ent != "":
- for per in role_person_dic[ent]:
- if per[4] not in d:
- d[per[4]] = per[3]
- log('规则补充候选人项目经理:%s, docid:%s'%(per[3], docid))
- __all__ = ["getPREMs", "correct_rolemoney", "limit_maximum_amount", "update_prem", "rule_add_role", "confirm_prem", "add_package_name", "fix_single_source", "demand_to_prem", "add_manager"]
|