attr_utils.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. # -*- coding: utf-8 -*-
  2. """属性处理纯函数(PURE FUNCTIONS)。
  3. 按 ARCHITECTURE.md Phase 6 拆分建议,从 ``interface/getAttributes.py`` 迁出
  4. 被 ``predictors/`` 反向导入的纯函数,消除 ``predictors ↔ interface.getAttributes``
  5. 循环依赖。
  6. 原位置:``interface/getAttributes.py``:
  7. - ``turnMoneySource`` — 资金来源文本归一(原 3335-3368 行)
  8. - ``get_days_between`` — 计算两日期天数差(原 4211-4228 行)
  9. - ``extract_serviceTime`` — 解析服务期(起止日期/天数)(原 4230-4407 行)
  10. 迁移原因:
  11. - ``predictors/table_prem.py`` 导入 ``extract_serviceTime``
  12. - ``predictors/approval.py`` 导入 ``turnMoneySource``
  13. - ``template_extractor.py`` 导入 ``extract_serviceTime``
  14. - ``interface/predictor.py`` 导入 ``turnMoneySource``, ``extract_serviceTime``
  15. 将这些函数下沉到 ``common/`` 层,使 ``predictors/`` 只依赖 ``common/``,
  16. 不再反向依赖 ``interface.getAttributes``(属于 assembly 层)。
  17. 按 ARCHITECTURE.md §4.3 依赖方向约束:
  18. common 可被各层引用,但不反向依赖 pipeline / predictors / assembly / rules。
  19. ``interface/getAttributes.py`` 仍 re-export 以上全部名称,老 import 不受影响。
  20. """
  21. from __future__ import absolute_import
  22. import re
  23. import calendar
  24. from datetime import datetime
  25. from BiddingKG.dl.common.Utils import isValidDate
  26. __all__ = [
  27. "turnMoneySource",
  28. "get_days_between",
  29. "extract_serviceTime",
  30. ]
  31. def turnMoneySource(moneysource):
  32. result_list = []
  33. if re.search("自筹|业主筹集|筹资|自有", moneysource):
  34. result_list.append("自筹")
  35. if re.search("财政", moneysource) and not re.search("非财政", moneysource):
  36. result_list.append("财政资金")
  37. if re.search("拨款|补助|划拨|拨付|国拨|上级资金", moneysource):
  38. result_list.append("上级拨款")
  39. if re.search("社会资本|社会资金", moneysource):
  40. result_list.append("社会资本")
  41. if re.search("贷款|借款|借贷", moneysource):
  42. result_list.append("贷款资金")
  43. if re.search("债券|债|国债", moneysource):
  44. result_list.append("债券资金")
  45. if re.search("专项|项目资金", moneysource):
  46. result_list.append("项目专项资金")
  47. if re.search("配套", moneysource):
  48. result_list.append("配套资金")
  49. if re.search("外资", moneysource):
  50. result_list.append("外资")
  51. if re.search("国有资金|国企资金|国资|国家投资", moneysource):
  52. result_list.append("国有资金")
  53. if re.search("投资|融资", moneysource):
  54. result_list.append("投资资金")
  55. if re.search("预算(?<!外)|预算内", moneysource):
  56. result_list.append("预算内资金")
  57. if re.search("预算外", moneysource):
  58. result_list.append("预算外资金")
  59. result_list = sorted(result_list, key=lambda x: x)
  60. if len(result_list) > 0 and len(result_list) < 5:
  61. return ",".join(result_list)
  62. else:
  63. return "其他资金"
  64. def get_days_between(day1, day2, get_abs=0):
  65. '''
  66. :param day1: 较小日期
  67. :param day2: 较大日期
  68. :param get_abs: 是否取绝对值
  69. :return: 天数差
  70. '''
  71. # 将日期字符串转换为datetime对象
  72. date1 = datetime.strptime(day1, '%Y-%m-%d')
  73. date2 = datetime.strptime(day2, '%Y-%m-%d')
  74. # 计算日期差
  75. delta = date2 - date1
  76. # 获取天数差
  77. days_difference = delta.days
  78. if get_abs:
  79. return abs(days_difference)
  80. else:
  81. return days_difference
  82. def extract_serviceTime(service_time, page_time):
  83. pattern1 = re.compile("\d{4}[年\-./]\d{1,2}[月\-./]\d{1,2}日?")
  84. pattern2 = re.compile("\d+(?:\.\d+)?[((]?个?[^\d]?[^\d]?(?:日|天|周年|整年|学?年|月|周|日历[天日]|工作[天日])")
  85. pattern3 = re.compile("\d{4}[年\-./]\d{1,2}月?")
  86. pattern4 = re.compile("(?:日|天|周年|年|月|周|日历[天日]|工作[天日]|星期)[^\d]{1,3}\d+(?:\.\d+)?")
  87. DigitsDic = {"零": 0, "壹": 1, "贰": 2, "叁": 3, "肆": 4, "伍": 5, "陆": 6, "柒": 7, "捌": 8, "玖": 9,
  88. "〇": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9,
  89. "两": 2, '貮': 2}
  90. def get_month_days(year, month):
  91. # calendar.monthrange(year, month)返回一个元组,其中第一个元素是月份的第一天是星期几(0-6为星期一到星期日),
  92. # 第二个元素是该月的天数。
  93. _, last_day = calendar.monthrange(year, month)
  94. return last_day
  95. def get_num(text):
  96. CN_UNIT = {'十': 10, '拾': 10, '百': 100,
  97. '佰': 100, '千': 1000, '仟': 1000}
  98. regex = re.compile(r'[〇一二三四五六七八九零壹贰叁肆伍陆柒捌玖貮两十拾百佰千仟]+')
  99. text = regex.search(text)
  100. if text:
  101. text = text.group()
  102. else:
  103. return ""
  104. result = 0
  105. result_list = []
  106. unit = 0
  107. control = 0
  108. for i, d in enumerate(text):
  109. if d in '零百佰千仟' and i == 0:
  110. return ""
  111. if d in DigitsDic:
  112. result += DigitsDic[d]
  113. elif d in CN_UNIT:
  114. if unit == 0:
  115. unit_1 = CN_UNIT[d]
  116. # 这里的处理主要是考虑到类似于二十三亿五千万这种数
  117. if result == 0:
  118. result = CN_UNIT[d]
  119. else:
  120. result *= CN_UNIT[d]
  121. unit = CN_UNIT[d]
  122. result_1 = result
  123. elif unit > CN_UNIT[d]:
  124. result -= DigitsDic[text[i - 1]]
  125. result += DigitsDic[text[i - 1]] * CN_UNIT[d]
  126. unit = CN_UNIT[d]
  127. elif unit <= CN_UNIT[d]:
  128. if (CN_UNIT[d] < unit_1) and (len(result_list) == control):
  129. result_list.append(result_1)
  130. result = (result - result_1) * CN_UNIT[d]
  131. control += 1
  132. else:
  133. result *= CN_UNIT[d]
  134. unit = CN_UNIT[d]
  135. if len(result_list) == control:
  136. unit_1 = unit
  137. result_1 = result
  138. else:
  139. return ""
  140. return sum(result_list) + result
  141. serviceTime_dict = {"service_start": "", "service_end": "", "service_days": 0}
  142. re_num = re.findall(r'[〇一二三四五六七八九零壹贰叁肆伍陆柒捌玖貮两十拾百佰千仟]+', service_time)
  143. for _num in re_num:
  144. if not re.search("[十拾百佰千仟]", _num):
  145. num = ""
  146. for word in _num:
  147. num += str(DigitsDic.get(word, word))
  148. service_time = service_time.replace(_num, num, 1)
  149. else:
  150. num = str(get_num(_num))
  151. service_time = service_time.replace(_num, num, 1)
  152. end_time = ""
  153. service_days = 0
  154. re_page_time = re.search("20\d{2}-\d{2}-\d{2}", page_time)
  155. page_time = re_page_time.group() if re_page_time else "2000-01-01" # page_time为空时默认值为2000-01-01
  156. if re.search(pattern1, service_time):
  157. # end_time = re.findall(pattern1,service_time)[-1]
  158. time_list = []
  159. for _time in re.findall(pattern1, service_time):
  160. _time = re.sub("日", "", _time)
  161. _time = re.sub("[年月./]", "-", _time)
  162. _year, _month, _day = _time.split("-")
  163. _month = int(_month)
  164. _day = int(_day)
  165. _year = int(_year)
  166. if _year > 2050 or _year <= 2000 or _month > 12 or _month <= 0 or _day <= 0 or _day > 31:
  167. service_days = 0
  168. else:
  169. if isValidDate(_year, _month, _day):
  170. _time = str(_year) + '-' + str(_month) + '-' + str(_day)
  171. _time = _time.split("-")[0] + '-' + _time.split("-")[1].rjust(2, "0") + '-' + _time.split("-")[
  172. 2].rjust(2, "0")
  173. time_list.append(_time)
  174. if len(time_list) >= 2:
  175. if get_days_between(page_time, time_list[1]) > 1 and get_days_between(time_list[0], time_list[1]) > 0:
  176. serviceTime_dict['service_end'] = time_list[1]
  177. serviceTime_dict['service_start'] = time_list[0]
  178. elif len(time_list) == 1:
  179. if get_days_between(page_time, time_list[0]) > 1:
  180. serviceTime_dict['service_end'] = time_list[0]
  181. # service_days = (time.mktime(time.strptime(end_time,"%Y-%m-%d"))-page_timestamp)/(24*60*60)
  182. elif re.search(pattern3, service_time):
  183. time_list = []
  184. # end_time = re.findall(pattern3,service_time)[-1]
  185. for _time in re.findall(pattern3, service_time):
  186. _time = re.sub("月", "", _time)
  187. _time = re.sub("[年./]", "-", _time)
  188. _year, _month = _time.split("-")
  189. _day = 0
  190. _month = int(_month)
  191. _year = int(_year)
  192. if _year > 2050 or _year <= 2000 or _month > 12 or _month <= 0:
  193. service_days = 0
  194. else:
  195. _day = get_month_days(_year, _month)
  196. if isValidDate(_year, _month, _day):
  197. _time = str(_year) + '-' + str(_month) + '-' + str(_day)
  198. _time = _time.split("-")[0] + '-' + _time.split("-")[1].rjust(2, "0") + '-' + _time.split("-")[
  199. 2].rjust(2, "0")
  200. time_list.append(_time)
  201. if len(time_list) >= 2:
  202. if get_days_between(page_time, time_list[1]) > 1 and get_days_between(time_list[0], time_list[1]) > 0:
  203. serviceTime_dict['service_end'] = time_list[1]
  204. serviceTime_dict['service_start'] = time_list[0]
  205. elif len(time_list) == 1:
  206. if get_days_between(page_time, time_list[0]) > 1:
  207. serviceTime_dict['service_end'] = time_list[0]
  208. # service_days = (time.mktime(time.strptime(end_time,"%Y-%m-%d"))-page_timestamp)/(24*60*60)
  209. elif re.search(pattern2, service_time) or re.search(pattern4, service_time):
  210. for pattern in [pattern2, pattern4]:
  211. unit = 1
  212. match = re.findall(pattern, service_time)
  213. if len(set(match)) == 1:
  214. match_text = match[0]
  215. if "月" in match_text:
  216. unit = 30
  217. elif "年" in match_text:
  218. unit = 365
  219. elif "周" in match_text or "星期" in match_text:
  220. unit = 7
  221. match_num = float(re.search("\d+", match_text).group())
  222. # 数字能被365整除,单位更正为天
  223. if int(match_num) % 365 == 0:
  224. unit = 1
  225. if unit == 365:
  226. if match_num > 10: # 单位为'年'时,排除数字过大的
  227. match_num = 0
  228. elif unit == 30:
  229. if match_num > 60: # 单位为'月'时,排除数字过大的
  230. match_num = 0
  231. elif unit == 1:
  232. if match_num > 4000: # 单位为'日'时,排除数字过大的
  233. match_num = 0
  234. service_days = int(match_num * unit)
  235. if service_days % 360 == 0:
  236. service_days = service_days / 360 * 365
  237. elif service_days % 180 == 0 and service_days % 360 != 0:
  238. service_days = service_days // 360 * 365 + 180
  239. service_days = int(service_days)
  240. if service_days <= 1 and service_days > 4000:
  241. service_days = 0
  242. # if service_days>3:
  243. if service_days > 0:
  244. # service_days = str(service_days) + "天"
  245. serviceTime_dict['service_days'] = service_days
  246. break
  247. elif "半年" in service_time:
  248. service_days = 180
  249. # service_days = str(service_days) + "天"
  250. serviceTime_dict['service_days'] = service_days
  251. if serviceTime_dict['service_start'] and serviceTime_dict['service_end']:
  252. service_days = get_days_between(serviceTime_dict['service_start'], serviceTime_dict['service_end'])
  253. serviceTime_dict['service_days'] = service_days
  254. return serviceTime_dict