_common.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. # -*- coding: utf-8 -*-
  2. """Predictor 共享状态与辅助函数。
  3. 按 ARCHITECTURE.md Phase 5 拆分建议,从 ``interface/predictor.py`` 迁出
  4. 模块级共享变量和辅助函数,供 ``predictors/`` 下各模块引用。
  5. 原位置:``interface/predictor.py`` 模块级代码:
  6. - ``sess_config`` — TF session 配置(当前为 None)
  7. - ``agency_set`` — 代理机构集合(pickle 加载)
  8. - ``header_set`` — 表头集合(pickle 加载)
  9. - ``is_agency`` / ``get_td_companys`` / ``get_role`` — 角色识别辅助函数
  10. ``interface/predictor.py`` 仍 re-export 以上全部名称,老 import 不受影响。
  11. """
  12. from __future__ import absolute_import
  13. import os
  14. import re
  15. import pickle
  16. # 显式 import,不使用 `from common.Utils import *`
  17. from BiddingKG.dl.common.logging import log
  18. from BiddingKG.dl.common.nerUtils import getNers
  19. from BiddingKG.dl.common.context_utils import clean_company
  20. __all__ = [
  21. "INTERFACE_DIR",
  22. "sess_config",
  23. "agency_set",
  24. "header_set",
  25. "is_agency",
  26. "get_td_companys",
  27. "get_role",
  28. ]
  29. #: ``interface/`` 目录绝对路径,用于定位模型文件、pickle 等。
  30. #: ``predictors/`` 与 ``interface/`` 同级,所以向上一级再进 ``interface/``。
  31. INTERFACE_DIR = os.path.normpath(
  32. os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "interface")
  33. )
  34. # ============================================================
  35. # TF session 配置(原 predictor.py 第 40-45 行)
  36. # ============================================================
  37. # 原 predictor.py 中 sess_config 先用 tf.ConfigProto 创建再被覆盖为 None。
  38. # 这里直接设为 None,保持与当前运行时行为一致。
  39. sess_config = None
  40. # ============================================================
  41. # 代理机构集合 / 表头集合(原 predictor.py 第 47-52 行)
  42. # ============================================================
  43. _agency_set_path = os.path.join(INTERFACE_DIR, "agency_set.pkl")
  44. with open(_agency_set_path, "rb") as _f:
  45. agency_set = pickle.load(_f)
  46. _header_set_path = os.path.join(INTERFACE_DIR, "header_set.pkl")
  47. with open(_header_set_path, "rb") as _f:
  48. header_set = pickle.load(_f)
  49. # ============================================================
  50. # 角色识别辅助函数(原 predictor.py 第 54-149 行)
  51. # ============================================================
  52. def is_agency(entity_text):
  53. if re.search(
  54. "(招投?标|采购|代理|咨询|管理|物资|事务所?|顾问|监理|拍卖)[()\w]{,4}(有限)?(责任)?公司|(采购|招投?标|交易|代理|咨询)[()\w]{,4}(中心|服务所)|法院$",
  55. entity_text,
  56. ) or entity_text in agency_set:
  57. return True
  58. return False
  59. def get_td_companys(text, nlp_enterprise):
  60. """获取字符串 text 角色实体(表格场景)。
  61. :param text: 待获取实体字符串
  62. :param nlp_enterprise: 公告中的角色实体列表
  63. :return: (leader, joint) 主报名人和联合体
  64. """
  65. text = re.sub(
  66. "主报名人:|联合报名人:|联合体:|联合体(成员|单位)[12345一二三四五]?:|(联合体)?成员单位[12345一二三四五]?:|特殊普通合伙:|[((【]([主成]|联合体)[))】]|((联合体)?(牵头|成员)(方|人|单位))",
  67. ",",
  68. text,
  69. )
  70. text = re.sub("\s", "", text) # 修复 370835008 表格中实体中间有\n
  71. text = re.sub(
  72. "[一二三四五六七八九十]+标段[::]|标段[一二三四五六七八九十]+[::]|第[一二三四五六七八九十]+名[::]",
  73. "",
  74. text,
  75. ) # 2024/4/22 修复 372839375 三标段:宁夏一山科技有限公司
  76. text = re.sub("1[3-9]\d{9}|\d{3}-\d{8}|\d{4}-\d{7}", "", text) # 2024/4/23 去除电话
  77. text = re.sub(r"([\w()]{5,20}有限)公?$", r"\1公司", text)
  78. joint = ""
  79. leader = ""
  80. if text in nlp_enterprise:
  81. leader = text
  82. elif re.sub("(个体工商户)$", "", text) in nlp_enterprise or re.match(
  83. "[\w()]{6,25}(个体工商户)$", text
  84. ):
  85. leader = text
  86. elif re.match(
  87. "^\w{3,25}(海关|殡仪馆|店|村委会|纪念馆|监狱|管教所|修养所|社区|农场|林场|羊场|猪场|石场|养殖场|饲养场|经营部|经销部|商贸部|经销处|商行)$",
  88. text,
  89. ):
  90. leader = text
  91. elif len(text) < 4:
  92. leader = ""
  93. elif len(nlp_enterprise) > 0:
  94. roles = []
  95. try:
  96. for it in re.finditer("|".join(nlp_enterprise), text):
  97. roles.append(it.group(0))
  98. except Exception as e:
  99. print("get_td_companys 异常:", e)
  100. if roles and len("".join(roles)) * 2 > len(text):
  101. leader = roles[0]
  102. if len(roles) > 1:
  103. joint = ",".join(set(roles))
  104. if leader == "" and len(text) > 4:
  105. ners = getNers([text], useselffool=True)
  106. roles = []
  107. if ners:
  108. for ner in ners[0]:
  109. entity_text = ner[3]
  110. if text[ner[1]:] == "(个体工商户)": # 修复 肇州县一胜建材经销处(个体工商户)这种类型
  111. entity_text = ner[3] + "(个体工商户)"
  112. if ner[2] in ["org", "company"]:
  113. roles.append(entity_text)
  114. elif ner[2] in ["location"] and re.search(
  115. "^\w{3,10}(海关|殡仪馆|店|村委会|纪念馆|监狱|管教所|修养所|社区|农场|林场|羊场|猪场|石场|经营部|经销处)$",
  116. ner[3],
  117. ):
  118. roles.append(entity_text)
  119. if roles and len("".join(roles)) > len(text) * 0.8:
  120. roles = [clean_company(company) for company in roles]
  121. roles = [it for it in roles if len(it) > 3]
  122. if roles:
  123. leader = roles[0]
  124. if len(roles) > 1:
  125. joint = ",".join(set(roles))
  126. if leader:
  127. leader = clean_company(leader)
  128. return leader, joint
  129. def get_role(text, nlp_enterprise):
  130. """获取字符串 text 角色实体。
  131. :param text: 待获取实体字符串
  132. :param nlp_enterprise: 公告中的角色实体列表
  133. :return: 角色实体文本
  134. """
  135. text = re.sub(
  136. "主报名人:|联合报名人:|联合体:|联合体(成员|单位)[12345一二三四五]?:|(联合体)?成员单位[12345一二三四五]?:|特殊普通合伙:|[((][主成][))]",
  137. ",",
  138. text,
  139. )
  140. text = re.sub("\s", "", text) # 修复 370835008 表格中实体中间有\n
  141. text = re.sub(
  142. "[一二三四五六七八九十]+标段[::]|标段[一二三四五六七八九十]+[::]|第[一二三四五六七八九十]+名[::]",
  143. "",
  144. text,
  145. ) # 2024/4/22 修复 372839375 三标段:宁夏一山科技有限公司
  146. text = re.sub("1[3-9]\d{9}|\d{3}-\d{8}|\d{4}-\d{7}", "", text) # 2024/4/23 去除电话
  147. if text in nlp_enterprise:
  148. return text
  149. if len(text) > 50 or len(text) < 4:
  150. return ""
  151. ners = getNers([text], useselffool=True)
  152. roles = []
  153. if ners:
  154. for ner in ners[0]:
  155. entity_text = ner[3]
  156. if text[ner[1]:] == "(个体工商户)": # 修复 肇州县一胜建材经销处(个体工商户)这种类型
  157. entity_text = ner[3] + "(个体工商户)"
  158. if ner[2] in ["org", "company"]:
  159. roles.append(entity_text)
  160. elif ner[2] in ["location"] and re.search(
  161. "^\w{3,10}(海关|殡仪馆|店|村委会|纪念馆|监狱|管教所|修养所|社区|农场|林场|羊场|猪场|石场|经营部|经销处)$",
  162. ner[3],
  163. ):
  164. roles.append(entity_text)
  165. if roles and len("".join(roles)) > len(text) * 0.8:
  166. entity = clean_company(roles[0])
  167. return entity
  168. else:
  169. return ""