import json
import logging
import traceback
import random
from collections import Counter
from libs.cache import redis_client2
from libs.es import es_index_adapt, es_msearch, es_query
from libs.error import logging_exception
from django.conf import settings
from search.utils.service import recommed_service_category_device_id, recommed_service_category_device_id_v2


def common_judge(data_type, cl_id, latest_tag_num,current_city_tag_id):
    try:
        device_latest_action_key = "device:latest:action:tag:names:update:flag" + str(cl_id)
        device_status = redis_client2.hgetall(device_latest_action_key)
        device_status_dict = {str(i, 'utf-8'): json.loads(device_status[i]) for i in device_status}
        user_portrait_is_change = device_status_dict.get("user_portrait_is_change_"+data_type, -1)
        latest_action_is_change = device_status_dict.get("latest_action_is_change_"+data_type, -1)
        data_type_hot_query_status = device_status_dict.get("data_type_hot_query_status_"+data_type, -1)
        latest_action_tags = device_status_dict.get("latest_action_tags", [])
        last_city_id = device_status_dict.get("last_city_id_"+data_type,-1)
        logging.info("device:%s:latest:action:tags:update:flag:%s" % (cl_id, device_status_dict))


        if last_city_id!=-1:
            if last_city_id != current_city_tag_id:
                user_portrait_is_change = 1 if user_portrait_is_change != -1 else 0
                latest_action_is_change = 1 if latest_action_is_change != -1 else 0
                data_type_hot_query_status = 1 if data_type_hot_query_status != -1 else 0
                redis_client2.hset(device_latest_action_key, "last_city_id_"+data_type, current_city_tag_id)
        else:
            user_portrait_is_change = 0 if user_portrait_is_change==-1 else user_portrait_is_change
            latest_action_is_change = 0 if latest_action_is_change==-1 else latest_action_is_change
            data_type_hot_query_status = 0 if data_type_hot_query_status==-1 else data_type_hot_query_status
            redis_client2.hset(device_latest_action_key,"last_city_id_"+data_type,current_city_tag_id)

        if latest_action_tags:
            if user_portrait_is_change:
                redis_client2.hset(device_latest_action_key, "user_portrait_is_change_"+data_type, 0)
                redis_client2.hset(device_latest_action_key, "latest_action_is_change_" + data_type, 0)
                # 需要获取画像tag_list + 最新行为tag_list，请求es
                return 1, latest_action_tags[:latest_tag_num]
            if latest_action_is_change:
                redis_client2.hset(device_latest_action_key, "latest_action_is_change_"+data_type, 0)
                # 需要获取最新行为tag_list，请求es
                return 2, latest_action_tags[:latest_tag_num]
            if not data_type_hot_query_status:
                redis_client2.hset(device_latest_action_key, "data_type_hot_query_status_" + data_type, 1)
                # 需要获取热搜词list，请求es
                return 3, latest_action_tags[:latest_tag_num]
            # 不需要请求es
            return -1, latest_action_tags[:latest_tag_num]
        else:
            if not data_type_hot_query_status:
                redis_client2.hset(device_latest_action_key, "data_type_hot_query_status_" + data_type, 1)
                # 需要获取画像list(有画像但是没有行为)/或者获取热搜词list，请求es
                return 3, latest_action_tags[:latest_tag_num]
            # 不需要请求es
            return -1, latest_action_tags[:latest_tag_num]
    except:
        logging.error("common_judge catch exception,err_msg:%s" % traceback.format_exc())
        logging_exception()
        return -1,list()


