money.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # -*- coding: utf-8 -*-
  2. """金额规则预测器。
  3. 按 ARCHITECTURE.md Phase 5 拆分建议,从 ``interface/predictor.py`` 迁出以下
  4. 金额相关类:
  5. - ``MoneyGrade`` — 金额概率分级(原 predictor.py 第 2954-3024 行)
  6. - ``DepositPaymentWay`` — 保证金支付方式提取(原 5551-5581 行)
  7. - ``TotalUnitMoney`` — 总价单价提取(原 6203-6231 行)
  8. ``interface/predictor.py`` 仍 re-export 以上全部名称,老 import 不受影响。
  9. """
  10. from __future__ import absolute_import
  11. import re
  12. from BiddingKG.dl.money.re_money_total_unit import extract_total_money, extract_unit_money
  13. __all__ = [
  14. "MoneyGrade",
  15. "DepositPaymentWay",
  16. "TotalUnitMoney",
  17. ]
  18. class MoneyGrade():
  19. def __init__(self):
  20. self.tenderee_money_left_9 = "(?P<tenderee_left_9>最高(投标)?限价)|控制(价|金额)|拦标价"
  21. self.tenderee_money_left_8 = "(?P<tenderee_left_8>预算|限价|起始|起拍|底价|标底)"
  22. self.tenderer_money_left_9 = "(?P<tenderer_left_9>(中标|成交|合同|总报价))"
  23. self.tenderer_money_left_8 = "(?P<tenderer_left_8>(投标|总价))"
  24. self.pattern_list = [self.tenderee_money_left_8, self.tenderer_money_left_8, self.tenderee_money_left_9, self.tenderer_money_left_9]
  25. def predict(self, list_sentences, list_entitys, span=10, min_prob=0.7):
  26. sentences = sorted(list_sentences[0], key=lambda x:x.sentence_index)
  27. role2id = {"tenderee": 0, "tenderer": 1}
  28. for entity in list_entitys[0]:
  29. if entity.entity_type in ['money'] and entity.label in [0, 1] and entity.values[entity.label]> 0.6:
  30. text = sentences[entity.sentence_index].sentence_text
  31. in_att = sentences[entity.sentence_index].in_attachment
  32. b = entity.wordOffset_begin
  33. e = entity.wordOffset_end
  34. context = text[max(0, b - span):b]
  35. front_long = text[max(0, b - span*3):b]
  36. behind = text[e:e+span]
  37. not_found = 1
  38. if entity.label == 0 and re.search('招标(控制价|金额|预算)小于$', context): # 修复预算提错。 784107197 招标控制价小于200万的标段招标文件不收取费用
  39. entity.values[entity.label] = 0.49
  40. continue
  41. for pattern in self.pattern_list:
  42. ser = re.search(pattern, context)
  43. if ser:
  44. groupdict = pattern.split('>')[0].replace('(?P<', '')
  45. _role, _direct, _prob = groupdict.split('_')
  46. if re.search('单价|不含税', context[-8:]) or re.search('(最低|风险)控制价', context) or entity.notes == '总投资' or re.match('[至到]($|¥)?\d+', behind):# or float(entity.entity_text)<100: # 753504698 最高限价单价 概率降低
  47. _prob = 6
  48. _label = role2id.get(_role)
  49. if _label != entity.label:
  50. continue
  51. _prob = int(_prob) * 0.1
  52. if in_att:
  53. _prob = max(0.5, _prob - 0.2)
  54. entity.values[_label] = _prob + entity.values[_label] / 20
  55. not_found = 0
  56. if _label == 0 and float(entity.entity_text)<100 and entity.values[_label] > 0.6: # 20250624 小金额预算概率降低 634252534 包合计才是真正的预算
  57. entity.values[_label] = 0.6
  58. break
  59. if not_found and entity.values[entity.label] > min_prob:
  60. if re.search('单价', context[-8:]) or re.search('(最低|风险)控制价', context) or float(entity.entity_text)<100:
  61. _prob = 0.6
  62. elif in_att:
  63. _prob = max(0.5, min_prob - 0.1)
  64. else:
  65. _prob = min_prob
  66. # _prob = min_prob - 0.1 if in_att else min_prob
  67. entity.values[entity.label] = _prob + entity.values[entity.label] / 20
  68. # print('找不到规则修改金额概率:', entity.entity_text, entity.label, entity.values)
  69. if not_found and re.search('[\d,.万]+元?至¥?$', front_long) and entity.values[entity.label] > 0.5:
  70. entity.values[entity.label] = 0.8
  71. # if entity.entity_type in ['money'] and entity.label in [0, 1] and 0.5<=entity.values[entity.label]<0.75 and float(entity.entity_text)<100: # 20241011 低概率小金额改为其他金额 # 20241128 小金额可能为单价,放单价存放
  72. # entity.label = 2
  73. elif entity.entity_type in ['money']:
  74. text = sentences[entity.sentence_index].sentence_text
  75. in_att = sentences[entity.sentence_index].in_attachment
  76. b = entity.wordOffset_begin
  77. e = entity.wordOffset_end
  78. context = text[max(0, b - span*4):b]
  79. if re.search('(?<!不)含税总价:$', context) and entity.label in [1, 2] and re.search('(中标|成交|合同|响应|投标)价|报价', context): # 783902219 响应报价:不含税总价:136800元,增值税税率:6%、含税总价:145008元
  80. entity.label = 1
  81. entity.values[1] = 0.8
  82. elif re.search('(预算|服务)金额:¥[\d,.万]+元?至¥?$', context): # 718392079 服务金额:¥10000.0元至¥20000.0元
  83. entity.label = 0
  84. entity.values[0] = 0.8
  85. # 保证金支付方式提取
  86. class DepositPaymentWay():
  87. def __init__(self,):
  88. self.pt = '(保证金的?(交纳|缴纳|应按下列|入账|支付)方式)[::]*([^,。]{,60})'
  89. self.pt2 = '保证金(必?须以|必?须?通过|以)(.{,8})方式'
  90. kws = ['银行转账', '公?对公方?式?转账', '对公转账', '柜台转账', '(线上|网上)自?行?(缴纳|交纳|缴退|收退)',
  91. '网上银行支付', '现金存入', '直接缴纳', '支票', '汇票', '本票', '电汇', '转账', '汇款', '随机码',
  92. '入账', '基本账户转出', '基本账户汇入', '诚信库中登记的账户转出',
  93. '银行保函', '电子保函', '担保函', '保证保险', '合法担保机构出具的担保', '金融机构、担保机构出具的保函']
  94. self.kws = sorted(kws, key=lambda x: len(x), reverse=True)
  95. def predict(self,content):
  96. pay_way = {'deposit_patment_way':''}
  97. result = []
  98. pay = re.search(self.pt, content)
  99. if pay:
  100. # print(pay.group(0))
  101. pay = pay.group(3)
  102. for it in re.finditer('|'.join(self.kws), pay):
  103. result.append(it.group(0))
  104. pay_way['deposit_patment_way'] = ';'.join(result)
  105. return pay_way
  106. pay = re.search(self.pt2, content)
  107. if pay:
  108. # print(pay.group(0))
  109. pay = pay.group(2)
  110. for it in re.finditer('|'.join(self.kws), pay):
  111. result.append(it.group(0))
  112. pay_way['deposit_patment_way'] = ';'.join(result)
  113. return pay_way
  114. else:
  115. return pay_way
  116. # 总价单价提取
  117. class TotalUnitMoney:
  118. def __init__(self):
  119. pass
  120. def predict(self, list_sentences, list_entitys):
  121. for i in range(len(list_entitys)):
  122. list_entity = list_entitys[i]
  123. # 总价单价
  124. for _entity in list_entity:
  125. if _entity.entity_type == 'money':
  126. word_of_sentence = list_sentences[i][_entity.sentence_index].sentence_text
  127. # 总价在中投标金额中
  128. if _entity.label == 1:
  129. result = extract_total_money(word_of_sentence,
  130. _entity.entity_text,
  131. [_entity.wordOffset_begin, _entity.wordOffset_end])
  132. if result:
  133. _entity.is_total_money = 1
  134. # 单价在普通金额中
  135. else:
  136. result = extract_unit_money(word_of_sentence,
  137. _entity.entity_text,
  138. [_entity.wordOffset_begin, _entity.wordOffset_end])
  139. if result:
  140. _entity.is_unit_money = 1
  141. # print("total_unit_money", _entity.entity_text,
  142. # _entity.is_total_money, _entity.is_unit_money)