#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
from collections import defaultdict
from itertools import chain
from operator import (
    attrgetter,
    itemgetter,
)

from django.db.models import Q
from django.conf import settings
from gm_types.gaia import (
    TAG_V3_TYPE,
)

from agile.models.tag import (
    TagV3,
    TagV3Relation,
    TagV3MapAttrTag,
    TagSynonym,
    TagMapOldTag,
    TagCategory,
    TagCategoryMapTag,
    TagMapTag,
)

from rpc.tool.dict_mixin import to_dict


class TagV3Control(object):

    @staticmethod
    def dfs_closure(initial_set, key_func, adj_enum_func, adj_batch_enum_func, exclude_init):
        assert adj_batch_enum_func or adj_enum_func
        stack = []
        result_dict = {}

        def add(n):
            key = key_func(n)
            if key in result_dict:
                return
            stack.append(n)
            result_dict[key] = n

        def add_list(node_list):
            for n in node_list:
                add(n)

        add_list(initial_set)

        while len(stack) > 0:
            if adj_batch_enum_func:
                next_nodes = adj_batch_enum_func(stack)
                stack = []
            else:
                node = stack.pop()
                next_nodes = adj_enum_func(node)
            add_list(next_nodes)

        if exclude_init:
            for node in initial_set:
                del result_dict[key_func(node)]

        return result_dict

    @staticmethod
    def dfs_parent_closure(initial_set, exclude_init, is_online_only=None):
        initial_set = list(initial_set)
        qs = TagV3Relation.objects.filter(is_online=True).select_related("parent")

        if is_online_only:
            initial_set = [tag for tag in initial_set if tag.is_online]
            qs = qs.filter(parent__is_online=True)

        adj_batch_enum_func = lambda tag_list: [
            rel.parent
            for rel in qs.filter(
                child_id__in=[
                    tag.id for tag in tag_list
                ]
            )
        ]

        return TagV3Control.dfs_closure(
            initial_set=initial_set,
            key_func=attrgetter('id'),
            adj_enum_func=None,
            adj_batch_enum_func=adj_batch_enum_func,
            exclude_init=exclude_init,
        )

    @staticmethod
    def dfs_child_closure(initial_set, exclude_init, is_online_only=None):
        initial_set = list(initial_set)
        qs = TagV3Relation.objects.filter(is_online=True).select_related("child")

        if is_online_only:
            initial_set = [tag for tag in initial_set if tag.is_online]
            qs = qs.filter(child__is_online=True)

        adj_batch_enum_func = lambda tag_list: [
            rel.child
            for rel in qs.filter(
                parent_id__in=[
                    tag.id for tag in tag_list
                ]
            )
        ]

        return TagV3Control.dfs_closure(
            initial_set=initial_set,
            key_func=attrgetter('id'),
            adj_enum_func=None,
            adj_batch_enum_func=adj_batch_enum_func,
            exclude_init=exclude_init,
        )

    @classmethod
    def get_ancestors(cls, initial_set, exclude_init, tag_type=None, is_online_only=None, only_need_app_display=False):
        """
        获取所有父类
        :param initial_set: obj对象
        :param exclude_init: 是否需要排除 initial_set
        :param tag_type:  某些类型的标签
        :param is_online_only:bool  是否在线
        :param only_need_app_display: bool  仅需要app外显的
        :return:
        """
        result = cls.dfs_parent_closure(
            initial_set=initial_set,
            exclude_init=exclude_init,
            is_online_only=is_online_only
        ).values()

        if only_need_app_display:
            result = list(filter(lambda item: item.is_display, result))

        if tag_type is not None:
            result = list(filter(lambda item: item.tag_type == tag_type, result))

        return result

    @classmethod
    def get_descendants(cls, initial_set, exclude_init, tag_type=None, is_online_only=None, only_need_app_display=False):
        """
        获取所有子类
        :param initial_set:
        :param exclude_init:
        :param tag_type:
        :param is_online_only:
        :param only_need_app_display: bool  仅需要app外显的
        :return:
        """
        result = cls.dfs_child_closure(
            initial_set=initial_set,
            exclude_init=exclude_init,
            is_online_only=is_online_only
        ).values()

        if only_need_app_display:
            result = list(filter(lambda item: item.is_display, result))

        if tag_type is not None:
            result = list(filter(lambda item: item.tag_type == tag_type, result))

        return result


