def update_user_info(user_info, recommend_result):
    """ update recommend_result to user_info
        @return None
    """
    #@note:暂时不获取item_feature结构(不管是news还是item类,为了计算用户的feature和feature偏好的pv部分);
    #@note:item_id列表的push由外围世荣处保证
    for reason,item_list in recommend_result.reason2itemidlist.iteritems():
        add_reason(user_info, reason)
        for v in item_list:
            resource_type = ResourceType.get_resource_type(v.item_id)
            assert(resource_type)
            user_info.recent_resource_visit_info.setdefault(resource_type, VisitInfo())
            user_info.recent_resource_visit_info[resource_type].pv_count += 1
    #set online user_feature_reason and user resource visit info
    feature_list = ItemFeatureList()
    for reason,weight in user_info.recent_push_reason_info.iteritems():
        feature = feature_list.feature.add()
        feature.feature_name = reason
        feature.weight = weight
    uid = user_info.uid
    errCode = model.set_online_user_feature_reason(uid, feature_list.SerializeToString())
    if not errCode:
        logger.warn("fail to set_online_user_feature_reason, uid:%s" % uid)
    for resource_type,visit_info in user_info.recent_resource_visit_info.iteritems():
        errCode = model.set_online_user_resource_visitinfo(uid, resource_type, visit_info.SerializeToString())
        if not errCode:
            logger.warn("fail to set_online_user_resource_visitinfo, uid:%s, resource_type:%s" % (uid, resource_type))
    #feedback for show action
    update_user_feature_list_for_show_action(user_info, recommend_result)
def filter_and_compose_recommend_item_list(user_info, reason, item_id_list_proto, result):
    """ try set result[reason] = item_id_list_proto and compose result if no dedup, update user_info
        @return None
    """
    logger.debug("try to add recommend, reason:%s, item_id_list_proto:%s" % (reason, item_id_list_proto))
    if  not check_reason(user_info, reason):
        logger.debug("existing feature_type, ignore:%s" % reason)
        return 
    result_item_id_list = []
    resource_type = ResourceType.get_resource_type(reason)
    for v in item_id_list_proto.item_id:
        if v in user_info.recent_push_item_id_list:
            continue
        result_item_id_list.append(v)
        user_info.recent_push_item_id_list.add(v)
        max_recommend_item_count_per_reason = get_max_recommend_item_count_per_resource(resource_type)
        if len(result_item_id_list) >= max_recommend_item_count_per_reason:
            logger.debug("real_list size:%s, select part:%s" % (len(item_id_list_proto.item_id),
                        max_recommend_item_count_per_reason))
            break
    if not result_item_id_list:
        logger.info("all item id list has been pushed")
        logger.debug("all item id list has been pushed:%s" % item_id_list_proto)
        return
    if not result_item_id_list:
        return
    result_recommend_item = [RecommendItem(item_id=v) for v in result_item_id_list]
    result.add(reason, result_recommend_item)
    add_reason(user_info, reason)
    return
def raw_get_news_online_item_by_feature_list(user_info, resource_type, feature_name_list_proto,
        count, max_recommend_item_count_per_reason, fetch_more_times, backup_feature_count):
    """ talk with news online item interface by feature list, @return RecommendItemContainer
        @note: should filter visited feature in user_info for user_feature_list, and update user_info
    """
    result = RecommendItemContainer()
    input = FeatureNameLimitList()
    logger.debug("feature list proto:%s" % feature_name_list_proto)
    for i in feature_name_list_proto.feature:
        if not check_reason(user_info,  i.feature_name):
            continue
        #@note: temp logic to ignore some feature type
        continue_out = False
        for prefix in NEWS_IGNORE_FEATURE_NAME_PREFIX:
            if i.feature_name.startswith(prefix):
                logger.info("ignore feature:%s" % i.feature_name)
                continue_out = True
                break
        if continue_out:
            continue
        cur = input.feature_name_limit.add()
        cur.feature_name = i.feature_name
        cur.limit = int(count * max_recommend_item_count_per_reason * fetch_more_times)
        if len(input.feature_name_limit) >= count + backup_feature_count:
            logger.info("break for enough feature count:%s,feature list:%s" %
                    (len(input.feature_name_limit), input))
            break
        #logger.debug("feature_name:%s,limit:%s" % (cur.feature_name, cur.limit))
    if not input.feature_name_limit:
        logger.info("no new feature to get for news:%s, feature_list size:%s" % (resource_type, len(feature_name_list_proto.feature)))
        logger.debug("current feature_list :%s" % (feature_name_list_proto))
        return result
    try:
        #socket_out = urllib2.urlopen(NEWS_ONLINE_URL_SEARCH_BY_QUERY, urllib.urlencode(input.SerializeToString()))
        socket_out = urllib2.urlopen(NEWS_ONLINE_URL_SEARCH_BY_QUERY, input.SerializeToString(),
                timeout=online_clicklog_feedback.NEWS_TIMEOUT_SECONDS)
        return_data = socket_out.read()
    except:
        logger.error("fail to talk with online news server,featurelists:%s,exceptions:%s" %
                (input, traceback.format_exc()))
        return result
    try:
        output = NewsFeatureItemList()
        output.ParseFromString(return_data)
        logger.debug("get news online item list result,user_info:%s,result:%s" % (user_info, output))
    except:
        logger.error("bad parse news return value:%s" % traceback.format_exc())
        return result
    for reason_item_id_list in output.feature_item:
        if not len(reason_item_id_list.item_feature_info):
            continue
        reason = reason_item_id_list.feature_name
        resource_type = ResourceType.get_resource_type(reason)
        item_list = []
        for i in reason_item_id_list.item_feature_info:
            if i.item_id in user_info.recent_push_item_id_list:
                logger.debug("ignore existing item id:%s" % i.item_id)
                continue
            item = RecommendItem()
            item.item_id = i.item_id
            item.item_info_json = i.item_info
            item.feature_name_list = i.item_feature_list 
            item_list.append(item)
            user_info.recent_push_item_id_list.add(i.item_id)
            logger.info("item_list:%s" %item_list)
            if len(item_list) >= max_recommend_item_count_per_reason:
                logger.debug("real_list size:%s, select part:%s" % (len(reason_item_id_list.item_feature_info),
                            max_recommend_item_count_per_reason))
                break
        if not item_list or len(item_list) < max_recommend_item_count_per_reason:
            logger.info("not enough data, discard:%s" % (item_list))
            ignore_reason(user_info, reason)
            #fetch not enough data, discard this reason
            continue
        result.add(reason, item_list)
        add_reason(user_info, reason)
        if result.size() >= count:
            break
    if not result:
        logger.info("get empty get_news_online_item_by_feature_list")
    return result