def get_user_service_portrait2_redis(cl_id, portrait_tag_num=4, data_type="diary", latest_tag_num=1,current_city_tag_id=-1):
    try:
        portrait_exist = False
        common_status, latest_action_tags = common_judge(data_type=data_type, cl_id=cl_id, latest_tag_num=latest_tag_num,current_city_tag_id=current_city_tag_id)
        logging.info("get_user_service_portrait2_redis,cl_id:%s,data_type:%s,current_city_tag_id:%s,common_status:%s,latest_action_tags:%s" % (str(cl_id), str(data_type),str(current_city_tag_id),str(common_status),str(latest_action_tags)))
        cl_id_portrait_key = "user:service_portrait_tags3:cl_id:" + str(cl_id)

        if common_status == 3:
            if redis_client2.exists(cl_id_portrait_key):
                user_portrait = redis_client2.hgetall(cl_id_portrait_key)
                user_portrait = {str(tag, 'utf-8'): float(score) for tag, score in user_portrait.items()}
                top_n = Counter(user_portrait).most_common(portrait_tag_num)
                top_n_tags = [tag_info[0] for tag_info in top_n]
                portrait_exist = True
                return True, top_n_tags, [], portrait_exist
            # 用户冷启动
            portrait_exist = False
            return False, [], [], portrait_exist
        if common_status == 1:
            user_portrait = redis_client2.hgetall(cl_id_portrait_key)
            user_portrait = {str(tag, 'utf-8'): float(score) for tag, score in user_portrait.items()}
            top_n = Counter(user_portrait).most_common(portrait_tag_num)
            top_n_tags = [tag_info[0] for tag_info in top_n]
            logging.info("user_service_portrait2_redis:cl_id:%s:top_n_tags:%s" % (str(cl_id),str(top_n_tags)))
            portrait_exist = True
            return True, top_n_tags, latest_action_tags, portrait_exist
        if common_status == 2:
            portrait_exist = True
            return True, [], latest_action_tags, portrait_exist
        if common_status == -1:
            if redis_client2.exists(cl_id_portrait_key):
                portrait_exist = True
                return False, [], [], portrait_exist
        return False, [], [], portrait_exist
    except:
        logging.error("catch exception,err_msg:%s" % traceback.format_exc())
        logging_exception()
        return False, [], [], False


def get_user_portrait_explore_tags(user_tags):
    """
    :return:set(tag_id)
    """
    try:
        gm_tag_cf_tags_key = "gm:tag:cf:tags"
        all_tag_cf_tags = redis_client2.hgetall(gm_tag_cf_tags_key)
        all_tag_cf_tags_dict = {str(i, 'utf-8'): json.loads(all_tag_cf_tags[i]) for i in all_tag_cf_tags}
        cf_tags = []
        for user_tag in user_tags:
            cf_tags.extend(all_tag_cf_tags_dict.get(user_tag, [])[:5])
        if not cf_tags:
            order_tags_key = "one:month:history:order:tag:names:"
            data = redis_client2.get(order_tags_key)
            data = str(data, 'utf-8').split(',')
            cf_tags = data[:25]
        return cf_tags
    except:
        logging.error("catch exception,err_msg:%s" % traceback.format_exc())
        logging_exception()
        return list()


def get_explore_tags_diary_id(tag_names, read_list, current_city_id, device_id, size=10, is_cpc=False):
    try:
        q = dict()
        if not is_cpc:
            sort_list = [
                {'_script': {
                    'lang': settings.ES_SCRIPT_LANG,
                    'script_file': 'sort_diary-recommend',
                    'type': 'number',
                    'params': {
                        'user_city_tag_id': current_city_id,
                    },
                    'order': 'desc',
                    '_cache': True,
                }}
            ]
        else:
            sort_list = []
        if recommed_service_category_device_id_v2(device_id):
            sort_list += [
                {'has_video_cover': {'order': 'asc'}},
                {'offline_score': {'order': 'desc'}},
                {"good_click": {"order": "desc"}},
                {'last_update_time': {'order': 'desc'}}
            ]
        else:
            sort_list += [
                {'has_video_cover': {'order': 'asc'}},
                {'offline_score': {'order': 'desc'}},
                {"good_click": {"order": "desc"}},
                {'last_update_time': {'order': 'desc'}}
            ]

        total_query_should_list = []
        for tag_name in tag_names:
            term_dict = {
                "match_phrase": {
                    "tags": {
                        "query": tag_name
                    }
                }
            }
            total_query_should_list.append(term_dict)

        q['query'] = {"bool": {
            "filter": [{"term": {"is_online": True}},
                       {"term": {"has_cover": True}}, {"term": {"is_sink": False}},
                       {"term": {"has_after_cover": True}}, {"term": {"has_before_cover": True}},
                       {"terms": {"content_level": [5, 4, 3.5, 3]}}],
            "should": total_query_should_list,
            "minimum_should_match": 1}}
        q['query']['bool']['must_not'] = [{"term": {"is_operate": True}}]
        if is_cpc:
            q['query']['bool']['filter'].append({"term": {"is_promote": True}})
            if current_city_id != -1:
                q['query']['bool']['filter'].append({"term": {"diary_city": current_city_id}})
        if len(read_list) > 0:
            q['query']['bool']['must_not'].append({"terms": {"id": read_list}})
        q['sort'] = sort_list
        q["_source"] = {"includes": ["id"]}
        es_res = es_query("diary", q, offset=0, size=size)
        explore_diarys = []
        for diay_info in es_res['hits']['hits']:
            explore_diarys.append(diay_info['_source']['id'])
        return explore_diarys
    except:
        logging.error("get_explore_tags_diary_id catch exception,err_msg:%s" % traceback.format_exc())
        logging_exception()
        return list()