class TagV3Service(object):
    base_query = Q(is_online=True)
    MAX_LENGTH = 100

    @staticmethod
    def get_objs_by_query_and_order(model_set, query, order_by=[]):
        """
        获取 query_set对象
        :param model_set:
        :param query:
        :param order_by:
        :return:
        """
        _queryset = model_set.objects.filter(
            query).order_by(*order_by)

        return _queryset

    @classmethod
    def list_second_appeal_attrs(cls, tag_ids):
        """获取标签的二级诉求"""

        items = list(
            TagV3MapAttrTag.objects.filter(tag_id__in=tag_ids).values("tag_attr_id", "tag_id")
        )
        tag_attr_ids = list(map(lambda item: item["tag_attr_id"], items))

        attrs = TagV3.objects.filter(pk__in=tag_attr_ids, tag_type=TAG_V3_TYPE.SECOND_APPEAL).in_bulk(tag_attr_ids)

        res = defaultdict(list)
        for item in items:
            attr = attrs.get(item["tag_attr_id"])
            if not attr:
                continue
            res[item["tag_id"]].append(attr)

        return res

    @classmethod
    def list_tags_by_attrs(cls, attr_ids):
        """根据属性获取关联的标签"""
        tag_ids = list(
            TagV3MapAttrTag.objects.filter(tag_attr_id__in=attr_ids).values_list("tag_id", flat=True)
        )
        return cls.get_tags_v3_by_ids(tag_ids)

    @classmethod
    def list_normal_tags_by_attrs(cls, attr_ids):
        """根据属性获取关联的标签"""
        tag_ids = list(
            TagV3MapAttrTag.objects.filter(
                tag_attr_id__in=attr_ids,
                is_online=True,
            ).values_list("tag_id", flat=True)
        )

        #从关联的标签中获取类型是普通标签的标签
        normal_tag_ids = list(TagV3.objects.filter(
            id__in=tag_ids,
            tag_type=TAG_V3_TYPE.NORMAL,
        ).values_list("id", flat=True))

        return cls.get_tags_v3_by_ids(normal_tag_ids)

    @classmethod
    def get_tags_v3_by_ids(cls, ids):
        """
        通过新标签ids 获取新标签的基础数据
        :param ids:
        :return: {tag_v3_id: {"id": x, "name": xx, "tag_type": x}, }
        """
        if not ids:
            return {}

        query = cls.base_query & Q(pk__in=ids)
        tags = cls.get_objs_by_query_and_order(
            model_set=TagV3,
            query=query
        )

        return {
            tag.id: to_dict(
                tag,
                fields=["id", "name", "tag_type"]
            ) for tag in tags
        }

    @classmethod
    def get_mapping_tag_ids(cls, ids, is_new=False):
        """
        获取映射的标签id
        :param ids: [ids]
        :param is_new: 布尔值，判断是否获取新老标签的映射标签
        :return:
        """
        if is_new:
            query = cls.base_query & Q(tag_id__in=ids)
        else:
            query = cls.base_query & Q(old_tag_id__in=ids)

        objs = cls.get_objs_by_query_and_order(
            model_set=TagMapOldTag,
            query=query
        )
        return objs.values_list("tag_id", "old_tag_id")

    @classmethod
    def get_attr_tag_info_by_attr_ids(cls, ids, attr_types=None):
        """
        属性标签信息
        :param ids:[ids] 属性标签id
        :param attr_types 属性标签类型;list结构 查某些属性的标签
        :return:
        """
        query = cls.base_query & Q(pk__in=ids)
        if attr_types:
            query &= Q(tag_type__in=attr_types)

        attr_tags = cls.get_objs_by_query_and_order(
            model_set=TagV3,
            query=query
        )
        return {
            attr_tag.id: to_dict(
                attr_tag,
                fields=["id", "name", "tag_type"]
            ) for attr_tag in attr_tags
        }

    @classmethod
    def get_parents_tags(cls, child_ids, child_id_type, parent_id_type, need_app_display=False):
        """
        通过子标签获取父标签的关联关系
        :param child_ids: [] 子标签id
        :param child_id_type: 子标签类型
        :param parent_id_type: 主标签类型
        :param need_app_display: 需要app外显属性的标签，默认是全部标签
        :return:
        """
        query = Q(
            is_online=True,
            parent__is_online=True,
            parent__tag_type=parent_id_type,
            child__is_online=True,
            child_id__in=child_ids,
            child__tag_type=child_id_type
        )
        if need_app_display:
            query &= Q(parent__is_display=True)

        return TagV3Relation.objects.filter(query).select_related('parent', 'child').values_list("parent_id", "child_id")

    @classmethod
    def get_child_tags(cls, parent_ids, child_id_type, parent_id_type, need_app_display=False):
        """
        通过父标签获取子标签的关联关系
        :param parent_ids: 父标签
        :param child_id_type: 子标签类型
        :param parent_id_type: 父标签类型
        :param need_app_display: 需要app外显属性的标签，默认是全部标签
        :return:
        """
        query = Q(
            is_online=True,
            parent__is_online=True,
            parent__tag_type=parent_id_type,
            parent_id__in=parent_ids,
            child__is_online=True,
            child__tag_type=child_id_type,
        )
        if need_app_display:
            query &= Q(child__is_display=True)

        return TagV3Relation.objects.filter(query).select_related('parent', 'child').values_list("parent_id", "child_id")

    #  --- 以上基本是公共方法 ---

    @classmethod
    def get_tag_v3_info_by_old_ids(cls, ids):
        """
        通过老标签id获取映射的新标签数据
        :param ids:[] 老标签id
        :return: {old_tag_id: [{"id": x, "name": xx, "tag_type": xx},新标签信息]}
        """
        if not ids:
            return {}

        tag_v3_mapping_old_ids = cls.get_mapping_tag_ids(ids)
        tag_v3_dict = cls.get_tags_v3_by_ids(set(map(itemgetter(0), tag_v3_mapping_old_ids)))

        old_map_new_data_dict = defaultdict(list)
        for tag_v3_id, old_tag_id in tag_v3_mapping_old_ids:

            new_tag_info = tag_v3_dict.get(tag_v3_id, {})
            if new_tag_info:
                old_map_new_data_dict[old_tag_id].append(new_tag_info)

        return dict(old_map_new_data_dict)

    # @classmethod
    # def get_tag_v3_attr_by_tag_v3_ids(cls, ids, attr_types=None):
    #     """
    #     获取标签属性
    #     :param ids: 新标签id
    #     :param attr_types: 标签属性 [attr_type, attr_type]
    #     :return:{tag_id: {属性枚举: [属性信息{"id": x, "name": xx, "aggregate_type": xx}，属性信息], }, }
    #     """
    #     attr_map_query = cls.base_query & Q(tag_id__in=ids)
    #     objs = cls.get_objs_by_query_and_order(
    #         model_set=TagV3MapAttrTag,
    #         query=attr_map_query
    #     )
    #     attr_ids = objs.values_list("tag_id", "tag_attr_id")
    #
    #     attr_data = cls.get_attr_tag_info_by_attr_ids(
    #         set(map(itemgetter(1), attr_ids)),
    #         attr_types
    #     )
    #
    #     tag_v3_attr_dic = dict()
    #     for tag_id, attr_id in attr_ids:
    #         if tag_id not in tag_v3_attr_dic:
    #             tag_v3_attr_dic[tag_id] = {}
    #
    #         attr_info = attr_data.get(attr_id, {})  # 属性信息
    #         attr_type = attr_info.get("aggregate_type", "")
    #
    #         if all([attr_info, attr_type]):
    #             if attr_type not in tag_v3_attr_dic[tag_id]:
    #                 tag_v3_attr_dic[tag_id][attr_type] = []
    #             tag_v3_attr_dic[tag_id][attr_type].append(attr_info)
    #
    #     return tag_v3_attr_dic

    @classmethod
    def get_category_by_tags(cls, tag_ids):

        category_map = TagCategoryMapTag.objects.filter(tag_id__in=tag_ids)

        category_maps = defaultdict(list)
        category_ids = []
        for item in category_map:
            category_maps[item.tag_id].append(item.tag_category_id)
            category_ids.append(item.tag_category_id)

        categories = TagCategory.objects.filter(pk__in=category_ids, is_online=True).in_bulk(category_ids)

        tag_category_map = defaultdict(list)
        for tag_id, category_ids in category_maps.items():
            for category_id in category_ids:
                if category_id not in categories:
                    continue

                category = categories[category_id]
                tag_category_map[tag_id].append({
                    "id": category.id,
                    "name": category.name,
                })

        return dict(tag_category_map)

    @classmethod
    def get_bodypart_tags(cls, need_app_display=False):
        """
        获取 一二三级项目标签关系树 逻辑树结构
        :param need_app_display: 需要app外显属性的标签，默认是全部标签
        :return:
        """
        result = []

        # 一级标签id
        first_classify_ids = set(TagV3.objects.filter(
            tag_type=TAG_V3_TYPE.FIRST_CLASSIFY,
            is_online=True,
            is_display=True
        ).values_list("id", flat=True))

        if not first_classify_ids:
            return result

        # 通过关联关系，找到1/2标签
        relation_1_2 = cls.get_child_tags(
            parent_ids=first_classify_ids,
            parent_id_type=TAG_V3_TYPE.FIRST_CLASSIFY,
            child_id_type=TAG_V3_TYPE.SECOND_CLASSIFY,
            need_app_display=need_app_display
        )

        # 通过关联关系，找到2/3标签
        relation_2_3 = cls.get_child_tags(
            parent_ids=list(map(itemgetter(1), relation_1_2)),
            parent_id_type=TAG_V3_TYPE.SECOND_CLASSIFY,
            child_id_type=TAG_V3_TYPE.NORMAL,
            need_app_display=need_app_display
        )

        # 关联关系整理
        relation_1_2_sort, relation_2_3_sort = defaultdict(list), defaultdict(list)

        for p_id, c_id in relation_1_2:
            relation_1_2_sort[p_id].append(c_id)

        for p_id, c_id in relation_2_3:
            relation_2_3_sort[p_id].append(c_id)

        # 获取所有标签信息
        tag_ids_set = frozenset(chain(
            list(map(itemgetter(1), relation_1_2)),
            list(map(itemgetter(1), relation_2_3)),
        )) | first_classify_ids
        tags_info = cls.get_tags_v3_by_ids(tag_ids_set)

        def structure_relation_tags(tag_ids, relation_dict, tags_info):
            """
            构建关联关系
            :param tag_ids:
            :param relation_dict:
            :param tags_info:
            :return:
            """
            result = []
            for tag_id in tag_ids:
                if not tags_info.get(tag_id, {}):
                    continue

                data = {
                    "id": tags_info[tag_id]["id"],
                    "name": tags_info[tag_id]["name"],
                    "tag_type": tags_info[tag_id]["tag_type"],
                    "sub_tags": [{
                        "id": tags_info[sub_tag_id]["id"],
                        "name": tags_info[sub_tag_id]["name"],
                        "tag_type": tags_info[sub_tag_id]["tag_type"],
                        "sub_tags": []
                    } for sub_tag_id in relation_dict.get(tag_id, []) if sub_tag_id in tags_info],
                }
                result.append(data)

            return result

        # 关系整理
        for k, sub_list in relation_1_2_sort.items():
            if not tags_info.get(k, {}):
                continue

            t = {
                "id": tags_info[k]["id"],
                "name": tags_info[k]["name"],
                "tag_type": tags_info[k]["tag_type"],
                "sub_tags": [],
            }

            t["sub_tags"].extend(structure_relation_tags(
                sub_list,
                relation_2_3_sort,
                tags_info
            ))

            result.append(t)

        return result

    @classmethod
    def get_category_by_ids(cls, ids):

        categories = TagCategory.objects.using(settings.SLAVE_DB_NAME).filter(pk__in=ids, is_online=True)

        category_map = defaultdict(dict)
        for category in categories:
            category_map[category.id] = {
                'id': category.id,
                'name': category.name,
            }

        return dict(category_map)

    def __rpc_param_limit(self, tag_ids=None, tag_names=None, tag_type=None):
        """
            参数大小限制 及必要参数校验
            return err, res
        """
        if not tag_type:
            return True, None
        if tag_ids and tag_names:
            return True, None
        if (tag_ids and len(tag_ids) >= self.MAX_LENGTH) or (tag_names and len(tag_names) >= self.MAX_LENGTH):
            return True, None
        return False, None

    @staticmethod
    def __gets_homoionym_to_list(homoionym):
        if not homoionym:
            return []
        return json.loads(homoionym)

    def __gets_relation(self, tag_ids):
        """
            获取关联标签
            return {
                tag_id: [tag_id]
            }
        """
        if not tag_ids:
            return None
        query = self.base_query & Q(parent_id__in=tag_ids) | Q(child_id__in=tag_ids)
        relations = TagV3Relation.objects.filter(query).values('parent_id', 'child_id')
        res = {}
        parent_dict = {}
        child_dict = {}
        for item in relations:
            if not parent_dict.get(item['parent_id']):
                parent_dict.setdefault(item['parent_id'], [])
            parent_dict[item['parent_id']].append(item['child_id'])
            if not child_dict.get(item['child_id']):
                child_dict.setdefault(item['child_id'], [])
            child_dict[item['child_id']].append(item['parent_id'])
        for tag_id in tag_ids:
            if not res.get(tag_id):
                res.setdefault(tag_id, [])
            res[tag_id].extend(parent_dict.get(tag_id, []))
            res[tag_id].extend(child_dict.get(tag_id, []))
        return res

    def __gets_map_tag(self, tag_ids):
        """
            获取映射关系
            return {
                tag_id: [tag_id]
            }
        """
        if not tag_ids:
            return None
        query = self.base_query & Q(tag_id__in=tag_ids)
        maps = TagMapTag.objects.filter(query).values('tag_id', 'related_tag_id')
        res = {}
        for item in maps:
            if not res.get(item['tag_id']):
                res.setdefault(item['tag_id'], [])
            res[item['tag_id']].append(item['related_tag_id'])
        return res

    def __gets_map_attr_tag(self, tag_ids):
        """
            获取属性映射关系
            return {
                tag_id: [tag_id]
            }
        """
        if not tag_ids:
            return None
        query = self.base_query & Q(tag_attr_id__in=tag_ids)
        attrs = TagV3MapAttrTag.objects.filter(query).values('tag_id', 'tag_attr_id')
        res = {}
        for item in attrs:
            if not res.get(item['tag_attr_id']):
                res.setdefault(item['tag_attr_id'], [])
            res[item['tag_attr_id']].append(item['tag_id'])

        query = self.base_query & Q(tag_id__in=tag_ids)
        attrs = TagV3MapAttrTag.objects.filter(query).values('tag_id', 'tag_attr_id')
        for item in attrs:
            if not res.get(item['tag_id']):
                res.setdefault(item['tag_id'], [])
            res[item['tag_id']].append(item['tag_attr_id'])
        return res

    def gets_relation_tag(self, tag_ids=None, tag_names=None, tag_type=None):
        """
            获取指定标签的相关标签
            return {
                "id1":[{"id":1,"name":"玻尿酸","tag_type":"1"},{"id":1,"name":"玻尿酸","tag_type":"2"}]
            } or {
                "name1":[{"id":1,"name":"玻尿酸","tag_type":"1"},{"id":1,"name":"玻尿酸","tag_type":"2"}]
            }
        """
        err, res = self.__rpc_param_limit(tag_ids=tag_ids, tag_names=tag_names, tag_type=tag_type)
        if err:
            return res

        query = self.base_query & Q(tag_type=tag_type)
        if tag_ids:
            query = query & Q(id__in=tag_ids)
        elif tag_names:
            query = query & Q(name__in=tag_names)
        else:
            return res

        tags = TagV3.objects.filter(query).values('id', 'name')
        tag_ids = [item['id'] for item in tags]

        relation_dict = self.__gets_relation(tag_ids)
        map_dict = self.__gets_map_tag(tag_ids)
        attr_dict = self.__gets_map_attr_tag(tag_ids)

        tag_relation_dict = {}
        all_tag_ids = []
        for tag_id in tag_ids:
            if not tag_relation_dict.get(tag_id):
                tag_relation_dict.setdefault(tag_id, [])
            tag_relation_dict[tag_id].extend(relation_dict.get(tag_id, []))
            tag_relation_dict[tag_id].extend(map_dict.get(tag_id, []))
            tag_relation_dict[tag_id].extend(attr_dict.get(tag_id, []))
            all_tag_ids.append(tag_id)
            all_tag_ids.extend(relation_dict.get(tag_id, []))
            all_tag_ids.extend(map_dict.get(tag_id, []))
            all_tag_ids.extend(attr_dict.get(tag_id, []))

        all_query = self.base_query & Q(id__in=list(set(all_tag_ids)))
        all_tags = TagV3.objects.filter(all_query).values('id', 'name', 'tag_type')
        all_tag_dict = {item['id']: item for item in all_tags}

        res = {}
        for tag_id, relation_ids in tag_relation_dict.items():
            relation_ids = list(set(relation_ids))
            if tag_ids:
                if not res.get(str(tag_id)):
                    res.setdefault(str(tag_id), [])
                res[str(tag_id)].extend([all_tag_dict.get(relation_id)
                                         for relation_id in relation_ids if all_tag_dict.get(relation_id)])
            elif tag_names:
                tag = all_tag_dict.get(tag_id)
                if not res.get(tag['name']):
                    res.setdefault(tag['name'], [])
                res[tag['name']].extend([all_tag_dict.get(relation_id)
                                         for relation_id in relation_ids if all_tag_dict.get(relation_id)])
            else:
                return None
        return res

    def gets_info(self, tag_ids=None, tag_names=None, tag_type=None):
        """
            获取指定标签的信息
            return [{"id":1,"name":"玻尿酸"},{"id":1,"name":"玻尿酸"}]
        """
        err, res = self.__rpc_param_limit(tag_ids=tag_ids, tag_names=tag_names, tag_type=tag_type)
        if err:
            return res

        query = self.base_query & Q(tag_type=tag_type)
        if tag_ids:
            query = query & Q(id__in=tag_ids)
        elif tag_names:
            query = query & Q(name__in=tag_names)
        else:
            return res
        tags = TagV3.objects.filter(query).values('id', 'name')
        return [{'id': item['id'], 'name': item['name']} for item in tags]

    @staticmethod
    def __gets_synonym(query):
        """
            获取对应筛选条件的同义词
            return {tag_id: [synonym]}
        """
        synonyms = TagSynonym.objects.filter(query).values('tag_id', 'name')
        res = {}
        for item in synonyms:
            if not res.get(item['tag_id']):
                res.setdefault(item['tag_id'], [])
            res[item['tag_id']].append(item['name'])
        return res

    def gets_synonym_or_homoionym(self, tag_ids=None, tag_names=None, tag_type=None, get_synonym=True):
        """
            获取指定标签的近义词或同义词
            return {
                    "name1":["玻尿酸","水光针"],
                    "name2":["玻尿酸","水光针"]
                }
                or
                {
                    "id1":["玻尿酸","水光针"],
                    "id2":["玻尿酸","水光针"]
                }
        """
        err, res = self.__rpc_param_limit(tag_ids=tag_ids, tag_names=tag_names, tag_type=tag_type)
        if err:
            return res
        query = self.base_query & Q(tag_type=tag_type)
        if tag_ids:
            query = query & Q(id__in=tag_ids)
        elif tag_names:
            query = query & Q(name__in=tag_names)
        else:
            return []

        res = {}
        tags = TagV3.objects.filter(query).values('id', 'name', 'homoionym')

        if get_synonym:
            synonym_query = self.base_query & Q(tag_type=tag_type)
            if tag_names:
                query_tag_ids = [item['id'] for item in tags]
            elif tag_ids:
                query_tag_ids = tag_ids
            synonym_query = synonym_query & Q(tag_id__in=query_tag_ids)
            synonym_dict = self.__gets_synonym(synonym_query)

            if tag_ids:
                res = {
                    str(item['id']): synonym_dict.get(item['id'])
                    for item in tags if synonym_dict.get(item['id'])
                }
            elif tag_names:
                res = {
                    item['name']: synonym_dict.get(item['id'])
                    for item in tags if synonym_dict.get(item['id'])
                }
        else:
            if tag_ids:
                res = {
                    str(item['id']): self.__gets_homoionym_to_list(item['homoionym'])
                    for item in tags if self.__gets_homoionym_to_list(item['homoionym'])
                }
            elif tag_names:
                res = {
                    item['name']: self.__gets_homoionym_to_list(item['homoionym'])
                    for item in tags if self.__gets_homoionym_to_list(item['homoionym'])
                }
        return res

    def gets_tag_info_by_ids(self, tag_ids=None, tag_type=None):
        """
            获取标签信息
            params 可指定标签类型
            return [{
                'id': int,
                'name': str,
                'tag_type': int(Enum),
                'is_using_ai': bool,
            }]
        """
        if not tag_ids:
            return []
        query = self.base_query & Q(id__in=tag_ids)
        if tag_type:
            query = query & Q(tag_type=tag_type)
        tags = TagV3.objects.filter(query).values('id', 'name', 'tag_type', 'is_using_ai')
        return [{
            'id': item['id'],
            'name': item['name'],
            'tag_type': item['tag_type'],
            'is_using_ai': item['is_using_ai'],
        } for item in tags]

    @classmethod
    def get_tag_v3_info_by_names(cls, names, tag_type):
        """
        通过names获取映射的新标签数据
        :param names:[] 标签name
        :param tag_type:[] 标签type
        :return:  [{"id": x, "name": xx, "tag_type": xx},新标签信息]
        """
        if not names:
            return {}

        query = cls.base_query & Q(name__in=names)
        if tag_type:
            query &= Q(tag_type=tag_type)

        tags = cls.get_objs_by_query_and_order(
            model_set=TagV3,
            query=query
        )

        return [to_dict(tag, fields=["id", "name", "tag_type"]) for tag in tags]

    @classmethod
    def get_section_tags(cls):
        """问题部位:Hera-标签管理-一级部位，过滤掉关联标签数为0的部位
                    :engineer 20200225: zhengwei@igengmei.com
                """
        attrs = TagV3.objects.filter(tag_type=TAG_V3_TYPE.FIRST_POSITION, is_online=1)
        attr_ids = [attr.id for attr in attrs]
        links = TagV3MapAttrTag.objects.filter(tag_attr_id__in=attr_ids)
        no_zero_attrs = set()
        [no_zero_attrs.add(link.tag_attr_id) for link in links]
        section_tags = []
        for attr in attrs:
            if attr.id in no_zero_attrs:
                section_tag = {
                    "id": attr.id,
                    "name": attr.name
                }
                section_tags.append(section_tag)

        return section_tags

    @classmethod
    def get_improve_appeals(cls, section_tag_ids):
        """「改善诉求」的数据来源为：Hera-标签管理-二级诉求，过滤掉关联标签数为0的诉求，
        「改善诉求」的选项仅受「问题部位」影响，也就是取跟选中「问题部位」关联的「改善诉求」
            :engineer 20200225: zhengwei@igengmei.com
        """
        links = TagV3MapAttrTag.objects.filter(tag_attr_id__in=section_tag_ids)
        tag_ids = set()
        [tag_ids.add(link.tag_id) for link in links]
        appeal_links = TagV3MapAttrTag.objects.filter(tag_id__in=list(tag_ids))
        attr_ids = set()
        [attr_ids.add(link.tag_attr_id) for link in appeal_links]
        attrs = TagV3.objects.filter(id__in=list(attr_ids), tag_type=TAG_V3_TYPE.SECOND_APPEAL)
        improve_appeals = [{"id": attr.id, "name": attr.name} for attr in attrs]
        return improve_appeals

    @classmethod
    def get_suit_projects(cls, improve_appeal_ids):
        """「适应项目」的数据来源：Hera-标签管理-标签（新），
        过滤掉状态为「已下线」的标签，「适应项目」的选项受「问题部位」和「改善诉求」
        的影响（选项为各个「改善诉求」关联「适应项目」的集合）。
            :engineer 20200225: zhengwei@igengmei.com
        """
        links = TagV3MapAttrTag.objects.filter(tag_attr_id__in=improve_appeal_ids)
        tag_ids = set()
        [tag_ids.add(link.tag_id) for link in links]
        suit_tags = TagV3.objects.filter(id__in=list(tag_ids), tag_type=TAG_V3_TYPE.NORMAL, is_online=True)
        suit_projects = [{"id": tag.id, "name": tag.name} for tag in suit_tags]
        return suit_projects

    @classmethod
    def section_tag_data(cls, section_tag_id):
        try:
            item = TagV3.objects.get(id=section_tag_id, tag_type=TAG_V3_TYPE.FIRST_POSITION)
            data = {
                "id": item.id,
                "name": item.name
            }
        except TagV3.DoesNotExist:
            data = {}
        return data

    @classmethod
    def improve_appeal_data(cls, improve_appeal_id):
        try:
            item = TagV3.objects.get(id=improve_appeal_id, tag_type=TAG_V3_TYPE.SECOND_APPEAL)
            data = {
                "id": item.id,
                "name": item.name
            }
        except TagV3.DoesNotExist:
            data = {}
        return data

    @classmethod
    def suit_project_data(cls, suit_project_id):
        try:
            item = TagV3.objects.get(id=suit_project_id, tag_type=TAG_V3_TYPE.NORMAL)
            data = {
                "id": item.id,
                "name": item.name
            }
        except TagV3.DoesNotExist:
            data = {}
        return data

    @classmethod
    def get_tag_ids_by_type(cls, tag_type):
        return list(TagV3.objects.using(settings.SLAVE_DB_NAME).filter(
            tag_type=tag_type, is_online=True,
        ).values_list('id', flat=True))
