tools.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. from flask import jsonify, request
  2. from public import db
  3. from datetime import datetime
  4. from queue import Queue
  5. import ipaddress
  6. import json
  7. import time
  8. import six
  9. def human_time(date=None):
  10. if date:
  11. assert isinstance(date, datetime)
  12. else:
  13. date = datetime.now()
  14. return date.strftime('%Y-%m-%d %H:%M:%S')
  15. def human_diff_time(time1, time2):
  16. if not (isinstance(time1, datetime) and isinstance(time2, datetime)):
  17. raise TypeError('Expect a datetime.datetime value')
  18. delta = time1 - time2 if time1 > time2 else time2 - time1
  19. if delta.seconds < 60:
  20. text = '%d秒' % delta.seconds
  21. elif delta.seconds < 3600:
  22. text = '%d分' % (delta.seconds / 60)
  23. else:
  24. text = '%d小时' % (delta.seconds / 3600)
  25. return '%d天%s' % (delta.days, text) if delta.days else text
  26. def is_valid_ip(*address):
  27. try:
  28. for ip in address:
  29. ipaddress.ip_address(ip)
  30. return True
  31. except ValueError:
  32. return False
  33. def json_response(data='', message=''):
  34. if message:
  35. return jsonify({'data': '', 'message': message})
  36. if isinstance(data, list) and all([hasattr(x, 'to_json') for x in data]):
  37. data = [x.to_json() for x in data]
  38. elif isinstance(data, db.Model) and hasattr(data, 'to_json'):
  39. data = data.to_json()
  40. return jsonify({'data': data, 'message': message})
  41. class ParseError(BaseException):
  42. def __init__(self, message):
  43. self.message = message
  44. class AttrDict(dict):
  45. def __setattr__(self, key, value):
  46. self.__setitem__(key, value)
  47. def __getattr__(self, item):
  48. return self.__getitem__(item)
  49. def __delattr__(self, item):
  50. self.__delitem__(item)
  51. class Argument(object):
  52. """
  53. :param name: name of option
  54. :param default: default value if the argument if absent
  55. :param bool required: is required
  56. """
  57. def __init__(self, name, default=None, required=True, type=str, filter=None, help=None, nullable=False):
  58. self.name = name
  59. self.default = default
  60. self.type = (type,)
  61. self.required = required
  62. self.nullable = nullable
  63. self.filter = filter
  64. self.help = help
  65. if type == str:
  66. self.type = six.string_types
  67. if not isinstance(self.name, six.string_types):
  68. raise TypeError('Argument name must be string')
  69. if filter and not callable(self.filter):
  70. raise TypeError('Argument filter is not callable')
  71. def parse(self, has_key, value):
  72. if not has_key:
  73. if self.required and self.default is None:
  74. raise ParseError(self.help or 'Required Error: %s is required' % self.name)
  75. else:
  76. return self.default
  77. elif value in [u'', '', None]:
  78. if self.default is not None:
  79. return self.default
  80. elif not self.nullable and self.required:
  81. raise ParseError(self.help or 'Value Error: %s must not be null' % self.name)
  82. else:
  83. return None
  84. try:
  85. if self.type[0] in six.integer_types:
  86. value = self.type[0](value)
  87. elif self.type[0] == dict:
  88. value = json.loads(value)
  89. elif self.type[0] == bool:
  90. assert value.lower() in ['true', 'false']
  91. value = value.lower() == 'true'
  92. except (ValueError, AssertionError):
  93. raise ParseError(self.help or 'Type Error: %s type must be %s' % (self.name, self.type[0]))
  94. if self.filter:
  95. if not self.filter(value):
  96. raise ParseError(self.help or 'Value Error: %s filter check failed' % self.name)
  97. return value
  98. class BaseParser(object):
  99. def __init__(self, *args):
  100. self.args = []
  101. for e in args:
  102. if isinstance(e, six.string_types):
  103. e = Argument(e)
  104. elif not isinstance(e, Argument):
  105. raise TypeError('%r is not instance of Argument' % e)
  106. self.args.append(e)
  107. def _get(self, key):
  108. raise NotImplementedError
  109. def _init(self, data):
  110. raise NotImplementedError
  111. def add_argument(self, **kwargs):
  112. self.args.append(Argument(**kwargs))
  113. def parse(self, data=None):
  114. rst = AttrDict()
  115. try:
  116. self._init(data)
  117. for e in self.args:
  118. rst[e.name] = e.parse(*self._get(e.name))
  119. except ParseError as err:
  120. return None, err.message
  121. return rst, None
  122. class JsonParser(BaseParser):
  123. def __init__(self, *args):
  124. self.__data = None
  125. super(JsonParser, self).__init__(*args)
  126. def _get(self, key):
  127. return key in self.__data, self.__data.get(key)
  128. def _init(self, data):
  129. if data is None:
  130. self.__data = request.args.to_dict()
  131. post_json = request.get_json()
  132. if isinstance(post_json, dict):
  133. self.__data.update(post_json or {})
  134. else:
  135. try:
  136. if isinstance(data, six.string_types):
  137. self.__data = json.loads(data)
  138. else:
  139. assert hasattr(data, '__contains__')
  140. assert hasattr(data, 'get')
  141. assert callable(data.get)
  142. self.__data = data
  143. except (ValueError, AssertionError):
  144. raise ParseError('Invalid data type for parse')
  145. class WarpQueue(Queue):
  146. def __init__(self, task_count, *args, **kwargs):
  147. # 每个队列的生命周期最大为30分钟
  148. self.expired_time = time.time() + 30 * 60
  149. self.task_count = task_count
  150. # 支持设置destroyed关键字参数,用于销毁队列前进行自定义操作
  151. destroyed = kwargs.pop('destroyed', [])
  152. if not all([callable(x) for x in destroyed]):
  153. raise TypeError('destroyed does not callable')
  154. self.destroyed = destroyed
  155. super().__init__(*args, **kwargs)
  156. @property
  157. def finished(self):
  158. # 超过生命周期或者任务完成后将被移除
  159. return time.time() > self.expired_time or self.task_count <= 0
  160. def done(self):
  161. self.task_count -= 1
  162. def destroy(self):
  163. for f in self.destroyed:
  164. f()
  165. class CommonQueue(object):
  166. def __init__(self):
  167. self._queues = dict()
  168. def make_queue(self, token, task_count, *args, **kwargs):
  169. self.remove_queue()
  170. q = WarpQueue(task_count, *args, **kwargs)
  171. self._queues[token] = q
  172. return q
  173. def get_queue(self, token):
  174. return self._queues.get(token)
  175. def remove_queue(self, token=None):
  176. if token:
  177. q = self._queues.pop(token, None)
  178. if q:
  179. q.destroy()
  180. return
  181. for t, q in [(t, q) for t, q in self._queues.items() if q.finished]:
  182. q.destroy()
  183. self._queues.pop(t)
  184. QueuePool = CommonQueue()