def get_portrait_tags_diary_id(device_diary_key, es_tags, user_latest_tag, user_tags, current_city_tag_id, read_list, device_id):
    query_body = ""
    diary_index_name = es_index_adapt(index_prefix=settings.ES_INDEX_PREFIX, doc_type="diary", rw="read")
    diary_header_dict = {'index': diary_index_name, 'type': "diary"}
    sort_list = [
        {'_script': {
            'lang': settings.ES_SCRIPT_LANG,
            'script_file': 'sort_diary-recommend',
            'type': 'number',
            'params': {
                'user_city_tag_id': current_city_tag_id,
            },
            'order': 'desc',
            '_cache': True,
        }}
    ]
    if recommed_service_category_device_id_v2(device_id):
        sort_list += [
            {'has_video_cover': {'order': 'asc'}},
            {'offline_score': {'order': 'desc'}},
            {"good_click": {"order": "desc"}},
            {'last_update_time': {'order': 'desc'}}
        ]
    else:
        sort_list += [
            {'has_video_cover': {'order': 'asc'}},
            {'offline_score': {'order': 'desc'}},
            {"good_click": {"order": "desc"}},
            {'last_update_time': {'order': 'desc'}}
        ]
    for i in es_tags:
        q = dict()
        q['query'] = {"bool": {
            "filter": [{"term": {"is_online": True}},
                       {"term": {"has_cover": True}}, {"term": {"is_sink": False}},
                       {"term": {"has_after_cover": True}}, {"term": {"has_before_cover": True}},
                       {"terms": {"content_level": [5, 4, 3.5, 3]}}]}}
        multi_fields_search = [
            {"match_phrase": {"tags": i}},
            {"term": {"doctor.name": i}},
            {"term": {"doctor.hospital.name": i}},
            {"term": {"doctor.hospital.officer_name": i}},
            {"term": {"user.last_name": i}},
            {"term": {"service.name": i}}
        ]
        q["query"]["bool"]["should"] = [multi_fields_search]
        q["query"]["bool"]["minimum_should_match"] = 1
        q['size'] = 100
        q["_source"] = {
            "includes": ["id"]
        }
        q['query']['bool']['must_not'] = [{"term": {"is_operate": True}}]
        if len(read_list) > 0:
            q['query']['bool']['must_not'].append({"terms": {"id": read_list}})
        q['sort'] = sort_list
        query_body += "{}\n{}\n".format(json.dumps(diary_header_dict), json.dumps(q))
    res_diary = es_msearch(query_body)
    if res_diary:
        tag_index = 0
        portrait_index = 1
        all_diary_id = set()
        for tag in res_diary['responses']:
            diary_id_list = [es_tags[tag_index]]
            for diary in tag['hits']['hits']:
                diary_id = diary['_source']['id']
                if diary_id not in all_diary_id:
                    diary_id_list.append(diary_id)
                    all_diary_id.add(diary_id)
            if es_tags[tag_index] in user_latest_tag:
                redis_client2.hset(device_diary_key, "tag0", json.dumps(diary_id_list))
                redis_client2.hset(device_diary_key, "tag0_cursor", 1)
                tag_index += 1
            elif es_tags[tag_index] in user_tags:
                redis_client2.hset(device_diary_key, "tag" + str(portrait_index), json.dumps(diary_id_list))
                redis_client2.hset(device_diary_key, "tag" + str(portrait_index) + "_cursor", 1)
                portrait_index += 1
                tag_index += 1
            else:
                tag_index += 1
                continue
        if user_tags:
            # 插入探索的tag
            explore_tags = get_user_portrait_explore_tags(user_tags)
            read_list = read_list + list(all_diary_id)
            explore_diarys = get_explore_tags_diary_id(explore_tags, read_list, current_city_tag_id, device_id, size=200, is_cpc=False)
            redis_client2.hset(device_diary_key, 'tag200', json.dumps(explore_diarys))
            redis_client2.hset(device_diary_key, "tag200_cursor", 0)


