Browse Source

add special mid recommend

liqian 2 years ago
parent
commit
22b9b1ba39
3 changed files with 94 additions and 0 deletions
  1. 7 0
      config.py
  2. 70 0
      recommend.py
  3. 17 0
      video_recall.py

+ 7 - 0
config.py

@@ -51,6 +51,7 @@ class BaseConfig(object):
     # abCode
     AB_CODE = {
         'initial': 10000,  # 初始
+        'special_mid': 99999,  # 特殊mid
         'w_h_rate': 10001,  # 视频宽高比实验(每组的前两个视频调整为横屏视频),已下线
         'position_insert': 10002,  # 按位置插入
         'relevant_video_op': 10003,  # 运营对某些视频给定一些相关视频,调整为对应视频相关推荐的头部
@@ -140,6 +141,7 @@ class BaseConfig(object):
         'top_video_relevant_appType_19': 'relevant_video',  # 相似视频
         'whole_movies': 'whole_movies',  # 完整影视
         'talk_videos': 'talk_videos',  # 影视解说
+        'special_mid': 'special_mid_videos',  # 特殊mid指定视频
     }
 
     # category id mapping
@@ -324,6 +326,11 @@ class BaseConfig(object):
     # 完整影视资源更新结果存放 redis key 前缀,完整格式:'com.weiqu.video.recall.whole.movies.item.{date}.{h}'
     RECALL_KEY_NAME_PREFIX_WHOLE_MOVIES = 'com.weiqu.video.recall.whole.movies.item.'
 
+    # 特殊mid更新结果存放 redis key ,完整格式:'com.weiqu.video.special.mid'
+    KEY_NAME_SPECIAL_MID = 'com.weiqu.video.special.mid'
+    # 特殊mid对应指定视频列表更新结果存放 redis key 前缀,完整格式:'com.weiqu.video.special.videos.item.{date}'
+    KEY_NAME_PREFIX_SPECIAL_VIDEOS = 'com.weiqu.video.special.videos.item.'
+
 
 class DevelopmentConfig(BaseConfig):
     """开发环境配置"""

+ 70 - 0
recommend.py

@@ -629,6 +629,12 @@ def video_homepage_recommend(request_id, mid, uid, size, app_type, algo_type, cl
 
     else:
         param_st = time.time()
+        # 特殊mid推荐处理
+        if mid in get_special_mid_list():
+            rank_result = special_mid_recommend(request_id=request_id, mid=mid, uid=uid, app_type=app_type, size=size)
+            return rank_result
+
+        # 普通mid推荐处理
         top_K, flow_pool_P, ab_code, rule_key, expire_time, no_op_flag, old_video_index = \
             get_recommend_params(ab_exp_info=ab_exp_info)
         log_.info({
@@ -698,6 +704,12 @@ def video_relevant_recommend(request_id, video_id, mid, uid, size, app_type, ab_
     :return: videos type-list
     """
     param_st = time.time()
+    # 特殊mid推荐处理
+    if mid in get_special_mid_list():
+        rank_result = special_mid_recommend(request_id=request_id, mid=mid, uid=uid, app_type=app_type, size=size)
+        return rank_result
+
+    # 普通mid推荐处理
     top_K, flow_pool_P, ab_code, rule_key, expire_time, no_op_flag, old_video_index = \
         get_recommend_params(ab_exp_info=ab_exp_info, page_type=page_type)
     log_.info({
@@ -751,6 +763,64 @@ def video_relevant_recommend(request_id, video_id, mid, uid, size, app_type, ab_
     return rank_result
 
 
+def special_mid_recommend(request_id, mid, uid, app_type, size,
+                          ab_code=config_.AB_CODE['special_mid'],
+                          push_from=config_.PUSH_FROM['special_mid'],
+                          expire_time=24*3600):
+    redis_helper = RedisHelper()
+    # 特殊mid推荐指定视频列表
+    pool_recall = PoolRecall(request_id=request_id, app_type=app_type,
+                             mid=mid, uid=uid, ab_code=ab_code)
+    # 获取相关redis key
+    special_key_name, redis_date = pool_recall.get_pool_redis_key(pool_type='special')
+    # 用户上一次在rov召回池对应的位置
+    last_special_recall_key = f'{config_.LAST_VIDEO_FROM_ROV_POOL_PREFIX}{app_type}.{mid}.{redis_date}'
+    value = redis_helper.get_data_from_redis(last_special_recall_key)
+    if value:
+        idx = redis_helper.get_index_with_data(special_key_name, value)
+        if not idx:
+            idx = 0
+        else:
+            idx += 1
+    else:
+        idx = 0
+
+    recall_result = []
+    # 每次获取的视频数
+    get_size = size * 5
+    # 记录获取频次
+    freq = 0
+    while len(recall_result) < size:
+        freq += 1
+        if freq > config_.MAX_FREQ_FROM_ROV_POOL:
+            break
+        # 获取数据
+        data = redis_helper.get_data_zset_with_index(key_name=special_key_name,
+                                                     start=idx, end=idx + get_size - 1,
+                                                     with_scores=True)
+        if not data:
+            break
+        # 获取视频id,并转换类型为int,并存储为key-value{videoId: score}
+        # 添加视频源参数 pushFrom, abCode
+        temp_result = [{'videoId': int(value[0]), 'rovScore': value[1],
+                        'pushFrom': push_from, 'abCode': ab_code}
+                       for value in data]
+        recall_result.extend(temp_result)
+        # 将此次获取的末位视频id同步刷新到Redis中,方便下次快速定位到召回位置,过期时间为1天
+        if mid:
+            # mid为空时,不做记录
+            redis_helper.set_data_to_redis(key_name=last_special_recall_key, value=data[-1][0], expire_time=expire_time)
+        idx += get_size
+
+    return recall_result[:size]
+
+
+def get_special_mid_list():
+    redis_helper = RedisHelper()
+    special_mid_list = redis_helper.get_data_from_set(key_name=config_.KEY_NAME_SPECIAL_MID)
+    return special_mid_list
+
+
 if __name__ == '__main__':
     videos = [
         {"videoId": 10136461, "rovScore": 99.971, "pushFrom": "recall_pool", "abCode": 10000},

+ 17 - 0
video_recall.py

@@ -630,6 +630,23 @@ class PoolRecall(object):
         elif pool_type == 'flow':
             return config_.FLOW_POOL_KEY_NAME_PREFIX + str(self.app_type)
 
+        elif pool_type == 'special':
+            key_name_prefix = config_.KEY_NAME_PREFIX_SPECIAL_VIDEOS
+            # 判断列表是否更新,未更新则使用前一天的列表
+            key_name = key_name_prefix + time.strftime('%Y%m%d')
+            if self.redis_helper.key_exists(key_name):
+                redis_date = date.today().strftime('%Y%m%d')
+            else:
+                redis_date = (date.today() - timedelta(days=1)).strftime('%Y%m%d')
+                key_name = key_name_prefix + redis_date
+                # 判断当前时间是否晚于更新时间,发送消息到飞书
+                now_h = datetime.now().hour
+                feishu_text = '{} —— 今日special mid 数据未按时更新,请及时查看解决。'.format(config_.ENV_TEXT)
+                if now_h > config_.ROV_UPDATE_H:
+                    send_msg_to_feishu(feishu_text)
+
+            return key_name, redis_date
+
         else:
             log_.error('pool type error')
             return None, None