EquipmentInfoService.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. # -*- encoding: utf-8 -*-
  2. """
  3. @File : EquipmentInfoService.py
  4. @Time : 2022/4/14 17:28
  5. @Author : stephen
  6. @Email : zhangdongming@asj6.wecom.work
  7. @Software: PyCharm
  8. """
  9. import datetime
  10. import itertools
  11. import logging
  12. import time
  13. from django.db.models import Value, CharField
  14. from Model.models import EquipmentInfoMonday, EquipmentInfoTuesday, EquipmentInfoWednesday, EquipmentInfoThursday, \
  15. EquipmentInfoFriday, EquipmentInfoSaturday, EquipmentInfoSunday
  16. from Object.utils import LocalDateTimeUtil
  17. """
  18. 设备分表查询Service
  19. 因数据量不断增加,单表保留近七天数据进行分表优化设计
  20. 进行拆分为七张表星期一至星期天进行分表存储。分担单表存储读写压力。
  21. """
  22. class EquipmentInfoService:
  23. @staticmethod
  24. def get_equipment_info_model(dt, val=0):
  25. """
  26. 根据日期判断是星期几,返回相应的Model对象
  27. @param val: 1-7代表week
  28. @param dt: 日期 例:2022-03-03
  29. @return: 星期一至星期天equipment_info对象实例
  30. """
  31. week = 1
  32. if dt:
  33. week = LocalDateTimeUtil.date_to_week(dt)
  34. if 0 < val < 8:
  35. week = val
  36. equipment_info = None
  37. if week == 1:
  38. equipment_info = EquipmentInfoMonday.objects.all().annotate(tab_val=Value('1', output_field=CharField()))
  39. elif week == 2:
  40. equipment_info = EquipmentInfoTuesday.objects.all().annotate(tab_val=Value('2', output_field=CharField()))
  41. elif week == 3:
  42. equipment_info = EquipmentInfoWednesday.objects.all().annotate(tab_val=Value('3', output_field=CharField()))
  43. elif week == 4:
  44. equipment_info = EquipmentInfoThursday.objects.all().annotate(tab_val=Value('4', output_field=CharField()))
  45. elif week == 5:
  46. equipment_info = EquipmentInfoFriday.objects.all().annotate(tab_val=Value('5', output_field=CharField()))
  47. elif week == 6:
  48. equipment_info = EquipmentInfoSaturday.objects.all().annotate(tab_val=Value('6', output_field=CharField()))
  49. elif week == 7:
  50. equipment_info = EquipmentInfoSunday.objects.all().annotate(tab_val=Value('7', output_field=CharField()))
  51. return equipment_info
  52. @classmethod
  53. def find_by_start_time_equipment_info(cls, page, size, user_id, start_time, end_time, event_type,
  54. uid_list):
  55. """
  56. 通过start_time查找指定日期当天设备消息推送
  57. @param page: 页数
  58. @param size: 每页条数
  59. @param user_id: 设备用户id
  60. @param start_time: 开始时间
  61. @param end_time: 结束时间
  62. @param event_type: 事件类型
  63. @param uid_list: 设备uid列表
  64. @return: result 查询结果
  65. """
  66. if start_time and end_time:
  67. start_date = datetime.datetime.fromtimestamp(int(start_time))
  68. # 根据开始日期,获取设备信息查询对象
  69. qs = EquipmentInfoService.get_equipment_info_model(str(start_date.date()), 0)
  70. # 调用查询方法
  71. qs = cls.query_equipment_info(qs, user_id, start_time, end_time, event_type,
  72. uid_list)
  73. # 时区问题
  74. week = LocalDateTimeUtil.date_to_week(str(start_date.date()))
  75. if week > 0:
  76. # 根据筛选日期 查找昨天数据
  77. yesterday = 7 if week == 1 else week - 1
  78. yesterday_info = EquipmentInfoService.get_equipment_info_model('', yesterday)
  79. yesterday_info = cls.query_equipment_info(yesterday_info, user_id, start_time, end_time, event_type,
  80. uid_list)
  81. # 根据筛选日期 查找明天数据
  82. tomorrow = 1 if week == 7 else week + 1
  83. tomorrow_info = EquipmentInfoService.get_equipment_info_model('', tomorrow)
  84. tomorrow_info = cls.query_equipment_info(tomorrow_info, user_id, start_time, end_time, event_type,
  85. uid_list)
  86. qs = qs.union(yesterday_info, tomorrow_info, all=True)
  87. if qs.exists():
  88. count = qs.count()
  89. qs_page = cls.get_equipment_info_page(qs, page, size)
  90. return qs_page, count
  91. return None, 0
  92. @classmethod
  93. def get_equipment_info_week_all(cls, page, size, user_id, start_time, end_time, event_type,
  94. uid_list):
  95. """
  96. 分表查询近七天设备消息推送
  97. @param page: 页数
  98. @param size: 分页大小
  99. @param user_id: 设备用户id
  100. @param start_time: 事件开始时间
  101. @param end_time: 事件结束时间
  102. @param event_type: 事件类型
  103. @param uid_list: uid列表
  104. @return: qs_page, count 结果集
  105. """
  106. # 星期一设备信息查询
  107. monday_qs = EquipmentInfoService.get_equipment_info_model('', 1)
  108. monday_qs = cls.query_equipment_info(monday_qs, user_id, start_time, end_time, event_type,
  109. uid_list)
  110. # 星期二设备信息查询
  111. tuesday_qs = EquipmentInfoService.get_equipment_info_model('', 2)
  112. tuesday_qs = cls.query_equipment_info(tuesday_qs, user_id, start_time, end_time, event_type,
  113. uid_list)
  114. # 星期三设备信息查询
  115. wednesday_qs = EquipmentInfoService.get_equipment_info_model('', 3)
  116. wednesday_qs = cls.query_equipment_info(wednesday_qs, user_id, start_time, end_time, event_type,
  117. uid_list)
  118. # 星期四设备信息查询
  119. thursday_qs = EquipmentInfoService.get_equipment_info_model('', 4)
  120. thursday_qs = cls.query_equipment_info(thursday_qs, user_id, start_time, end_time, event_type,
  121. uid_list)
  122. # 星期五设备信息查询
  123. friday_qs = EquipmentInfoService.get_equipment_info_model('', 5)
  124. friday_qs = cls.query_equipment_info(friday_qs, user_id, start_time, end_time, event_type,
  125. uid_list)
  126. # 星期六设备信息查询
  127. saturday_qs = EquipmentInfoService.get_equipment_info_model('', 6)
  128. saturday_qs = cls.query_equipment_info(saturday_qs, user_id, start_time, end_time, event_type,
  129. uid_list)
  130. # 星期天设备信息查询
  131. sunday_qs = EquipmentInfoService.get_equipment_info_model('', 7)
  132. sunday_qs = cls.query_equipment_info(sunday_qs, user_id, start_time, end_time, event_type,
  133. uid_list)
  134. result = monday_qs.union(tuesday_qs, wednesday_qs, thursday_qs, friday_qs, saturday_qs, sunday_qs, all=True)
  135. count = result.count()
  136. qs_page = cls.get_equipment_info_page(result, page, size)
  137. return qs_page, count
  138. @classmethod
  139. def query_equipment_info(cls, qs, user_id, start_time, end_time, event_type,
  140. uid_list):
  141. """
  142. 设备信息条件查询,根据分表设计,默认条件event_time大于七天前时间
  143. @param qs: 设备信息查询对象
  144. @param user_id: 设备用户id
  145. @param start_time: 开始时间
  146. @param end_time: 结束时间
  147. @param event_type: 事件类型
  148. @param uid_list: 设备uid列表
  149. @return: result 设备信息结果集
  150. """
  151. now_time = int(time.time())
  152. # 获取七天前时间戳
  153. seven_days_before_time = LocalDateTimeUtil.get_before_days_timestamp(now_time, 7)
  154. # 默认查询当前表event_time大于七天前时间
  155. qs = qs.filter(event_time__gt=seven_days_before_time)
  156. if user_id:
  157. qs = qs.filter(device_user_id=user_id)
  158. if event_type:
  159. # 多类型查询
  160. eventTypeList = cls.get_comb_event_type(event_type)
  161. eventTypeList += cls.get_combo_type_bins(event_type)
  162. eventTypeList = list(set(eventTypeList))
  163. qs = qs.filter(event_type__in=eventTypeList)
  164. if start_time and end_time:
  165. qs = qs.filter(event_time__range=(start_time, end_time))
  166. else:
  167. qs = qs.filter(event_time__range=(start_time, now_time))
  168. if uid_list:
  169. uid_list = uid_list.split(',')
  170. qs = qs.filter(device_uid__in=uid_list)
  171. return qs
  172. @classmethod
  173. def get_equipment_info_page(cls, equipment_info_qs, page, size):
  174. """
  175. 获取查询结果集进行排序、分页,遍历重命名字典key(主要针对原函数返回结果集)
  176. @param equipment_info_qs: 设备信息结果集
  177. @param page: 页数
  178. @param size: 分页大小
  179. @return: qs_page 遍历后的设备信息结果集
  180. """
  181. equipment_info_qs = equipment_info_qs.values('id', 'device_uid', 'device_nick_name', 'channel', 'event_type',
  182. 'status', 'answer_status', 'alarm',
  183. 'event_time', 'receive_time', 'is_st', 'add_time',
  184. 'storage_location', 'border_coords', 'tab_val')
  185. equipment_info_qs = equipment_info_qs.order_by('-event_time')
  186. qs_page = equipment_info_qs[(page - 1) * size:page * size]
  187. if not qs_page or not qs_page.exists() or qs_page.count == 0:
  188. return qs_page
  189. for item in qs_page:
  190. # 星期表值
  191. tab_val = item['tab_val']
  192. # id = 星期表值+id
  193. item['id'] = int(tab_val + str(item['id']))
  194. item['devUid'] = item['device_uid']
  195. item['devNickName'] = item['device_nick_name']
  196. item['Channel'] = item['channel']
  197. item['eventType'] = item['event_type']
  198. item['eventTime'] = item['event_time']
  199. item['receiveTime'] = item['receive_time']
  200. item['addTime'] = item['add_time']
  201. item['borderCoords'] = item['border_coords']
  202. item.pop('device_uid')
  203. item.pop('device_nick_name')
  204. item.pop('channel')
  205. item.pop('event_type')
  206. item.pop('event_time')
  207. item.pop('receive_time')
  208. item.pop('add_time')
  209. item.pop('border_coords')
  210. item.pop('tab_val')
  211. return qs_page
  212. @classmethod
  213. def get_comb_event_type(cls, event_type):
  214. """
  215. 重新组合ai消息类型查询,使其支持ai多标签查询
  216. @param event_type: 消息类型
  217. @return: event_type_list 消息类型数组
  218. """
  219. if ',' in event_type:
  220. event_type_list = event_type.split(',')
  221. event_type_list = [int(i.strip()) for i in event_type_list]
  222. else:
  223. event_type_list = [int(event_type)]
  224. ai_event_type_list = []
  225. normal_event_type_list = []
  226. for val in event_type_list:
  227. if val <= 4: # 分离出ai类型,以便后续组合ai标签,目前只存在4个ai类型1,2,3,4
  228. ai_event_type_list.append(val)
  229. else:
  230. normal_event_type_list.append(val)
  231. if len(ai_event_type_list) < 1:
  232. return normal_event_type_list
  233. ai_event_type_list.sort()
  234. ai_type = [1, 2, 3, 4] # AI目前所有的标签,1人,2车,3宠物,4包裹,后续有新类型需要这里加, 后续会优化,存在表里,包裹存对应的aws标签
  235. comb_ai_event_type = []
  236. seen = set()
  237. for i in range(1, len(ai_type) + 1): # 计算所有组合,如[1, 2, 3, 4], 4取1,4取2,4取3,4取4
  238. for s in itertools.combinations(ai_type, i):
  239. if s not in seen: # 去除重复项, 如a=[1,2,3,4,4],会有两个[1,2,3,4,4],[1,2,3,4,4]的组合
  240. seen.add(s)
  241. s_list = list(s)
  242. for ai_event_type in ai_event_type_list:
  243. if ai_event_type in s_list: # 排除没有选择的标签组合
  244. if s_list not in comb_ai_event_type:
  245. s_list = [str(v) for v in s_list]
  246. comb_ai_event_type.append(s_list)
  247. regroup_list = []
  248. for val in comb_ai_event_type: # 组合ai类型组合,如[[2,3],[1,3]] -> [23, 13]
  249. val = ''.join(val)
  250. regroup_list.append(int(val))
  251. group_list = regroup_list + normal_event_type_list # 加上普通移动消息类型
  252. return group_list
  253. @classmethod
  254. def get_all_comb_event_type(cls):
  255. """
  256. 计算ai消息类型全组合
  257. @return: event_type_list ai所有消息类型数组
  258. """
  259. ai_type = [1, 2, 3, 4] # AI目前所有的标签,1人,2车,3宠物,4包裹,后续有新类型需要这里加, 后续会优化,存在表里,包裹存对应的aws标签
  260. comb_ai_event_type = []
  261. for i in range(1, len(ai_type) + 1): # 计算所有组合,如[1, 2, 3, 4], 4取1,4取2,4取3,4取4
  262. for s in itertools.combinations(ai_type, i):
  263. s_list = list(s)
  264. s_list = [str(v) for v in s_list]
  265. comb_ai_event_type.append(s_list)
  266. regroup_list = []
  267. for val in comb_ai_event_type: # 组合ai类型组合,如[[2,3],[1,3]] -> [23, 13]
  268. val = ''.join(val)
  269. regroup_list.append(int(val))
  270. return regroup_list
  271. @staticmethod
  272. def get_equipment_info_obj(dt, **kwargs):
  273. """
  274. 根据日期判断是星期几,返回相应的对象实例
  275. @param dt: 日期 例:2022-03-03
  276. @param kwargs: 设备信息属性值
  277. @return: 星期一至星期天equipment_info对象实例
  278. """
  279. logger = logging.getLogger('info')
  280. week = LocalDateTimeUtil.date_to_week(dt)
  281. logger.info('本周{}'.format(str(week)))
  282. equipment_info = None
  283. if week == 1:
  284. equipment_info = EquipmentInfoMonday(**kwargs)
  285. elif week == 2:
  286. equipment_info = EquipmentInfoTuesday(**kwargs)
  287. elif week == 3:
  288. equipment_info = EquipmentInfoWednesday(**kwargs)
  289. elif week == 4:
  290. equipment_info = EquipmentInfoThursday(**kwargs)
  291. elif week == 5:
  292. equipment_info = EquipmentInfoFriday(**kwargs)
  293. elif week == 6:
  294. equipment_info = EquipmentInfoSaturday(**kwargs)
  295. elif week == 7:
  296. equipment_info = EquipmentInfoSunday(**kwargs)
  297. logger.info(type(equipment_info))
  298. logger.info(equipment_info)
  299. return equipment_info
  300. @classmethod
  301. def get_combo_types(cls, event_type):
  302. """
  303. 获取设备算法组合类型
  304. 51:移动侦测,52:传感器报警,53:影像遗失,54:PIR,55:门磁报警,56:外部发报,57:人型报警(提示:有人出现),58:车型,59:宠物,60:人脸,61:异响,
  305. 0:代表空字符,702:摄像头休眠,703:摄像头唤醒,704:电量过低
  306. AWS AI识别 1:人形,2:车型,3:宠物,4:包裹。云端AI类型
  307. @param event_type:
  308. @return:
  309. """
  310. try:
  311. types = []
  312. res_type = cls.is_type_exist(event_type)
  313. if res_type == 0:
  314. return types
  315. combo_types = [51, 57, 58, 60, 59, 61]
  316. event_type = str(event_type)
  317. len_type = len(event_type)
  318. for i in range(0, len_type):
  319. e_type = int(event_type[len_type - 1 - i])
  320. if e_type == 1:
  321. types.append(combo_types[i])
  322. return types
  323. except Exception as e:
  324. print('推送错误异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  325. return event_type
  326. @classmethod
  327. def is_type_exist(cls, event_type):
  328. """
  329. 判断类型是否存在列表
  330. @param event_type:
  331. @return: 0 or event_type
  332. """
  333. combo_types = cls.combo_type_all()
  334. if not combo_types:
  335. return 0
  336. if event_type in combo_types:
  337. return event_type
  338. return 0
  339. @staticmethod
  340. def combo_type_all():
  341. """
  342. 获取所有组合类型
  343. @return:
  344. """
  345. arr_list = []
  346. event_arr = []
  347. resource_list = [1, 2, 4, 8, 16, 32]
  348. for i in range(2, len(resource_list) + 1):
  349. arr_list += list(itertools.combinations(resource_list, i)) # 表示从 [1,2,3,4] 中选出 3个元素的组合情况
  350. for i in arr_list:
  351. val = 0
  352. for item in i:
  353. val += item
  354. event_arr.append(int(EquipmentInfoService.dec_to_bin(val)))
  355. return event_arr
  356. @staticmethod
  357. def dec_to_bin(num):
  358. """
  359. 十进制转二进制
  360. @param num:
  361. @return:
  362. """
  363. result = ""
  364. while num != 0:
  365. ret = num % 2
  366. num //= 2
  367. result = str(ret) + result
  368. return result
  369. @staticmethod
  370. def get_combo_type_bins(event_type):
  371. """
  372. 获取组合类型二进制列表
  373. @param event_type: 标签类型
  374. @return:
  375. """
  376. res_list = []
  377. try:
  378. if ',' in event_type:
  379. res_list = event_type.split(',')
  380. res_list = [int(i.strip()) for i in res_list]
  381. else:
  382. res_list = [int(event_type)]
  383. combo_types = [51, 57, 58, 60, 59, 61]
  384. for e_item in res_list:
  385. bins = EquipmentInfoService.combo_type_all()
  386. if e_item in combo_types:
  387. event_label = combo_types.index(e_item) + 1
  388. for item in bins:
  389. val = str(item)
  390. val_len = len(val)
  391. if val_len >= event_label and int(val[val_len - event_label]) == 1:
  392. res_list.append(int(item))
  393. return res_list
  394. except Exception as e:
  395. print('推送错误异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  396. return res_list