def fetch_diary_by_user_portrait(device_id, current_city_tag_id, read_list, size):
    """
    :param device_id: 设备id
    :param current_city_tag_id: 设备当前的城市tag id
    :param read_list: 召回的过滤卡片list
    :param size: 召回个数
    召回逻辑:
        画像：5
        最新行为：1/0
        探索：2
        日记：12篇
        7 ：
            tag：a1 b1 a2 a3 b2 a4 a5
            diary：a1 2   b1 1    a2 2   a3 1    b2 1    a4 2    a5 1
        8：
            tag：new_a a1 b1 a2 a3 b2 a4 a5
            diary：new_a 2  a1 1   b1 1    a2 1   a3 2    b2 1    a4 1    a5 1
    :return: list
    """
    try:
        device_diary_key = 'feed:recommend:device_id:' + device_id + ':type:' + 'diary'
        if_es, user_tags, user_latest_tag, portrait_exist = get_user_service_portrait2_redis(cl_id=device_id, portrait_tag_num=5,
                                                                             data_type="diary", latest_tag_num=1,current_city_tag_id=current_city_tag_id)
        if if_es:
            es_tags = user_latest_tag + user_tags
            get_portrait_tags_diary_id(device_diary_key, es_tags, user_latest_tag, user_tags, current_city_tag_id, read_list, device_id)
        # 用户冷启动(没有画像)
        if not portrait_exist:
            # if recommed_service_category_device_id_v2(device_id):
            #     coldstart_diary_queue_key = "coldstart:light:clinic:beauty:diary:queue:offline:score:v1"
            # else:
            coldstart_diary_queue_key = "coldstart:light:clinic:beauty:diary:queue"
            coldstart_diary_queue = redis_client2.hget(coldstart_diary_queue_key, current_city_tag_id)
            if coldstart_diary_queue:
                coldstart_diary_queue = json.loads(coldstart_diary_queue)
            else:
                coldstart_diary_queue = []

            coldstart_cursor = redis_client2.hget(device_diary_key, 'tag300_cursor')
            if coldstart_cursor:
                coldstart_cursor = json.loads(coldstart_cursor)
            else:
                coldstart_cursor = 0

            result = coldstart_diary_queue[coldstart_cursor: coldstart_cursor+size]
            if coldstart_cursor+size > len(coldstart_diary_queue)-size-1:
                redis_client2.hset(device_diary_key, 'tag300_cursor', 0)
            else:
                redis_client2.hset(device_diary_key, 'tag300_cursor', coldstart_cursor+size)
            return result

        device_portrait_diary_queue = redis_client2.hgetall(device_diary_key)
        if device_portrait_diary_queue:
            device_portrait_diary_queue_dict = {str(i, 'utf-8'): json.loads(device_portrait_diary_queue[i]) for i in
                                                device_portrait_diary_queue}
            logging.info("test:device:%s:device_portrait_diary_queue_dict:%s" % (str(device_id), str(device_portrait_diary_queue_dict)))
            diary_result = []
            latest_diary_result = []
            if "tag0" in device_portrait_diary_queue_dict:
                tag0cursor = device_portrait_diary_queue_dict["tag0_cursor"]
                tag0_queue = device_portrait_diary_queue_dict["tag0"]
                latest_diary_result.extend(tag0_queue[tag0cursor:tag0cursor+2])
                if tag0cursor > len(tag0_queue)-3:
                    redis_client2.hset(device_diary_key, "tag0_cursor", 1)
                    device_portrait_diary_queue_dict["tag0_cursor"] = 1
                else:
                    redis_client2.hset(device_diary_key, "tag0_cursor", tag0cursor+2)
                    device_portrait_diary_queue_dict["tag0_cursor"] = tag0cursor+2
            for i in range(5):
                tag_field_name = "tag" + str(i+1)
                # 如果tag_cursor_name不存在，则用tag0_cursor(最新tag与画像前5tag重合)
                if tag_field_name not in device_portrait_diary_queue_dict:
                    tag_field_name = "tag0"
                tag_cursor_name = tag_field_name + "_cursor"
                cur_tag_cursor = device_portrait_diary_queue_dict[tag_cursor_name]
                tag_queue = device_portrait_diary_queue_dict[tag_field_name]

                if len(latest_diary_result) > 0:
                    if i == 2:
                        diary_result.extend(tag_queue[cur_tag_cursor:cur_tag_cursor+2])
                        if cur_tag_cursor > len(tag_queue)-3:
                            redis_client2.hset(device_diary_key, tag_cursor_name, 1)
                            device_portrait_diary_queue_dict[tag_cursor_name] = 1
                        else:
                            redis_client2.hset(device_diary_key, tag_cursor_name, cur_tag_cursor + 2)
                            device_portrait_diary_queue_dict[tag_cursor_name] = cur_tag_cursor+2
                    else:
                        diary_result.extend(tag_queue[cur_tag_cursor:cur_tag_cursor + 1])
                        if cur_tag_cursor > len(tag_queue)-2:
                            redis_client2.hset(device_diary_key, tag_cursor_name, 1)
                            device_portrait_diary_queue_dict[tag_cursor_name] = 1
                        else:
                            redis_client2.hset(device_diary_key, tag_cursor_name, cur_tag_cursor + 1)
                            device_portrait_diary_queue_dict[tag_cursor_name] = cur_tag_cursor+1

                else:
                    if i in [0, 1, 3]:
                        diary_result.extend(tag_queue[cur_tag_cursor:cur_tag_cursor+2])
                        if cur_tag_cursor > len(tag_queue)-3:
                            redis_client2.hset(device_diary_key, tag_cursor_name, 1)
                            device_portrait_diary_queue_dict[tag_cursor_name] = 1
                        else:
                            redis_client2.hset(device_diary_key, tag_cursor_name, cur_tag_cursor + 2)
                            device_portrait_diary_queue_dict[tag_cursor_name] = cur_tag_cursor+2
                    else:
                        diary_result.extend(tag_queue[cur_tag_cursor:cur_tag_cursor + 1])
                        if cur_tag_cursor > len(tag_queue)-2:
                            redis_client2.hset(device_diary_key, tag_cursor_name, 1)
                            device_portrait_diary_queue_dict[tag_cursor_name] = 1
                        else:
                            redis_client2.hset(device_diary_key, tag_cursor_name, cur_tag_cursor + 1)
                            device_portrait_diary_queue_dict[tag_cursor_name] = cur_tag_cursor+1

            diary_result = latest_diary_result + diary_result

            if "tag200" in device_portrait_diary_queue_dict:
                cursor_field = "tag200_cursor"
                tag_field = "tag200"
                cur_tag_cursor = device_portrait_diary_queue_dict[cursor_field]
                tag_queue = device_portrait_diary_queue_dict[tag_field]
                if len(latest_diary_result) == 0:
                    diary_result.insert(2, tag_queue[cur_tag_cursor:cur_tag_cursor+1][0])
                    cur_tag_cursor = cur_tag_cursor + 1
                    diary_result.insert(6, tag_queue[cur_tag_cursor:cur_tag_cursor+1][0])
                    cur_tag_cursor = cur_tag_cursor + 1
                    if cur_tag_cursor > len(tag_queue)-2:
                        redis_client2.hset(device_diary_key, cursor_field, 0)
                        device_portrait_diary_queue_dict[cursor_field] = 0
                    else:
                        redis_client2.hset(device_diary_key, cursor_field, cur_tag_cursor)
                        device_portrait_diary_queue_dict[cursor_field] = cur_tag_cursor
                else:
                    diary_result.insert(3, tag_queue[cur_tag_cursor:cur_tag_cursor+1][0])
                    cur_tag_cursor = cur_tag_cursor + 1
                    diary_result.insert(7, tag_queue[cur_tag_cursor:cur_tag_cursor+1][0])
                    cur_tag_cursor = cur_tag_cursor + 1
                    if cur_tag_cursor > len(tag_queue)-2:
                        redis_client2.hset(device_diary_key, cursor_field, 0)
                        device_portrait_diary_queue_dict[cursor_field] = 0
                    else:
                        redis_client2.hset(device_diary_key, cursor_field, cur_tag_cursor)
                        device_portrait_diary_queue_dict[cursor_field] = cur_tag_cursor
            return diary_result[:size]
        else:
            return list()
    except:
        logging.error("catch exception,err_msg:%s" % traceback.format_exc())
        logging_exception()
        return list()


