MerchantPainter.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. from .DataStream import EMchPosmap as pos_map, span_days, time_border, calc_interval
  2. from .MerchantReader import MerchantReader
  3. from matplotlib.figure import Figure
  4. from matplotlib import ticker
  5. from io import BytesIO
  6. import numpy as np
  7. from .algorithm import calc_mchratios, calc_morder_send
  8. import time as time
  9. import logging
  10. logger = logging.getLogger('painter')
  11. def allpathes(reader: MerchantReader, tuple_pathes: dict, days: list, spec=None):
  12. count = len(days)
  13. show_detail = True if len(list(tuple_pathes.keys())) == 1 else False
  14. if show_detail == False:
  15. all_datas = reader.init_data(count)
  16. else:
  17. all_datas = None
  18. for name, tup in tuple_pathes.items():
  19. mch_datas = reader.init_data(count)
  20. for _card_type, _spec in tup:
  21. if spec is not None and _spec != spec:
  22. continue
  23. if show_detail:
  24. detail_datas = reader.init_data(count)
  25. else:
  26. detail_datas = None
  27. for i, day in enumerate(days):
  28. data = reader.read(day, name, _card_type, _spec)
  29. if data is not None:
  30. column_pos = i * 86400
  31. view = mch_datas[:, column_pos:column_pos + 86400]
  32. view += data
  33. if show_detail:
  34. view = detail_datas[:, column_pos:column_pos + 86400]
  35. view += data
  36. if show_detail:
  37. yield name, _card_type, _spec, detail_datas
  38. if all_datas is not None:
  39. all_datas += mch_datas
  40. yield name, None, None, mch_datas
  41. if show_detail == False:
  42. yield 'all', None, None, all_datas
  43. class MerchantPainter(object):
  44. def __init__(self, start_time: int, end_time: int, mchids: set = None, card_types: set = None, spec: int = None, filter_wave: int = None):
  45. self._reader = MerchantReader()
  46. _start_time, _end_time, self._mchids, self._card_types, self._spec, self._filter_wave = start_time, end_time, mchids, card_types, spec, filter_wave
  47. if _end_time is None:
  48. _end_time = int(time.time())
  49. end_time = self._reader.near_stamp(_end_time, False)
  50. if end_time is None:
  51. raise Exception('data is empty')
  52. if _start_time is None or start_time > end_time:
  53. _start_time = end_time - 7200
  54. start_time = self._reader.near_stamp(_start_time, True)
  55. if start_time is None:
  56. raise Exception('data is empty')
  57. stime = lambda t: time.strftime('%d-%H:%M:%S', time.localtime(t))
  58. logger.debug("start_time %d , %s end_time=%s", start_time, stime(start_time), stime(end_time))
  59. interval = calc_interval(start_time, end_time)
  60. start_time = time_border(interval, start_time, True)
  61. end_time = time_border(interval, end_time, False)
  62. self._days = span_days(start_time, end_time)
  63. self._start_time = start_time
  64. self._end_time = end_time
  65. self._interval = interval
  66. pass
  67. def _fig_funs(self):
  68. def create():
  69. fig = Figure(figsize=(19, 8))
  70. ax = fig.subplots()
  71. ax.set_title('success ratio')
  72. ax.set(xlabel='time', ylabel='ratio')
  73. return ax, fig
  74. def flush(ax, fig, xticks=None, xlables=None, yticks=None, ylables=None):
  75. ax.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1, decimals=4))
  76. if xticks is not None:
  77. ax.set_xticks(ticks=xticks)
  78. if xlables is not None:
  79. ax.set_xticklabels(xlables)
  80. if yticks is not None:
  81. ax.set_yticks(ticks=yticks)
  82. if ylables is not None:
  83. ax.set_yticklabels(ylables)
  84. fig.autofmt_xdate()
  85. ax.grid()
  86. fig.subplots_adjust(left=0.1, right=0.8, top=0.95, bottom=0.1)
  87. ax.legend(bbox_to_anchor=(1, 1), loc='upper left')
  88. buf = BytesIO()
  89. fig.savefig(buf, format="png")
  90. return buf
  91. return create, flush
  92. def paint_ratios(self):
  93. tuple_pathes = self._reader.many_tuple_path(self._days, self._mchids, self._card_types, self._spec)
  94. gen = allpathes(self._reader, tuple_pathes, self._days, self._spec)
  95. if len(self._days) == 0:
  96. return BytesIO()
  97. day_stamp = self._days[0]
  98. fig_create, fig_flush = self._fig_funs()
  99. ax, fig = fig_create()
  100. x = np.array([d - self._start_time for d in range(self._start_time, self._end_time)])
  101. if self._filter_wave is not None and self._filter_wave > 1:
  102. window = np.ones(self._filter_wave) / self._filter_wave
  103. else:
  104. window = None
  105. for _mchid, _card_type, _spec, _data in gen:
  106. succ, count, y = calc_mchratios(_data, pos_map, self._start_time - day_stamp, self._end_time - day_stamp)
  107. y = np.convolve(y, window, 'same') if window is not None else y
  108. ax.plot(x, y, ls='-', label=self._label(chname=_mchid, succ=succ, count=count, card_type=_card_type, spec=_spec))
  109. xticks = [d - self._start_time for d in range(self._start_time, self._end_time + 1, self._interval)]
  110. xlables = [time.strftime('%d-%H:%M:%S', time.localtime(d + self._start_time)) for d in xticks]
  111. return fig_flush(ax, fig, xticks=xticks, xlables=xlables)
  112. def _label(self, chname, succ, count, card_type=None, spec=None):
  113. _card_type = None
  114. if card_type == 1:
  115. _card_type = 'SY'
  116. elif card_type == 2:
  117. _card_type = 'SH'
  118. elif card_type == 4:
  119. _card_type = 'YD'
  120. elif card_type == 5:
  121. _card_type = 'LT'
  122. elif card_type == 6:
  123. _card_type = 'DX'
  124. elif card_type == 7:
  125. _card_type = 'TH'
  126. lable = f"{chname}"
  127. if _card_type is not None:
  128. lable += f"-{_card_type}"
  129. if spec is not None:
  130. lable += f"-{spec}"
  131. if count > 0:
  132. ratio = round(succ * 100 / count, 2)
  133. else:
  134. ratio = 0.00
  135. lable += f":{succ}/{count}={ratio}%"
  136. return lable
  137. def _fig_bar_funs(self):
  138. def create():
  139. fig = Figure(figsize=(19, 8))
  140. ax = fig.subplots()
  141. ax.set_title('success ratio')
  142. ax.set(xlabel='time', ylabel='ratio')
  143. return ax, fig
  144. def flush(ax, fig, xticks=None, xlables=None, yticks=None, ylables=None):
  145. if xticks is not None:
  146. ax.set_xticks(ticks=xticks)
  147. if xlables is not None:
  148. ax.set_xticklabels(xlables)
  149. if yticks is not None:
  150. ax.set_yticks(ticks=yticks)
  151. if ylables is not None:
  152. ax.set_yticklabels(ylables)
  153. ax.grid()
  154. fig.autofmt_xdate()
  155. buf = BytesIO()
  156. fig.savefig(buf, format="png")
  157. return buf
  158. return create, flush
  159. def paint_refilling(self):
  160. tuple_pathes = self._reader.many_tuple_path(self._days, self._mchids, self._card_types, self._spec)
  161. gen = allpathes(self._reader, tuple_pathes, self._days, self._spec)
  162. if len(self._days) == 0:
  163. return BytesIO()
  164. day_stamp = self._days[0]
  165. fig_create, fig_flush = self._fig_bar_funs()
  166. ax, fig = fig_create()
  167. lables = list()
  168. datas = list()
  169. for _mchid, _card_type, _spec, _data in gen:
  170. if _mchid == 'all':
  171. continue
  172. lables.append(f"{_mchid}")
  173. ret = calc_morder_send(_data, pos_map, self._start_time - day_stamp, self._end_time - day_stamp)
  174. datas.append(ret)
  175. send_count, submit_count, succ_count, fail_count, amounts, lack = zip(*datas)
  176. width = 0.20
  177. x_asix = np.arange(len(lables))
  178. rect_send = ax.bar(x_asix - width * 0.5, list(send_count), width, label='send', align='center')
  179. rect_submit = ax.bar(x_asix - width * 1.5, list(submit_count), width, label='summit',align='center')
  180. rect_succ = ax.bar(x_asix + width * 0.5, list(succ_count), width, label='succ',align='center')
  181. rect_fail = ax.bar(x_asix + width * 1.5, list(fail_count), width, label='fail', align='center')
  182. ax.bar_label(rect_send, padding=3)
  183. # ax.bar_label(rect_submit, padding=3)
  184. # ax.bar_label(rect_succ, padding=3)
  185. # ax.bar_label(rect_fail, padding=3)
  186. return fig_flush(ax, fig, xticks=x_asix, xlables=lables)