joint.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. # -*- coding: utf-8 -*-
  2. """联合体与多中标人金额装配(Phase 6 迁移)。
  3. 本模块由 ``interface/getAttributes.py`` 第 4804-5006 行迁移而来,负责:
  4. - ``get_win_joint``:从正文与实体列表中识别联合体信息并写入 prem
  5. - ``get_multi_winner_and_money``:装配多中标人(multi_winner)并收集正文/附件金额
  6. 迁移说明:原文件使用 ``from BiddingKG.dl.common.Utils import *``,本模块改为显式导入。
  7. 经核查,两个函数仅依赖标准库 ``re``,不依赖 common.Utils 中的任何名称。
  8. """
  9. from __future__ import absolute_import
  10. import re
  11. __all__ = ["get_win_joint", "get_multi_winner_and_money"]
  12. def get_win_joint(prem, list_entitys, list_sentences, list_articles):
  13. '''
  14. 获取联合体信息, 添加到prem
  15. :param prem:
  16. :param list_entitys:
  17. :param list_sentences:
  18. :param list_articles:
  19. :return:
  20. '''
  21. try:
  22. if 'win_tenderer' in str(prem[0]['prem']) and re.search('联合(体|方|投标人):|联合体?(成员|单位)[12345一二三四五]?:|(联合体?)?成员单位[12345一二三四五]?:|特殊普通合伙:|(联合(体|投标人))|(联合体(成员|单位)方?[12345一二三四五]?)|((联合体)?成员单位[12345一二三四五]?)|(特殊普通合伙|成员?)|[,;]成:|(成[),]|[与和][^,。]{6,100}联合体', list_articles[0].content):
  23. sentences = sorted(list_sentences[0], key=lambda x:x.sentence_index)
  24. for v in prem[0]['prem'].values():
  25. for d in v['roleList']:
  26. if d.get('role_name', '') == 'win_tenderer':
  27. winner = d.get('role_text')
  28. join_l = [winner]
  29. for list_entity in list_entitys:
  30. for i in range(len(list_entity)-1):
  31. _entity = list_entity[i]
  32. b = _entity.wordOffset_begin
  33. e = _entity.wordOffset_end
  34. if _entity.entity_type in ['org', 'company'] and _entity.label==2 and _entity.values[2] > 0.7 and _entity.entity_text==winner: # 补充概率,修复多中标错误 746420740 ,推荐理由:中国移动通信集团辽宁有限公司大连分公司、大连天途有线电视网络股份有限公司、中国联合网络通信有限公司大连市分公司,参与了本项目投标,
  35. s = sentences[_entity.sentence_index].sentence_text
  36. find_joint = 0 # 是否包含联合体
  37. for j in range(i+1, len(list_entity)):
  38. behind_entity = list_entity[j]
  39. b2 = behind_entity.wordOffset_begin
  40. e2 = behind_entity.wordOffset_end
  41. if _entity.sentence_index == behind_entity.sentence_index and behind_entity.entity_type in ['org', 'company'] \
  42. and b2-e<13 and re.search('联合(体|方|投标人):|联合体?(成员|单位)[12345一二三四五]?:|(联合体?)?成员单位[12345一二三四五]?:|特殊普通合伙:|[,;]成:|(成)$', s[e:b2]) or \
  43. re.search('(联合(体|方|投标人))|(联合体?(成员|单位)方?[12345一二三四五]?)|((联合体?)?成员单位[12345一二三四五]?)|(特殊普通合伙|成员?)|^(成[),]$', s[e2:e2+10]) and behind_entity.label in [2, 5]:
  44. join_l.append(behind_entity.entity_text)
  45. b = b2
  46. e = e2
  47. find_joint = 1
  48. elif (find_joint or re.search('[与和][^,。]{6,100}联合体', list_articles[0].content)) and behind_entity.entity_type in ['org', 'company'] and s[e:b2] in ['与', '和',';','、','&',',','/','//'] and (len(s)==e2 or s[e2] in [';','、','&',',','/','//', '。', ')', '】'] or s[e2:e2+3]=='联合体'):
  49. join_l.append(behind_entity.entity_text)
  50. b = b2
  51. e = e2
  52. elif e == e2: # 修复重复实体导致中断情况
  53. continue
  54. else:
  55. break
  56. if len(join_l)>1:
  57. d['win_tenderer_joint'] = ','.join(set(join_l))
  58. # behind_entity = list_entity[i + 1]
  59. # if _entity.sentence_index== behind_entity.sentence_index and _entity.entity_type in ['org', 'company'] and _entity.label==2\
  60. # and _entity.entity_text==winner and behind_entity.entity_type in ['org', 'company'] and behind_entity.label==5:
  61. # s = sentences[_entity.sentence_index].sentence_text
  62. # b = _entity.wordOffset_begin
  63. # e = _entity.wordOffset_end
  64. # b2 = behind_entity.wordOffset_begin
  65. # e2 = behind_entity.wordOffset_end
  66. # if re.search('(联合体)', s[e2:e2+6]) and b2-e<3:
  67. # print('联合体:', s[max(0, b-10):e2+10])
  68. # d['win_tenderer_joint'] = '%s,%s'%(_entity.entity_text, behind_entity.entity_text)
  69. # break
  70. # elif re.search('(联合体((牵头|主办)(人|方|单位)|主体)|牵头(人|方|单位))|(联合体)?成员:|特殊普通合伙:', s[e:b2]) and b2-e<10:
  71. # d['win_tenderer_joint'] = '%s,%s' % (_entity.entity_text, behind_entity.entity_text)
  72. # print('联合体:', s[max(0, b - 10):e2 + 10])
  73. # break
  74. except Exception as e:
  75. print('获取联合体抛出异常', e)
  76. def get_multi_winner_and_money(channel_dic, prem, list_entitys,list_sentences, all_winner=False):
  77. '''
  78. 获取多中标人及正文、附件所有金额,多中标人multi_winner写入prem,返回金额列表
  79. :param channel_dic:
  80. :param prem:
  81. :param list_entitys:
  82. :param list_sentences:
  83. :return:
  84. '''
  85. def add_multi_winner(pack_l, winner_l):
  86. if len(prem[0]['prem']) > 1 and len(set([it[0] for it in pack_l])) > 1: # 多标段多中标人处理
  87. pk_dic = {}
  88. for ent in winner_l:
  89. for i in range(len(pack_l)):
  90. pk, s1, b1, _ = pack_l[i]
  91. if ent[1] < s1 or ent[1] == s1 and ent[2] < b1:
  92. break
  93. elif (ent[1] > s1 or ent[1] == s1 and ent[2] > b1):
  94. if i < len(pack_l) - 1:
  95. pk2, s2, b2, _ = pack_l[i + 1]
  96. if (ent[1] < s2 or ent[1] == s2 and ent[2] < b2):
  97. if pk not in pk_dic:
  98. pk_dic[pk] = set()
  99. pk_dic[pk].add(ent[0])
  100. else:
  101. continue
  102. else:
  103. if pk not in pk_dic:
  104. pk_dic[pk] = set()
  105. pk_dic[pk].add(ent[0])
  106. else:
  107. continue
  108. for pk, multi_winner in pk_dic.items():
  109. multi_winner = multi_winner - tenderee_or_agency
  110. if len(multi_winner) < 2:
  111. continue
  112. for k, v in prem[0]['prem'].items():
  113. if pk == k:
  114. for d in v['roleList']:
  115. if d.get('role_name', '') == 'win_tenderer':
  116. if d.get('role_text', '') in multi_winner and 'multi_winner' not in d:
  117. d['multi_winner'] = ','.join(set(multi_winner))
  118. elif 0 < len(prem[0]['prem']) < 3: # 修复 单包多中标人 例:285780273
  119. multi_winner = set([it[0] for it in winner_l]) - tenderee_or_agency
  120. if len(multi_winner) > 1:
  121. for v in prem[0]['prem'].values():
  122. for d in v['roleList']:
  123. if d.get('role_name', '') == 'win_tenderer':
  124. if d.get('role_text', '') in multi_winner and 'multi_winner' not in d:
  125. d['multi_winner'] = ','.join(set(multi_winner))
  126. break
  127. moneys = []
  128. moneys_attachment = []
  129. if channel_dic['docchannel']['life_docchannel'] in ['中标信息','候选人公示','合同公告'] and 'win_tenderer' in str(prem):
  130. sentences = sorted(list_sentences[0], key=lambda x: x.sentence_index)
  131. finalists = [] # 入围供应商
  132. multi_winner_l = [] # 保存中标人名称列表
  133. tenderee_or_agency = set()
  134. package_l = []
  135. i = 0
  136. while i < len(list_entitys[0])-1:
  137. ent = list_entitys[0][i]
  138. b_idx_fr = ent.wordOffset_begin
  139. e_idx_fr = ent.wordOffset_end
  140. i += 1
  141. if ent.entity_type in ['money']:
  142. money = float(ent.entity_text)
  143. if ent.in_attachment:
  144. moneys_attachment.append(money)
  145. else:
  146. moneys.append(money)
  147. elif ent.entity_type in ['package']:
  148. package_l.append((ent.entity_text, ent.sentence_index, ent.wordOffset_begin, ent.in_attachment))
  149. elif ent.entity_type in ['org', 'company']:
  150. sentence_text = sentences[ent.sentence_index].sentence_text
  151. pre_text = sentence_text[max(0, b_idx_fr - 10):b_idx_fr]
  152. if ent.label in [0,1] and ent.values[ent.label] > 0.8:
  153. tenderee_or_agency.add(ent.entity_text)
  154. elif ent.label == 2 and (ent.values[ent.label] > 0.8 or all_winner):
  155. multi_winner_l.append((ent.entity_text, ent.sentence_index, ent.wordOffset_begin, ent.in_attachment))
  156. for j in range(i, len(list_entitys[0])):
  157. ent_bh = list_entitys[0][j]
  158. b_idx_bh = ent_bh.wordOffset_begin
  159. e_idx_bh = ent_bh.wordOffset_end
  160. if ent_bh.entity_type in ['org','company'] and ent_bh.label in [2,5] and ent_bh.sentence_index == ent.sentence_index and b_idx_bh - e_idx_fr in [1, 2]:
  161. if sentence_text[e_idx_fr:b_idx_bh] in [';', '、', '&', ',', '/', '//'] and (
  162. len(sentence_text) == e_idx_bh or sentence_text[e_idx_bh] in [';', '、', '&', ',','/', '//','。']): # 修复多中标人刚好在文末index超出报错,例子 407126558
  163. multi_winner_l.append((ent_bh.entity_text, ent_bh.sentence_index, ent_bh.wordOffset_begin, ent_bh.in_attachment))
  164. e_idx_fr = e_idx_bh
  165. i = j + 1
  166. else:
  167. break
  168. 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: # 两实体间没符号分割情况
  169. multi_winner_l.append((ent_bh.entity_text, ent_bh.sentence_index, ent_bh.wordOffset_begin, ent_bh.in_attachment))
  170. e_idx_fr = e_idx_bh
  171. i = j + 1
  172. elif ent_bh.entity_type in ['org', 'company'] and ent_bh.label == 5 and e_idx_fr == e_idx_bh: # 处理 514603520 中国邮政储蓄银行股份有限公司淄博市临淄区支行 实体由于字典匹配重复两次情况
  173. i = j + 1
  174. else:
  175. break
  176. if re.search('入围', pre_text) and re.search('未入围', pre_text)==None:
  177. finalists.append((ent.entity_text, ent.sentence_index, ent.wordOffset_begin, ent.in_attachment))
  178. elif all_winner==1 and ent.label in [3,4,5] and re.search('第[一二三四五六七八九十0-9]+名|候选(人|单位)|入围(单位|供应商)|投标银行', pre_text) and re.search('未', pre_text)==None:
  179. multi_winner_l.append((ent.entity_text, ent.sentence_index, ent.wordOffset_begin, ent.in_attachment))
  180. if len(multi_winner_l)>=2:
  181. winner_main = [it for it in multi_winner_l if not it[3]]
  182. winner_attn = [it for it in multi_winner_l if it[3]]
  183. pack_main = [it for it in package_l if not it[3]]
  184. pack_attn = [it for it in package_l if it[3]]
  185. if len(set([it[0] for it in winner_main]))>=2: # 有两个及以上多中标人及多标段 例:441612746
  186. add_multi_winner(pack_main, winner_main)
  187. elif len(set([it[0] for it in winner_attn]))>=2:
  188. add_multi_winner(pack_attn, winner_attn)
  189. if len(finalists)>=2: # 多入围候选人
  190. winner_main = [it for it in finalists if not it[3]]
  191. winner_attn = [it for it in finalists if it[3]]
  192. pack_main = [it for it in package_l if not it[3]]
  193. pack_attn = [it for it in package_l if it[3]]
  194. if len(set([it[0] for it in winner_main]))>=2: # 有两个及以上多中标人及多标段 例:276326152
  195. add_multi_winner(pack_main, winner_main)
  196. elif len(set([it[0] for it in winner_attn]))>=2:
  197. add_multi_winner(pack_attn, winner_attn)
  198. else:
  199. for i in range(len(list_entitys[0])):
  200. ent = list_entitys[0][i]
  201. if ent.entity_type in ['money']:
  202. money = float(ent.entity_text)
  203. if ent.in_attachment:
  204. moneys_attachment.append(money)
  205. else:
  206. moneys.append(money)
  207. return {'moneys': list(set(moneys)), 'moneys_attachment': list(set(moneys_attachment))}