def fetch_qa_by_user_portrait(device_id, read_list, size):
    """
    :param device_id: 设备id
    :param current_city_tag_id: 设备当前的城市tag id
    :param read_list: 召回的过滤卡片list
    :param size: 召回个数
    :return:
    """
    try:
        is_es_query, user_tags, user_latest_tag, portrait_exist = get_user_service_portrait2_redis(device_id, portrait_tag_num=3, data_type="qa",latest_tag_num=1)

        # 获取用户画像以及最新行为对应的tag
        device_qa_key = 'feed:recommend:device_id:' + device_id + ':type:' + 'qa'
        logging.info("gyz add fetch_qa_debug:%s:%s:%s:%s:%s" % (str(device_id), str(is_es_query), str(user_tags), str(user_latest_tag), str(portrait_exist)))

        if is_es_query:
            if user_latest_tag:
                user_tags = user_latest_tag + user_tags[:2]
            request_es_qa_data(user_tags, read_list, device_qa_key, device_id)

        if not portrait_exist:
            if recommed_service_category_device_id_v2(device_id):
                coldstart_qa_queue_key = "coldstart:light:clinic:beauty:qa:queue:grey"
            else:
                coldstart_qa_queue_key = "coldstart:light:clinic:beauty:qa:queue"

            coldstart_cursor = redis_client2.hget(device_qa_key, 'tag300_cursor')
            if coldstart_cursor:
                coldstart_cursor = json.loads(coldstart_cursor)
            else:
                coldstart_cursor = 0

            result = redis_client2.lrange(coldstart_qa_queue_key, coldstart_cursor, coldstart_cursor+size-1)
            result = [int(qa_id) for qa_id in result]
            if coldstart_cursor+size > redis_client2.llen(coldstart_qa_queue_key)-size:
                redis_client2.hset(device_qa_key, 'tag300_cursor', 0)
            else:
                redis_client2.hset(device_qa_key, 'tag300_cursor', coldstart_cursor + size)
            return result

        result = get_qa_data_by_redis(device_qa_key, 3)
        return result
    except:
        logging.error("catch exception,err_msg:%s" % traceback.format_exc())
        logging_exception()
        return list()


