utils.py 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  1. import traceback
  2. import requests
  3. import json
  4. import time
  5. import gevent
  6. import pandas as pd
  7. import random
  8. from datetime import date, timedelta, datetime
  9. from typing import List
  10. # from db_helper import HologresHelper, RedisHelper, MysqlHelper
  11. from db_helper import RedisHelper, MysqlHelper
  12. from config import set_config
  13. from log import Log
  14. from parameter_update import param_update_risk_rule
  15. from parameter_update import param_update_risk_videos
  16. from parameter_update import param_update_risk_filter_flag
  17. config_ = set_config()
  18. log_ = Log()
  19. FESTIVAL = [
  20. ["除夕", 2024020900, 2024030100],
  21. ["春节", 2024020900, 2024021800],
  22. ["初一", 2024021010, 2024021800],
  23. ["初二", 2024021110, 2024021800],
  24. ["初三", 2024021210, 2024021800],
  25. ["初四", 2024021310, 2024021800],
  26. ["初五", 2024021410, 2024021800],
  27. ["情人节", 2024021410, 2024021800],
  28. ["初六", 2024021410, 2024021800],
  29. ["初七", 2024021410, 2024021800],
  30. ["初八", 2024021410, 2024021800],
  31. ["雨水", 2024021909, 2024022000],
  32. ["妇女节", 2024030808, 2024031200],
  33. ["龙抬头", 2024031109, 2024031400],
  34. ["清明", 2024040408, 2024040700]
  35. ]
  36. def send_msg_to_feishu(msg_text):
  37. """发送消息到飞书"""
  38. # webhook地址
  39. webhook = 'https://open.feishu.cn/open-apis/bot/v2/hook/8de4de35-30ed-4692-8854-7a154e89b2f2'
  40. # 自定义关键词key_word
  41. key_word = '服务报警'
  42. headers = {'Content-Type': 'application/json'}
  43. payload_message = {
  44. "msg_type": "text",
  45. "content": {
  46. "text": '{}: {}'.format(key_word, msg_text)
  47. }
  48. }
  49. response = requests.request('POST', url=webhook, headers=headers, data=json.dumps(payload_message))
  50. # print(response.text)
  51. def request_post(request_url, request_data, timeout):
  52. """
  53. post 请求 HTTP接口
  54. :param request_url: 接口URL
  55. :param request_data: 请求参数
  56. :param timeout: 超时时间,单位为秒,type-float or tuple(connect_timeout, read_timeout)
  57. :return: res_data json格式
  58. """
  59. try:
  60. headers = {"Connection": "close"}
  61. #print(request_url)
  62. #print(headers)
  63. response = requests.post(url=request_url, json=request_data, timeout=timeout, headers=headers)
  64. #print("response:", response)
  65. if response.status_code == 200:
  66. res_data = json.loads(response.text)
  67. return res_data
  68. else:
  69. return None
  70. except Exception as e:
  71. #print(e)
  72. log_.error('url: {}, exception: {}, traceback: {}'.format(request_url, e, traceback.format_exc()))
  73. return None
  74. def request_post_data(request_url, request_data, timeout):
  75. """
  76. post 请求 HTTP接口
  77. :param request_url: 接口URL
  78. :param request_data: 请求参数
  79. :param timeout: 超时时间,单位为秒,type-float or tuple(connect_timeout, read_timeout)
  80. :return: res_data json格式
  81. """
  82. try:
  83. headers = {'content-type': 'application/json'}
  84. response = requests.post(url=request_url, data=request_data, timeout=timeout, headers=headers)
  85. #print("response:", response)
  86. if response.status_code == 200:
  87. res_data = json.loads(response.text)
  88. return res_data['outputs']
  89. else:
  90. return None
  91. except Exception as e:
  92. #print(e)
  93. log_.error('url: {}, exception: {}, traceback: {}'.format(request_url, e, traceback.format_exc()))
  94. return None
  95. def request_get(request_url, timeout):
  96. """
  97. get 请求 HTTP接口
  98. :param request_url: 接口URL
  99. :param timeout: 超时时间,单位为秒,type-float or tuple(connect_timeout, read_timeout)
  100. :return: res_data json格式
  101. """
  102. try:
  103. response = requests.get(url=request_url, timeout=timeout)
  104. if response.status_code == 200:
  105. res_data = json.loads(response.text)
  106. return res_data
  107. else:
  108. return None
  109. except Exception as e:
  110. log_.error('url: {}, exception: {}, traceback: {}'.format(request_url, e, traceback.format_exc()))
  111. return None
  112. def get_user_has30day_return(mid):
  113. """
  114. 获取用户近30天是否有回流
  115. :param mid: mid
  116. :return: data, type
  117. """
  118. if not mid:
  119. return None
  120. # 获取redis中存储的状态值
  121. user_key = f"{config_.KEY_NAME_PREFIX_USER_HAS30DAY_RETURN}{mid}"
  122. redis_helper = RedisHelper()
  123. data = redis_helper.get_data_from_redis(key_name=user_key)
  124. if data is not None:
  125. return int(data)
  126. else:
  127. request_url = f"{config_.GET_USER_30DayReturnCnt_URL}{mid}"
  128. result = request_get(request_url=request_url, timeout=0.1)
  129. if result is None:
  130. return None
  131. if result['code'] != 0:
  132. return None
  133. data = result['data']
  134. if data is True:
  135. redis_data = 1
  136. else:
  137. redis_data = 0
  138. redis_helper.set_data_to_redis(key_name=user_key, value=redis_data, expire_time=2 * 3600)
  139. return redis_data
  140. def get_videos_remain_view_count(app_type, videos):
  141. """
  142. 获取视频在流量池中的剩余可分发数
  143. :param app_type: 产品标识 type-int
  144. :param videos: 视频信息 (视频id, 流量池标记) type-list,[{'videoId': video_id, 'flowPool': flow_pool}, ...]
  145. :return: data type-list,[(video_id, flow_pool, view_count), ...]
  146. error_flag 错误标记,True为错误
  147. """
  148. error_flag = False
  149. if not videos:
  150. return [], error_flag
  151. request_data = {'appType': app_type, 'videos': videos}
  152. result = request_post(request_url=config_.GET_REMAIN_VIEW_COUNT_URL, request_data=request_data, timeout=(0.1, 1))
  153. if result is None:
  154. error_flag = True
  155. return [], error_flag
  156. if result['code'] != 0:
  157. log_.info('获取视频在流量池中的剩余可分发数失败')
  158. error_flag = True
  159. return [], error_flag
  160. data = [(item['videoId'], item['flowPool'], item['distributeCount']) for item in result['data']]
  161. return data, error_flag
  162. def get_videos_local_distribute_count(video_id, flow_pool):
  163. """
  164. 获取流量池视频本地分发数
  165. :param video_id: video_id
  166. :param flow_pool: 流量池标记
  167. :return: current_count 本地记录的分发数
  168. """
  169. # redis_h = datetime.now().hour
  170. # if datetime.now().minute >= 30:
  171. # redis_h += 0.5
  172. # key_name = config_.LOCAL_DISTRIBUTE_COUNT_PREFIX + str(redis_h)
  173. key_name = f'{config_.LOCAL_DISTRIBUTE_COUNT_PREFIX}{video_id}:{flow_pool}'
  174. redis_helper = RedisHelper()
  175. # video = '{}-{}'.format(video_id, flow_pool)
  176. # current_count = redis_helper.get_score_with_value(key_name=key_name, value=video)
  177. current_count = redis_helper.get_data_from_redis(key_name=key_name)
  178. if current_count is not None:
  179. return int(current_count)
  180. else:
  181. return None
  182. def update_video_w_h_rate(video_id, key_name):
  183. """
  184. 获取横屏视频的宽高比,并存入redis中 (width/height>1)
  185. :param video_id: videoId type-int
  186. :param key_name: redis key
  187. :return: None
  188. """
  189. # 获取数据
  190. sql = "SELECT id, width, height, rotate FROM longvideo.wx_video WHERE id = {};".format(video_id)
  191. mysql_helper = MysqlHelper()
  192. data = mysql_helper.get_data(sql=sql)
  193. if len(data) == 0:
  194. return
  195. # 更新到redis
  196. width, height, rotate = int(data[0][1]), int(data[0][2]), int(data[0][3])
  197. if width == 0 or height == 0:
  198. return
  199. if rotate in (90, 270):
  200. w_h_rate = height / width
  201. else:
  202. w_h_rate = width / height
  203. if w_h_rate > 1:
  204. info_data = {int(video_id): w_h_rate}
  205. else:
  206. return
  207. redis_helper = RedisHelper()
  208. # 写入新数据
  209. if len(info_data) > 0:
  210. redis_helper.add_data_with_zset(key_name=key_name, data=info_data)
  211. class FilterVideos(object):
  212. """视频过滤"""
  213. def __init__(self, request_id, app_type, video_ids, mid='', uid='',
  214. expansion_factor=None,
  215. risk_filter_flag=None,
  216. app_region_filtered=None,
  217. videos_with_risk=None,
  218. force_truncation=None,
  219. env_dict=None
  220. ):
  221. """
  222. 初始化
  223. :param request_id: request_id
  224. :param app_type: 产品标识 type-int
  225. :param video_ids: 需过滤的视频列表 type-list
  226. :param mid: mid type-string
  227. :param uid: uid type-string
  228. """
  229. self.request_id = request_id
  230. self.app_type = app_type
  231. self.mid = mid
  232. self.uid = uid
  233. self.video_ids = video_ids
  234. self.expansion_factor = expansion_factor
  235. self.risk_filter_flag = risk_filter_flag
  236. self.app_region_filtered = app_region_filtered
  237. self.videos_with_risk = videos_with_risk
  238. self.force_truncation = force_truncation
  239. self.env_dict = env_dict
  240. def filter_video_status_h(self, video_ids, rule_key, data_key, ab_code, province_code, key_flag=''):
  241. """召回小时级更新的视频状态过滤"""
  242. # 根据Redis缓存中的数据过滤
  243. redis_helper = RedisHelper()
  244. # 获取不符合推荐状态的视频
  245. if ab_code in [code for _, code in config_.AB_CODE['region_rank_by_h'].items()]:
  246. if key_flag == 'region_24h':
  247. key_prefix = f"{config_.REGION_H_VIDEO_FILER_24H}{province_code}."
  248. elif key_flag == 'day_24h':
  249. key_prefix = f"{config_.H_VIDEO_FILER_24H}{province_code}."
  250. else:
  251. key_prefix = f"{config_.REGION_H_VIDEO_FILER}{province_code}."
  252. elif ab_code in [code for _, code in config_.AB_CODE['rank_by_24h'].items()]:
  253. key_prefix = config_.H_VIDEO_FILER_24H
  254. elif key_flag == '24h':
  255. key_prefix = config_.H_VIDEO_FILER_24H
  256. else:
  257. key_prefix = config_.H_VIDEO_FILER
  258. filter_videos_list = redis_helper.get_data_from_set(
  259. key_name=f"{key_prefix}{self.app_type}.{data_key}.{rule_key}"
  260. )
  261. if not filter_videos_list:
  262. return video_ids
  263. filter_videos = [int(video) for video in filter_videos_list]
  264. filtered_videos = [video_id for video_id in video_ids if video_id not in filter_videos]
  265. return filtered_videos
  266. def filter_videos_h(self, rule_key, data_key, ab_code, province_code, key_flag='', pool_type='rov'):
  267. """召回小时级更新的视频过滤"""
  268. # 预曝光过滤
  269. # st_pre = time.time()
  270. filtered_pre_result = self.filter_video_previewed(self.video_ids)
  271. # et_pre = time.time()
  272. # log_.info({
  273. # 'logTimestamp': int(time.time() * 1000),
  274. # 'request_id': self.request_id,
  275. # 'app_type': self.app_type,
  276. # 'mid': self.mid,
  277. # 'uid': self.uid,
  278. # 'operation': 'preview_filter',
  279. # 'request_videos': self.video_ids,
  280. # 'preview_filter_result': filtered_pre_result,
  281. # 'executeTime': (time.time() - st_pre) * 1000
  282. # })
  283. if not filtered_pre_result:
  284. return None
  285. # 视频状态过滤
  286. # st_status = time.time()
  287. filtered_status_result = self.filter_video_status_h(video_ids=filtered_pre_result, rule_key=rule_key,
  288. data_key=data_key, ab_code=ab_code,
  289. province_code=province_code, key_flag=key_flag)
  290. # et_status = time.time()
  291. # log_.info({
  292. # 'logTimestamp': int(time.time() * 1000),
  293. # 'request_id': self.request_id,
  294. # 'app_type': self.app_type,
  295. # 'mid': self.mid,
  296. # 'uid': self.uid,
  297. # 'operation': 'status_filter',
  298. # 'request_videos': filtered_pre_result,
  299. # 'status_filter_result': filtered_status_result,
  300. # 'executeTime': (time.time() - st_status) * 1000
  301. # })
  302. if not filtered_status_result:
  303. return None
  304. # 视频已曝光过滤
  305. st_viewed = time.time()
  306. filtered_viewed_result = self.filter_video_viewed(video_ids=filtered_status_result)
  307. # et_viewed = time.time()
  308. log_.info({
  309. 'logTimestamp': int(time.time() * 1000),
  310. 'pool_type': pool_type,
  311. 'request_id': self.request_id,
  312. 'app_type': self.app_type,
  313. 'mid': self.mid,
  314. 'uid': self.uid,
  315. 'operation': 'view_filter',
  316. 'request_videos': filtered_status_result,
  317. 'view_filter_result': filtered_viewed_result,
  318. 'executeTime': (time.time() - st_viewed) * 1000
  319. })
  320. if not filtered_viewed_result:
  321. return None
  322. else:
  323. return [int(video_id) for video_id in filtered_viewed_result]
  324. def filter_videos(self, pool_type='rov', region_code=None, shield_config=None):
  325. """视频过滤"""
  326. # todo: 添加app和region的风险过滤。
  327. st_viewed = time.time()
  328. videos_filtered = self.filter_videos_with_risk_video(self.video_ids, self.app_type, region_code)
  329. # videos_filtered.append(18562889)
  330. # videos_filtered.append(18613648)
  331. # videos_filtered.append(18608478)
  332. videos_filtered = self.filter_videos_with_festival(videos_filtered)
  333. # print(str(videos_filtered))
  334. # log_.info({
  335. # 'logTimestamp': int(time.time() * 1000),
  336. # 'pool_type': "zhangbo-filter-pool_type",
  337. # 'request_id': self.request_id,
  338. # 'app_type': self.app_type,
  339. # 'mid': "zhangbo-filter_videos",
  340. # 'uid': self.uid,
  341. # 'operation': 'shield_filter',
  342. # 'request_videos': self.video_ids,
  343. # 'shield_filter_result': videos_filtered,
  344. # 'executeTime': (time.time() - st_viewed) * 1000
  345. # })
  346. # 预曝光过滤
  347. st_pre = time.time()
  348. filtered_pre_result = self.filter_video_previewed(videos_filtered)
  349. # print("filtered_pre:", (time.time()-st_pre)*1000)
  350. # et_pre = time.time()
  351. # log_.info({
  352. # 'logTimestamp': int(time.time() * 1000),
  353. # 'request_id': self.request_id,
  354. # 'app_type': self.app_type,
  355. # 'mid': self.mid,
  356. # 'uid': self.uid,
  357. # 'operation': 'preview_filter',
  358. # 'request_videos': self.video_ids,
  359. # 'preview_filter_result': filtered_pre_result,
  360. # 'executeTime': (time.time() - st_pre) * 1000
  361. # })
  362. if not filtered_pre_result:
  363. return None
  364. # 视频状态过滤采用离线定时过滤方案
  365. # 视频状态过滤
  366. # st_status = time.time()
  367. # filtered_status_result = self.filter_video_status(video_ids=filtered_pre_result)
  368. # et_status = time.time()
  369. # log_.info('filter by video status: result = {}, execute time = {}ms'.format(
  370. # filtered_status_result, (et_status - st_status) * 1000))
  371. # if not filtered_status_result:
  372. # return None
  373. # 视频已曝光过滤
  374. st_viewed = time.time()
  375. filtered_viewed_result = self.filter_video_viewed(video_ids=filtered_pre_result, region_code=region_code)
  376. # print("filtered_pre:", (time.time() - st_viewed) * 1000)
  377. # et_viewed = time.time()
  378. # log_.info({
  379. # 'logTimestamp': int(time.time() * 1000),
  380. # 'pool_type': pool_type,
  381. # 'request_id': self.request_id,
  382. # 'app_type': self.app_type,
  383. # 'mid': self.mid,
  384. # 'uid': self.uid,
  385. # 'operation': 'view_filter',
  386. # 'request_videos': filtered_pre_result,
  387. # 'view_filter_result': filtered_viewed_result,
  388. # 'executeTime': (time.time() - st_viewed) * 1000
  389. # })
  390. if not filtered_viewed_result:
  391. return None
  392. filtered_viewed_videos = [int(video_id) for video_id in filtered_viewed_result]
  393. return filtered_viewed_videos
  394. # if pool_type == 'flow' or pool_type=='normal':
  395. # # 流量池视频需过滤屏蔽视频
  396. # if region_code is None or shield_config is None:
  397. # return filtered_viewed_videos
  398. # else:
  399. # shield_key_name_list = shield_config.get(region_code, None)
  400. # if shield_key_name_list is not None:
  401. # filtered_shield_video_ids = self.filter_shield_video(
  402. # video_ids=filtered_viewed_videos, shield_key_name_list=shield_key_name_list
  403. # )
  404. # log_.info({
  405. # 'logTimestamp': int(time.time() * 1000),
  406. # 'pool_type': pool_type,
  407. # 'request_id': self.request_id,
  408. # 'app_type': self.app_type,
  409. # 'mid': self.mid,
  410. # 'uid': self.uid,
  411. # 'operation': 'shield_filter',
  412. # 'request_videos': filtered_viewed_videos,
  413. # 'shield_filter_result': filtered_shield_video_ids,
  414. # 'executeTime': (time.time() - st_viewed) * 1000
  415. # })
  416. # # print("filtered_pre flow:", (time.time() - st_viewed) * 1000)
  417. # return filtered_shield_video_ids
  418. # else:
  419. # return filtered_viewed_videos
  420. # else:
  421. # return filtered_viewed_videos
  422. def filter_video_previewed(self, video_ids):
  423. """
  424. 预曝光过滤
  425. :param video_ids: 需过滤的视频列表 type-list
  426. :return: filtered_videos 过滤后的列表 type-list
  427. """
  428. pre_time = time.time()
  429. if not self.mid or self.mid == 'null':
  430. # mid为空时,不做预曝光过滤
  431. return video_ids
  432. # 根据Redis缓存中的数据过滤
  433. redis_helper = RedisHelper()
  434. # key拼接
  435. key_name = f"{config_.PREVIEW_KEY_PREFIX}{self.app_type}:{self.mid}"
  436. #print("key_name:", key_name)
  437. pe_videos_list = redis_helper.get_data_from_set(key_name)
  438. #print("pe_videos_list:", pe_videos_list)
  439. # log_.info('****app_type = {}, mid = {}, uid = {}, pe_videos_list = {}'.format(
  440. # self.app_type, self.mid, self.uid, pe_videos_list))
  441. # log_.info('****app_type = {}, mid = {}, uid = {}, video_ids = {}'.format(
  442. # self.app_type, self.mid, self.uid, video_ids))
  443. if not pe_videos_list:
  444. return video_ids
  445. pe_videos = [int(video) for video in pe_videos_list]
  446. #print("pe_videos:", len(pe_videos))
  447. filtered_videos = [video_id for video_id in video_ids if video_id not in pe_videos]
  448. #print(f"pre res: {filtered_videos}\nexecute_time: {(time.time() - pre_time) * 1000}")
  449. return filtered_videos
  450. # def filter_video_status(self, video_ids):
  451. # """
  452. # 对视频状态进行过滤
  453. # :param video_ids: 视频id列表 type-list
  454. # :return: filtered_videos
  455. # """
  456. # if len(video_ids) == 1:
  457. # sql = "set hg_experimental_enable_shard_pruning=off; " \
  458. # "SELECT video_id " \
  459. # "FROM {} " \
  460. # "WHERE audit_status = 5 " \
  461. # "AND applet_rec_status IN (1, -6) " \
  462. # "AND open_status = 1 " \
  463. # "AND payment_status = 0 " \
  464. # "AND encryption_status != 5 " \
  465. # "AND transcoding_status = 3 " \
  466. # "AND video_id IN ({});".format(config_.VIDEO_STATUS, video_ids[0])
  467. # else:
  468. # sql = "set hg_experimental_enable_shard_pruning=off; " \
  469. # "SELECT video_id " \
  470. # "FROM {} " \
  471. # "WHERE audit_status = 5 " \
  472. # "AND applet_rec_status IN (1, -6) " \
  473. # "AND open_status = 1 " \
  474. # "AND payment_status = 0 " \
  475. # "AND encryption_status != 5 " \
  476. # "AND transcoding_status = 3 " \
  477. # "AND video_id IN {};".format(config_.VIDEO_STATUS, tuple(video_ids))
  478. #
  479. # hologres_helper = HologresHelper()
  480. # data = hologres_helper.get_data(sql=sql)
  481. # filtered_videos = [int(temp[0]) for temp in data]
  482. # return filtered_videos
  483. def filter_video_viewed(self, video_ids, region_code, types=(1, 6,)):
  484. """
  485. 调用后端接口过滤用户已观看视频
  486. :param video_ids: 视频id列表 type-list
  487. :param types: 过滤参数 type-tuple, 默认(1, )
  488. 1-已观看 2-视频状态 3-是否进入老年人社区 4-话题状态 5-推荐状态 6-白名单过滤 7-涉政视频过滤
  489. :return: filtered_videos
  490. """
  491. # 获取对应端的过滤参数types
  492. types = config_.FILTER_VIEWED_TYPES_CONFIG.get(self.app_type, None)
  493. if types is None:
  494. types = config_.FILTER_VIEWED_TYPES_CONFIG.get('other')
  495. try:
  496. log_.info("cityCode:" + self.env_dict["cityCode"] if "cityCode" in self.env_dict else "-1")
  497. log_.info("hotSenceType:" + str(self.env_dict and self.env_dict["hotSenceType"] if "hotSenceType" in self.env_dict else 0))
  498. except Exception as e:
  499. pass
  500. request_data = {"appType": self.app_type,
  501. "mid": self.mid,
  502. "uid": self.uid,
  503. "types": list(types),
  504. "videoIds": video_ids,
  505. "cityCode": self.env_dict["cityCode"] if self.env_dict and "cityCode" in self.env_dict else "-1",
  506. "hotSenceType": self.env_dict["hotSenceType"] if self.env_dict and "hotSenceType" in self.env_dict else 0
  507. }
  508. # print(request_data)
  509. # 调用http接口
  510. result = request_post(request_url=config_.VIDEO_FILTER_URL, request_data=request_data, timeout=(0.2, 1))
  511. # print("result:", result)
  512. if result is None:
  513. # print("result is None")
  514. # log_.info('过滤失败,types: {}'.format(types))
  515. return []
  516. if result['code'] != 0:
  517. # log_.info('过滤失败,types: {}'.format(types))
  518. return []
  519. filtered_videos = result['data']
  520. return filtered_videos
  521. def filter_video_viewed_new(self, video_ids):
  522. """
  523. 调用后端接口过滤用户已观看视频
  524. :param video_ids: 视频id列表 type-list
  525. :param types: 过滤参数 type-tuple, 默认(1, )
  526. 1-已观看 2-视频状态 3-是否进入老年人社区 4-话题状态 5-推荐状态 6-白名单过滤 7-涉政视频过滤
  527. :return: filtered_videos
  528. """
  529. # 获取对应端的过滤参数types
  530. st_time = time.time()
  531. types = config_.FILTER_VIEWED_TYPES_CONFIG.get(self.app_type, None)
  532. #print(types)
  533. if types is None:
  534. types = config_.FILTER_VIEWED_TYPES_CONFIG.get('other')
  535. if 6 in types:
  536. types = list(types)
  537. types.remove(6)
  538. #print(types)
  539. request_data = {"appType": self.app_type,
  540. "mid": self.mid,
  541. "uid": self.uid,
  542. "types": list(types),
  543. "videoIds": video_ids}
  544. # 调用http接口
  545. result = request_post(request_url=config_.VIDEO_FILTER_URL, request_data=request_data, timeout=(0.1, 1))
  546. #print(f"view res: {result}\nexecute_time: {(time.time() - st_time) * 1000}")
  547. if result is None:
  548. # log_.info('过滤失败,types: {}'.format(types))
  549. return []
  550. if result['code'] != 0:
  551. # log_.info('过滤失败,types: {}'.format(types))
  552. return []
  553. filtered_videos = result['data']
  554. return filtered_videos
  555. def filter_shield_video(self, video_ids, shield_key_name_list):
  556. """
  557. 过滤屏蔽视频视频
  558. :param video_ids: 需过滤的视频列表 type-list
  559. :param shield_key_name_list: 过滤视频 redis-key
  560. :return: filtered_videos 过滤后的列表 type-list
  561. """
  562. # print("filter_shield_video:", len(filter_shield_video))
  563. if len(video_ids) == 0:
  564. return video_ids
  565. # 根据Redis缓存中的数据过滤
  566. redis_helper = RedisHelper()
  567. for shield_key_name in shield_key_name_list:
  568. video_ids = [
  569. int(video_id) for video_id in video_ids
  570. if not redis_helper.data_exists_with_set(key_name=shield_key_name, value=video_id)
  571. ]
  572. # shield_videos_list = redis_helper.get_data_from_set(key_name=shield_key_name)
  573. # if not shield_videos_list:
  574. # continue
  575. # shield_videos = [int(video) for video in shield_videos_list]
  576. # video_ids = [int(video_id) for video_id in video_ids if int(video_id) not in shield_videos]
  577. # print("video_ids:", len(video_ids))
  578. return video_ids
  579. def new_filter_video(self):
  580. """视频过滤"""
  581. # 1. 预曝光过滤
  582. st_pre = time.time()
  583. #print("new_filter video_ids:", self.video_ids)
  584. filtered_pre_result = self.filter_video_previewed(self.video_ids)
  585. if not filtered_pre_result:
  586. return None
  587. # log_.info({
  588. # 'logTimestamp': int(time.time() * 1000),
  589. # 'request_id': self.request_id,
  590. # 'app_type': self.app_type,
  591. # 'mid': self.mid,
  592. # 'uid': self.uid,
  593. # 'operation': 'preview_filter',
  594. # 'request_videos': self.video_ids,
  595. # 'preview_filter_result': filtered_pre_result,
  596. # 'executeTime': (time.time() - st_pre) * 1000
  597. # })
  598. #2. 视频已曝光过滤
  599. st_viewed = time.time()
  600. #print("---filtered viewed---")
  601. #print("filtered_pre_result:",filtered_pre_result)
  602. filtered_viewed_result = self.filter_video_viewed(video_ids=filtered_pre_result)
  603. if not filtered_viewed_result:
  604. return None
  605. return filtered_viewed_result
  606. def new_flow_video(self, vid_list, flow_vids_set, region_code, shield_config):
  607. flow_video_list = []
  608. normal_video_list = []
  609. for v_id in vid_list:
  610. if v_id in flow_vids_set:
  611. flow_video_list.append(v_id)
  612. else:
  613. normal_video_list.append(v_id)
  614. shield_key_name_list = shield_config.get(region_code, None)
  615. if shield_key_name_list is not None:
  616. filtered_shield_video_ids = self.filter_shield_video(
  617. video_ids=flow_video_list, shield_key_name_list=shield_key_name_list
  618. )
  619. return normal_video_list, filtered_shield_video_ids
  620. else:
  621. return normal_video_list, flow_video_list
  622. def filter_movie_religion_video(self, video_ids):
  623. """过滤白名单视频(影视,宗教)"""
  624. # 影视 + 宗教: rov.filter.movie.{videoId}
  625. # 宗教: rov.filter.religion.{videoId}
  626. st_time = time.time()
  627. if self.app_type not in [config_.APP_TYPE['WAN_NENG_VIDEO'],
  628. config_.APP_TYPE['LAO_HAO_KAN_VIDEO'],
  629. config_.APP_TYPE['ZUI_JING_QI'],
  630. config_.APP_TYPE['H5']]:
  631. # 过滤 影视 + 宗教
  632. keys = [f"rov.filter.movie.{video_id}" for video_id in video_ids]
  633. elif self.app_type in [config_.APP_TYPE['WAN_NENG_VIDEO'],
  634. config_.APP_TYPE['ZUI_JING_QI'],
  635. config_.APP_TYPE['H5']]:
  636. # 过滤 影视 + 宗教
  637. keys = [f"rov.filter.religion.{video_id}" for video_id in video_ids]
  638. else:
  639. #print(f"m_r res: {video_ids}\nexecute_time: {(time.time() - st_time) * 1000}")
  640. return video_ids
  641. redis_helper = RedisHelper(redis_info=config_.REDIS_INFO_FILTER)
  642. filter_videos = []
  643. for i in range(len(keys)//1000 + 1):
  644. video_ids_temp = video_ids[i*1000:(i+1)*1000]
  645. if len(video_ids_temp) == 0:
  646. break
  647. mget_res = redis_helper.mget(keys=keys[i*1000:(i+1)*1000])
  648. filter_videos.extend([int(data) for data in mget_res if data is not None])
  649. if len(filter_videos) > 0:
  650. filtered_videos = set(video_ids) - set(filter_videos)
  651. #print(f"m_r res: {list(filtered_videos)}\nexecute_time: {(time.time() - st_time) * 1000}")
  652. return list(filtered_videos)
  653. else:
  654. #print(f"m_r res: {video_ids}\nexecute_time: {(time.time() - st_time) * 1000}")
  655. return video_ids
  656. def filter_videos_new(self, region_code=None, shield_config=None, flow_set=None):
  657. """视频过滤"""
  658. # 预曝光过滤
  659. st_pre = time.time()
  660. #print("self.video_ids:", len(self.video_ids))
  661. filtered_pre_result = self.filter_video_previewed(self.video_ids)
  662. if not filtered_pre_result:
  663. return None
  664. #print("filtered_pre_result:", len(filtered_pre_result))
  665. #print(filtered_pre_result)
  666. # 视频已曝光过滤/白名单过滤
  667. st_viewed = time.time()
  668. t = [
  669. gevent.spawn(self.filter_video_viewed_new, filtered_pre_result),
  670. gevent.spawn(self.filter_movie_religion_video, filtered_pre_result)]
  671. gevent.joinall(t)
  672. filtered_result_list = [i.get() for i in t]
  673. #print("filtered_result_list1:",filtered_result_list[0])
  674. #print("filtered_result_list2:",filtered_result_list[1])
  675. filtered_viewed_set = set('')
  676. for i in filtered_result_list[0]:
  677. filtered_viewed_set.add(int(i))
  678. filter_video_set =set('')
  679. for j in filtered_result_list[1]:
  680. filter_video_set.add(int(j))
  681. filtered_viewed_result = list(filtered_viewed_set & filter_video_set)
  682. #print(f"view&m_r res: {filtered_viewed_result}\nexecute_time: {(time.time() - st_viewed) * 1000}")
  683. #print("filtered:",len(filtered_viewed_result))
  684. if not filtered_viewed_result:
  685. return None
  686. filtered_viewed_videos = [int(video_id) for video_id in filtered_viewed_result]
  687. #print("result:", filtered_viewed_videos)
  688. if flow_set is None:
  689. return filtered_viewed_videos
  690. else:
  691. # 流量池视频需过滤屏蔽视频
  692. if region_code is None or shield_config is None:
  693. return filtered_viewed_videos
  694. else:
  695. normal_recall_ids = []
  696. left_flow_ids = []
  697. for vid in filtered_viewed_videos:
  698. if vid in flow_set:
  699. left_flow_ids.append(vid)
  700. else:
  701. normal_recall_ids.append(vid)
  702. shield_key_name_list = shield_config.get(region_code, None)
  703. if shield_key_name_list is not None:
  704. filtered_shield_video_ids = self.filter_shield_video(
  705. video_ids=left_flow_ids, shield_key_name_list=shield_key_name_list
  706. )
  707. return normal_recall_ids+filtered_shield_video_ids
  708. else:
  709. return filtered_viewed_videos
  710. def filter_videos_status(self, pool_type='rov', region_code=None, shield_config=None):
  711. """视频过滤"""
  712. # todo: 添加app和region的风险过滤。
  713. st_viewed = time.time()
  714. videos_filtered = self.filter_videos_with_risk_video(self.video_ids, self.app_type, region_code)
  715. videos_filtered = self.filter_videos_with_festival(videos_filtered)
  716. # log_.info({
  717. # 'logTimestamp': int(time.time() * 1000),
  718. # 'pool_type': "zhangbo-filter-pool_type",
  719. # 'request_id': self.request_id,
  720. # 'app_type': self.app_type,
  721. # 'mid': "zhangbo-filter_videos_status",
  722. # 'uid': self.uid,
  723. # 'operation': 'shield_filter',
  724. # 'request_videos': self.video_ids,
  725. # 'shield_filter_result': videos_filtered,
  726. # 'executeTime': (time.time() - st_viewed) * 1000
  727. # })
  728. # 预曝光过滤
  729. st_pre = time.time()
  730. filtered_pre_result = self.filter_video_previewed(videos_filtered)
  731. # print("filtered_pre:", (time.time()-st_pre)*1000)
  732. # et_pre = time.time()
  733. # log_.info({
  734. # 'logTimestamp': int(time.time() * 1000),
  735. # 'request_id': self.request_id,
  736. # 'app_type': self.app_type,
  737. # 'mid': self.mid,
  738. # 'uid': self.uid,
  739. # 'operation': 'preview_filter',
  740. # 'request_videos': self.video_ids,
  741. # 'preview_filter_result': filtered_pre_result,
  742. # 'executeTime': (time.time() - st_pre) * 1000
  743. # })
  744. if not filtered_pre_result:
  745. return None
  746. # 视频状态过滤采用离线定时过滤方案
  747. # 视频状态过滤
  748. # st_status = time.time()
  749. # filtered_status_result = self.filter_video_status(video_ids=filtered_pre_result)
  750. # et_status = time.time()
  751. # log_.info('filter by video status: result = {}, execute time = {}ms'.format(
  752. # filtered_status_result, (et_status - st_status) * 1000))
  753. # if not filtered_status_result:
  754. # return None
  755. # 视频已曝光过滤
  756. st_viewed = time.time()
  757. filtered_viewed_result = self.filter_video_viewed_status(video_ids=filtered_pre_result, region_code=region_code)
  758. # print("filtered_pre:", (time.time() - st_viewed) * 1000)
  759. # et_viewed = time.time()
  760. # log_.info({
  761. # 'logTimestamp': int(time.time() * 1000),
  762. # 'pool_type': pool_type,
  763. # 'request_id': self.request_id,
  764. # 'app_type': self.app_type,
  765. # 'mid': self.mid,
  766. # 'uid': self.uid,
  767. # 'operation': 'view_filter',
  768. # 'request_videos': filtered_pre_result,
  769. # 'view_filter_result': filtered_viewed_result,
  770. # 'executeTime': (time.time() - st_viewed) * 1000
  771. # })
  772. if not filtered_viewed_result:
  773. return None
  774. filtered_viewed_videos = [int(video_id) for video_id in filtered_viewed_result]
  775. return filtered_viewed_videos
  776. # if pool_type == 'flow' or pool_type=='normal':
  777. # # 流量池视频需过滤屏蔽视频
  778. # if region_code is None or shield_config is None:
  779. # return filtered_viewed_videos
  780. # else:
  781. # shield_key_name_list = shield_config.get(region_code, None)
  782. # if shield_key_name_list is not None:
  783. # filtered_shield_video_ids = self.filter_shield_video(
  784. # video_ids=filtered_viewed_videos, shield_key_name_list=shield_key_name_list
  785. # )
  786. # log_.info({
  787. # 'logTimestamp': int(time.time() * 1000),
  788. # 'pool_type': pool_type,
  789. # 'request_id': self.request_id,
  790. # 'app_type': self.app_type,
  791. # 'mid': self.mid,
  792. # 'uid': self.uid,
  793. # 'operation': 'shield_filter',
  794. # 'request_videos': filtered_viewed_videos,
  795. # 'shield_filter_result': filtered_shield_video_ids,
  796. # 'executeTime': (time.time() - st_viewed) * 1000
  797. # })
  798. # # print("filtered_pre flow:", (time.time() - st_viewed) * 1000)
  799. # return filtered_shield_video_ids
  800. # else:
  801. # return filtered_viewed_videos
  802. # else:
  803. # return filtered_viewed_videos
  804. def filter_video_viewed_status(self, video_ids, region_code, types=(1, 6,)):
  805. """
  806. 调用后端接口过滤用户已观看视频
  807. :param video_ids: 视频id列表 type-list
  808. :param types: 过滤参数 type-tuple, 默认(1, )
  809. 1-已观看 2-视频状态 3-是否进入老年人社区 4-话题状态 5-推荐状态 6-白名单过滤 7-涉政视频过滤
  810. :return: filtered_videos
  811. """
  812. # 获取对应端的过滤参数types
  813. types = config_.FILTER_VIEWED_TYPES_CONFIG.get(self.app_type, None)
  814. if types is None:
  815. types = config_.FILTER_VIEWED_TYPES_CONFIG.get('other')
  816. types = list(types)
  817. types.append(2)
  818. request_data = {"appType": self.app_type,
  819. "mid": self.mid,
  820. "uid": self.uid,
  821. "types": types,
  822. "videoIds": video_ids,
  823. "cityCode": self.env_dict["cityCode"] if self.env_dict and "cityCode" in self.env_dict else "-1",
  824. "hotSenceType": self.env_dict["hotSenceType"] if self.env_dict and "hotSenceType" in self.env_dict else 0
  825. }
  826. # print(request_data)
  827. # 调用http接口
  828. result = request_post(request_url=config_.VIDEO_FILTER_URL, request_data=request_data, timeout=(0.2, 1))
  829. # print("result:", result)
  830. if result is None:
  831. # print("result is None")
  832. # log_.info('过滤失败,types: {}'.format(types))
  833. return []
  834. if result['code'] != 0:
  835. # log_.info('过滤失败,types: {}'.format(types))
  836. return []
  837. filtered_videos = result['data']
  838. return filtered_videos
  839. def filter_videos_with_risk_video(self, video_ids, app_type, region_code):
  840. # 0 用一个开关控制,是否过滤生效。 便于回滚功能。
  841. risk_filter_flag = self.risk_filter_flag
  842. if not risk_filter_flag:
  843. return self.truncation(video_ids)
  844. # 1 判断是否过滤,不展示的app+区域列表,-1必须过滤
  845. app_region_filtered = self.app_region_filtered
  846. if app_type in app_region_filtered.keys():
  847. if_filtered = False
  848. if region_code in app_region_filtered[app_type]:
  849. if_filtered = True
  850. else:
  851. if_filtered = True
  852. if not if_filtered:
  853. return self.truncation(video_ids)
  854. # 2 确认过滤,获取风险video列表param_update_risk_videos
  855. videos_with_risk = self.videos_with_risk
  856. # 3 过滤 返回结果
  857. video_ids_new = [i for i in video_ids if i not in videos_with_risk]
  858. # print(risk_filter_flag)
  859. # print(app_region_filtered)
  860. # print(video_ids)
  861. # print(app_type)
  862. # print(region_code)
  863. # print(videos_with_risk)
  864. # print(video_ids_new)
  865. # print(len(video_ids))
  866. # print(len(video_ids_new))
  867. return self.truncation(video_ids_new)
  868. def truncation(self, video_ids):
  869. if self.force_truncation is None:
  870. return video_ids
  871. else:
  872. return video_ids[:min(self.force_truncation, len(video_ids))]
  873. def filter_videos_with_festival(self, video_ids: List[int]):
  874. # 1 获取当前时间,判断过滤标准
  875. now_date = datetime.today()
  876. now_dt = datetime.strftime(now_date, '%Y%m%d%H')
  877. now_dt_int = int(now_dt)
  878. filter_fes = []
  879. for k, v1, v2 in FESTIVAL:
  880. if now_dt_int >= v1 and now_dt_int < v2:
  881. filter_fes.append(k)
  882. if len(filter_fes) == 0:
  883. return video_ids
  884. # 2 过滤
  885. redis_keys = ["alg_recsys_video_tags_" + str(id) for id in video_ids]
  886. redis_helper = RedisHelper()
  887. redis_values = redis_helper.get_batch_key(redis_keys)
  888. # print(str(video_ids))
  889. # print(str(redis_values))
  890. if redis_values and len(redis_values) > 0 and len(redis_values) == len(redis_keys):
  891. video_ids_new = []
  892. for id, tags in zip(video_ids, redis_values):
  893. flag = True
  894. if tags and len(tags) > 0:
  895. for t in tags.split(","):
  896. if t in filter_fes:
  897. flag = False
  898. break
  899. if flag:
  900. video_ids_new.append(id)
  901. return video_ids_new
  902. else:
  903. return video_ids
  904. if __name__ == '__main__':
  905. user = [
  906. ('weixin_openid_o0w175fDc8pNnywrYN49E341tKfI', ''),
  907. ('weixin_openid_o0w175YwC3hStzcR5DAQdbgzdMeI', ''),
  908. ('weixin_openid_o0w175ftZDl6VJVDx9la3WVPh7mU', '15900461'),
  909. ('weixin_openid_o0w175SPqpCVRcp7x1XvnX4qpIvI', '19659040'),
  910. ('weixin_openid_o0w175cOnguapyWIrDrHkOWl4oFQ', '31210128'),
  911. ('weixin_openid_o0w175UXYId-o71e1Q3SOheYNteQ', '33099722'),
  912. ('weixin_openid_o0w175QQ5b42AtOe50bchrFgcttA', ''),
  913. ('weixin_openid_o0w175bgaPlfLsp3YLDKWqLWtXX8', '35371534'),
  914. ('weixin_openid_o0w175eRpvbmV6nOhM1VTyyLICWA', '30488803'),
  915. ('weixin_openid_o0w175TZYvG47pQkOjyJFoxQuqsw', '')
  916. ]
  917. video_df = pd.read_csv('./data/videoids.csv')
  918. videoid_list = video_df['videoid'].tolist()
  919. for mid, uid in user:
  920. video_ids = random.sample(videoid_list, 1000)
  921. start_time = time.time()
  922. filter_ = FilterVideos(request_id=f'{mid} - {uid}', app_type=0, mid=mid, uid=uid, video_ids=video_ids)
  923. res = filter_.filter_videos_new()
  924. print(f"res: {res}\nexecute_time: {(time.time() - start_time) * 1000}")
  925. # filter_.filter_video_status(video_ids=[1, 3, 5])
  926. # videos = [{'videoId': 9034659, 'flowPool': '3#11#3#1637824188547'}, {'videoId': 9035052, 'flowPool': '3#11#3#1637824172827'}]
  927. # res = get_videos_remain_view_count(4, videos)
  928. # print(res)
  929. # text = '测试 @李倩'
  930. # send_msg_to_feishu(text)
  931. # update_video_w_h_rate(video_id=113, key_name='')
  932. # mid = "weixin_openid_obHDW5c4g3aULfCWh-68LcUSxCB"
  933. # request_url = f"{config_.GET_USER_30DayReturnCnt_URL}{mid}"
  934. # res = request_get(request_url=request_url, timeout=100)
  935. # res = get_user_has30day_return(mid=mid)
  936. # print(res, type(res))