def fetch_topic_by_user_portrait(device_id, read_list, size):
    try:
        is_es_query, user_tags, user_latest_tag, portrait_exist = get_user_service_portrait2_redis(device_id,
                                                                                   portrait_tag_num=5,
                                                                                   data_type="topic",
                                                                                   latest_tag_num=1)
        device_topic_key = 'feed:recommend:topic:device_id:' + device_id
        if is_es_query:
            if user_latest_tag:
                user_tags = user_latest_tag + user_tags
                request_es_topic_data(device_id, user_tags, read_list, device_topic_key)

        if not portrait_exist:
            if recommed_service_category_device_id(device_id):
                coldstart_topic_queue_key = "coldstart:light:clinic:beauty:topic:queue:grey"
            else:
                coldstart_topic_queue_key = "coldstart:light:clinic:beauty:topic:queue"

            coldstart_cursor = redis_client2.hget(device_topic_key, 'tag300_cursor')
            if coldstart_cursor:
                coldstart_cursor = json.loads(coldstart_cursor)
            else:
                coldstart_cursor = 0

            result = redis_client2.lrange(coldstart_topic_queue_key, coldstart_cursor, coldstart_cursor+size-1)
            result = [int(topic_id) for topic_id in result]
            if coldstart_cursor+size > redis_client2.llen(coldstart_topic_queue_key)-size:
                redis_client2.hset(device_topic_key, 'tag300_cursor', 0)
            else:
                redis_client2.hset(device_topic_key, 'tag300_cursor', coldstart_cursor + size)
            return result

        result = get_topic_by_redis(device_topic_key)
        return result
    except:
        logging.error("catch exception,err_msg:%s" % traceback.format_exc())
        logging_exception()
        return list()


def request_es_topic_data(device_id, user_tags, read_list, name):
    # 计算每个tag召回日记卡片的数量
    card_size = list()
    tag_size = len(user_tags)
    if tag_size:
        if tag_size == 5:
            card_size = [2, 1, 1, 1, 2]
        else:
            card_size = [1, 2, 1, 1, 1, 1]
    if card_size:
        query_body = ""
        index_name = es_index_adapt(index_prefix=settings.ES_INDEX_PREFIX, doc_type="tractate", rw="read")
        header_dict = {'index': index_name, 'type': "tractate"}
        for i in range(tag_size):
            q = dict()
            q['query'] = {"bool": {
                "filter": [{"term": {"is_online": True}}, {"terms": {"content_level": [5, 4, 3.5, 3]}}]}}
            q['size'] = 100
            q["_source"] = {
                "includes": ["id"]
            }
            multi_fields_search = [
                {"term": {"content": user_tags[i]}},
                {"match_phrase": {"tractate_tag_name": user_tags[i]}},
                {"term": {"tractate_tag_name_content": user_tags[i]}},
                {"term": {"author": user_tags[i]}},
                {"match_phrase": {"fresh_tractate_tag_name": user_tags[i]}},
                {"term": {"fresh_tractate_tag_name_content": user_tags[i]}}
            ]
            q["query"]["bool"]["should"] = [multi_fields_search]
            q["query"]["bool"]["minimum_should_match"] = 1
            if len(read_list) > 0:
                q['query']['bool']['must_not'] = [{"terms": {"id": read_list}}]

            if recommed_service_category_device_id(device_id):
                q["sort"] = [
                    {"is_video": {"order": "asc"}},
                    {"tractate_score": {"order": "desc"}},
                    {"good_click": {"order": "desc"}}
                ]
            else:
                q["sort"] = [
                    {"is_video": {"order": "asc"}},
                    {"tractate_score": {"order": "desc"}},
                    {"good_click": {"order": "desc"}}
                ]
            query_body += "{}\n{}\n".format(json.dumps(header_dict), json.dumps(q))
        res_diary = es_msearch(query_body)
        if res_diary:
            topic_set = set()
            for i in range(len(res_diary['responses'])):
                card_id_list = list()
                tag = res_diary['responses'][i]
                for diary in tag['hits']['hits']:
                    diary_id = diary['_source']['id']
                    if diary_id not in topic_set:
                        card_id_list.append(int(diary_id))
                        topic_set.add(diary_id)

                redis_client2.hset(name=name, key='tag' + str(i), value=json.dumps(card_id_list))
                redis_client2.hset(name=name, key='cursor' + str(i), value=0)


def get_topic_by_redis(name):
    result = []
    redis_result_1 = redis_client2.hgetall(name=name)
    redis_result = {str(i, 'utf-8'): json.loads(redis_result_1[i]) for i in redis_result_1}
    num = len(redis_result)//2  # 去掉tag300_cursor的影响
    if redis_client2.exists(name):
        for i in range(num):
            cursor = redis_result['cursor' + str(i)]
            data_list = redis_result['tag' + str(i)]

            get_num = 1
            if num == 5 and (i == 0 or i == 4):
                get_num = 2
            if num == 6 and i == 1:
                get_num = 2
            data = data_list[cursor:(cursor + get_num)]
            if len(data) < get_num:
                cursor = 0
            else:
                cursor = cursor + get_num

            if cursor >= len(data_list) - 1:
                cursor = 0

            redis_client2.hset(name=name, key='cursor' + str(i), value=cursor)

            result = result + data

            logging.info("get_topic_by_redis:name:" + str(name) + ":tag" + str(i) + ":result_list:" + str(result))
    else:
        logging.info("get_topic_by_redis:name:" + str(name) + ":this key not exist")

    return result


def get_qa_data_by_redis(name, num):
    result = []
    for i in range(num):
        redis_result_1 = redis_client2.hgetall(name=name)
        redis_result = {str(i, 'utf-8'): json.loads(redis_result_1[i]) for i in redis_result_1}
        if redis_client2.exists(name):
            cursor = int(redis_result['cursor'+str(i)])
            data_list = redis_result['tag'+str(i)]

            get_num = 1

            data = data_list[cursor:(cursor + get_num)]
            if len(data) < get_num:
                cursor = get_num - len(data)
                data = data + data_list[0:cursor]
            else:
                cursor = cursor + get_num

            if cursor >= len(data_list) - 1:
                cursor = 1
            redis_result['cursor'] = cursor

            redis_client2.hset(name=name, key='cursor'+str(i), value=cursor)

            result = result + data

            logging.info("get_feed_data_by_redis:name:" + str(name) + ":tag" + str(i) + ":result_list:" + str(result))
        else:
            logging.info("get_feed_data_by_redis:name:" + str(name) + ":tag:" + str(i) + ":this tag have no data")
    return result


def request_es_qa_data(user_tags, read_list, name, device_id):
    tag_size = len(user_tags)
    query_body = ""
    diary_index_name = es_index_adapt(index_prefix=settings.ES_INDEX_PREFIX, doc_type="answer", rw="read")
    diary_header_dict = {'index': diary_index_name, 'type': "answer"}

    if recommed_service_category_device_id_v2(device_id):
        sort_list = [
            {'has_picture': {'order': 'desc'}},
            {'smart_rank_v2': {'order': 'desc'}},
            {"good_click": {"order": "desc"}}
        ]
    else:
        sort_list = [
            {'has_picture': {'order': 'desc'}},
            {'smart_rank_v2': {'order': 'desc'}}
        ]

    for i in range(tag_size):
        q = dict()
        q['query'] = {"bool": {
            "filter": [{
                        "range": {
                            "content_length": {
                                "gte": 30
                            }
                        }
                       },
                {"term": {"is_online": True}},
                {"terms": {"content_level": ['5', '4', '3.5', '3']}
                 }]
            }
        }
        multi_fields_search = [
            {"term": {"title": user_tags[i]}},
            {"term": {"desc": user_tags[i]}},
            {"term": {"answer": user_tags[i]}},
            {"match_phrase": {"tag_name": user_tags[i]}}
        ]
        q["query"]["bool"]["should"] = [multi_fields_search]
        q["query"]["bool"]["minimum_should_match"] = 1
        q['size'] = 100
        q["_source"] = {
            "includes": ["id"]
        }
        if len(read_list) > 0:
            q['query']['bool']['must_not'] = [{"terms": {"id": read_list}}]
        q['sort'] = sort_list
        query_body += "{}\n{}\n".format(json.dumps(diary_header_dict), json.dumps(q))
    res_diary = es_msearch(query_body)
    # logging.info("fetch_qa_by_user_portrait:device_id:{0}:query_body:{1}:res_diary:{2}".format(device_id, query_body, res_diary))
    if res_diary:
        qa_set = set()
        for i in range(len(res_diary['responses'])):
            tag = res_diary['responses'][i]
            card_id_list = list()
            for diary in tag['hits']['hits']:
                diary_id = diary['_source']['id']
                if diary_id not in qa_set:
                    card_id_list.append(int(diary_id))
                    qa_set.add(int(diary_id))

            redis_client2.hset(name=name, key='tag'+str(i), value=json.dumps(card_id_list))
            redis_client2.hset(name=name, key='cursor'+str(i), value=0)


def get_device_portrait_list(device, portrait_tag_num):
    device_portrait_key = "user:service_portrait_tags3:cl_id:" + str(device)
    if redis_client2.exists(device_portrait_key):
        user_portrait = redis_client2.hgetall(device_portrait_key)
        user_portrait = {str(tag, 'utf-8'): float(score) for tag, score in user_portrait.items()}
        top_n = Counter(user_portrait).most_common(portrait_tag_num)
        top_n_tags = [tag_info[0] for tag_info in top_n]
        return top_n_tags
    else:
        return list()
