# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import, print_function
import re
import logging
import traceback
import datetime
import time
from utils.pic import PictureTools
import redis, json
from cached_property import cached_property
from collections import defaultdict
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.utils.html import escape
from data_sync.utils import to_epoch, tzlc
from gm_serializer import fields
from gm_types.gaia import USER_TYPE, VOTEOBJECT, DIARY_CONTENT_LEVEL, TAG_TYPE
from gm_types.mimas import (
    SPAM_LABEL,
    GRABBING_PLATFORM,
    QUESTION_TYPE,
    SEND_ANSWER_STATUS,
    QA_CONTENT_TYPE,
    MEDIA_IMAGE_URL_SOURCE,
    IMAGE_TYPE,
    QUESTION_AUDIT_STATUS,
)
from gm_types.mimas.qa import CONTENT_CLASS, VIDEO_SOURCE_TYPE
from gm_types.push import AUTOMATED_PUSH
from gm_upload import ImgUrlField, IMG_TYPE

from talos.services import UserConvertService
from talos.services.tag_v3 import TagV3Service
from utils.rpc import RPCMixin
from qa.cache import ViewRecord, answer_cache
from qa.utils import const_strings
from qa.utils.image import get_w_path, get_thumb_path, get_half_path
from qa.utils.time import get_humanize_datetime, get_timestamp_or_none, get_timestamp
from qa.utils.user import get_auth_type_by_userid, get_user_level
from qa.utils.get_video_cover import get_video_cover_url
from utils.exceptions import Impossible
from utils.common import convert_image
from utils.user import get_user_gm_url
from utils.protocol import gm_protocol
from talos.services.tag import TagService

# from talos.models.tractate.tractate import StrategyContentExposureIndex

doris_redis_client = redis.StrictRedis.from_url(settings.REDIS_URL)


class UserManager(RPCMixin):
    def __call__(self, pk_list):
        """
        :param pk_list:
        :return:
        """
        return self.call_rpc('api/user/get_fundamental_info_by_user_ids', user_ids=pk_list)


class TagManager(RPCMixin):
    def __call__(self, pk_list):
        return self.call_rpc('api/tag/info_by_ids', tag_ids=pk_list)


class ApiAnswerScore(models.Model):
    class Meta:
        db_table = 'api_answer_score'

    answer_id = models.IntegerField(unique=True)
    new_score = models.FloatField(verbose_name=u'新的answer_score', blank=True, default=0)


class ApiAnswerScoreV2(models.Model):
    class Meta:
        db_table = 'api_answer_score_v2'

    answer_id = models.IntegerField(unique=True)
    new_score = models.FloatField(verbose_name=u'新的answer_score', blank=True, default=0)


class Question(models.Model):
    class Meta:
        verbose_name = u'问答'
        db_table = 'api_question'
        app_label = 'qa'

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id")
    title = models.CharField(max_length=128, null=False, verbose_name=u'问题')
    content = models.TextField(null=True, verbose_name=u'描述')
    cover_url = ImgUrlField(img_type=IMG_TYPE.NOWATERMARK, max_length=300, verbose_name=u'原图片地址', null=True, blank=True,
                            default=None)
    is_online = models.BooleanField(verbose_name='是否在线', default=True)
    is_recommend = models.BooleanField(verbose_name='是否推荐', default=False)
    recommend_xiaochengxu = models.BooleanField(verbose_name='吐槽小程序,是否推荐', default=False)
    like_num = models.IntegerField(verbose_name='点赞数量', default=0)
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)
    # is_spam字段已弃用，请使用spam_label
    is_spam = models.BooleanField(verbose_name=u'是否为垃圾回答', default=False)
    spam_label = models.SmallIntegerField(verbose_name=u'spam标签', default=SPAM_LABEL.NORMAL, choices=SPAM_LABEL)
    question_type = models.IntegerField(verbose_name=u'问题类型', max_length=3, default=QUESTION_TYPE.TRADE,
                                        choices=QUESTION_TYPE)
    # from topic
    problem_id = models.IntegerField(verbose_name='帖子id', null=True, default=None, unique=True)
    city_id = models.CharField(max_length=40, null=True, verbose_name=u'用户对应的城市id')
    is_invite_doctor = models.BooleanField(verbose_name='是否邀请医生回答', default=False)
    platform = models.CharField(u'抓取平台信息', max_length=2, choices=GRABBING_PLATFORM, default=None,
                                null=True)  # 7675后，平台支持富文本展示content格式数据不同 新增类型
    platform_id = models.CharField(verbose_name='抓取平台ID', max_length=40, default=None, db_index=True)
    platform_tag = models.CharField(verbose_name='抓取平台标签中文', max_length=100, default=None)
    # v 7.7.10 新增内容类型字段 枚举值 用于数据区分及召回
    content_type = models.CharField(u'内容类型', max_length=2, choices=QA_CONTENT_TYPE, default=QA_CONTENT_TYPE.ORDINARY)

    audit_status = models.CharField(verbose_name=u'审核状态', max_length=1, choices=QUESTION_AUDIT_STATUS,
                                    default=QUESTION_AUDIT_STATUS.UNAUDITED)

    def user_can_view(self, user_id):
        if user_id == self.user_id:
            if self.audit_status == QUESTION_AUDIT_STATUS.AUDITED and not self.is_online:
                return False
        elif not self.is_online:
            return False

        return True

    @cached_property
    def tags(self):
        """
        :return:
        """
        return QuestionTag.objects.filter(question=self)

    @staticmethod
    def _get_view_amount_of_id(question_id):
        return ViewRecord(const_strings.QUESTION_VIEW)[question_id] or 0

    @staticmethod
    def set_view_amount_of_id(question_id, num):
        ViewRecord(const_strings.QUESTION_VIEW)[question_id] = str(num)

    @staticmethod
    def _get_vote_amount_of_id(question_id):
        return ViewRecord(const_strings.QUESTION_VIEW)[question_id] or 0

    @property
    def vote_amount(self):
        """7720修改 话题点赞数*7"""
        return int(self._get_vote_amount_of_id(self.id)) * 7

    @cached_property
    def view_amount(self):
        """
        v 7.6.40 浏览量计算规则调整：问题本身浏览量 + 该问题下所有回答浏览量
        v 7.7.20 浏览量*13
        :return:
        """
        num1 = int(self._get_view_amount_of_id(self.id))  # 问题本身的浏览量
        answer_ids = self.answers.filter(is_online=True).values_list("id", flat=True)
        num2 = sum(map(int, filter(None, ViewRecord(const_strings.ANSWER_VIEW).view_hmget(answer_ids))))
        # num2 = sum([int(answer.view_amount) for answer in self.answers.filter(is_online=True)])  # 所有回答的浏览量
        return (num1 + num2) * 13

    @cached_property
    def answer_num(self):
        return self.answers.filter(is_online=True).count()

    @cached_property
    def last_answer_time(self):
        last_answer = self.answers.filter(is_online=True).order_by("-id").first()
        if last_answer:
            return get_timestamp_or_none(last_answer.create_time)

        return None

    @property
    def comment_num(self):
        answers_ids = list(self.answers.filter(is_online=True).values_list('id', flat=True))
        if not answers_ids:
            return 0
        answers_reply_num = AnswerReply.objects.using(settings.SLAVE_DB_NAME).filter(is_online=True,
                                                                                     answer_id__in=answers_ids).count()  # 所有回答的评论数
        return len(answers_ids) + answers_reply_num

    @property
    def recommend_answer(self):
        answer = self.answers.filter(is_recommend=True, is_online=True)
        if answer:
            return answer.first()
        return None

    @property
    def fisrt_answer(self):
        answer = self.answers.filter(is_recommend=True, is_online=True)
        if answer:
            return answer.first()
        return self.answers.filter(is_online=True).order_by('-like_num').first()

    def data_for_list(self, user=None):

        if self.cover_url:
            cover_url = get_w_path(self.cover_url)
        else:
            cover_url = ''
        if self.content:
            video_urls = re.findall('(' + settings.VIDEO_HOST + '.*?)\"', self.content)
            video_cover_list = get_video_cover_url(source_id=self.id, source_type=VIDEO_SOURCE_TYPE.QUESTION,
                                                   video_urls=video_urls)
        else:
            video_cover_list = None
        data = {
            'user_id': self.user.id,
            'user_name': self.user.nickname,
            'user_portrait': self.user.portrait,
            'membership_level': self.user.membership_level,
            'title': self.title,
            'content': escape(self.content) if self.content else '',
            'row_content': self.content or '',
            'image': cover_url,
            'answer_num': str(self.comment_num),
            'timestamp': int(self.create_time.strftime("%s")),
            'question_id': str(self.id),
            'time': get_humanize_datetime(self.create_time),
            'user_level': get_user_level(self.user),
            'view_num': self.view_amount,
            'tags': [tag.tag for tag in self.tags if tag.tag],
            'content_images': self.content_images,
            'platform': self.platform,
            'question_type': self.question_type,
            'video_cover_list': video_cover_list,
        }
        return data

    def data_for_discovery(self):
        """发现页"""

        if self.cover_url:
            cover_url = get_w_path(self.cover_url)
        else:
            cover_url = ''

        update_time = self.create_time
        answers = self.answers.filter(is_online=True).order_by("-create_time").values("id", "create_time")
        if answers:
            update_time = max(answers[0]["create_time"], update_time)
            ids = [i["id"] for i in answers]
            reply = AnswerReply.objects.filter(is_online=True, answer__in=ids). \
                order_by("-create_time").first()
            if reply:
                update_time = max(reply.create_time, update_time)

        data = {
            "title": self.title,
            "question_id": self.id,
            'title': self.title,
            'content': escape(self.content) if self.content else '',
            'row_content': self.content or '',
            'image': cover_url,
            'question_id': str(self.id),
            'desc': get_humanize_datetime(update_time),
            'tags': [tag.tag for tag in self.tags if tag.tag],
            'content_images': self.content_images,
            'platform': self.platform,
        }

        return data

    def data_for_doctor(self):
        data = {
            'user_id': self.user_id,
            'user_name': self.user.nickname,
            'user_portrait': self.user.portrait,
            'membership_level': self.user.membership_level,
            'title': self.title,
            'answer_num': str(self.answer_num),
            'question_id': str(self.id),
            'time': get_humanize_datetime(self.create_time),
        }
        return data

    def data_for_my_list(self, need_view_amount=True):
        data = {
            'title': self.title,
            'content': escape(self.content),
            'answer_num': str(self.answer_num),
            'vote_num': self.vote_amount,
            'question_id': str(self.id),
            'time': get_humanize_datetime(self.create_time),
            'create_time': self.create_time.strftime('%y-%m-%d'),
            'platform': self.platform
        }
        if need_view_amount:
            data["view_num"] = self.view_amount
        return data

    def data_for_es(self):
        answer = self.fisrt_answer
        if answer:
            return answer.data_for_es()
        return {
            'title': self.title,
            'content': '',
            'vote_num': 0,
            'comment_num': self.answer_num,
            'question_id': self.id,
            'answer_id': ''
        }

    @property
    def content_images(self):
        _m = []
        images = self.images.filter(
            image_url_source=MEDIA_IMAGE_URL_SOURCE.CREATE
        ).all()
        for image in images:
            if image.image_url:
                _m.append(image.image_url)

        return _m

    def get_tags_v3_info(self):
        """
        问题对应的tag_v3信息
        :return: [{'id': 11, 'tag_type'：'11'， 'name': '标签名'}]
        """
        tag_v3_id_list = QuestionTagV3.objects.filter(question_id=self.id).values_list('tag_v3_id', flat=True)
        tags_v3 = [
            {
                'id': tag.id,
                'tag_type': str(tag.tag_type),
                'name': tag.name
            }
            for tag in TagV3Service.get_tags_by_tag_v3_ids(tag_v3_id_list).values()
        ]

        return tags_v3


class QuestionImage(models.Model):
    class Meta:
        verbose_name = u'问答图片'
        db_table = 'api_question_image'
        app_label = 'qa'

    question = models.ForeignKey(Question, related_name='images')
    image_url = ImgUrlField(img_type=IMG_TYPE.TOPICIMAGE, max_length=300, verbose_name=u'图片地址')
    width = models.IntegerField(verbose_name="图片宽度", default=0)
    height = models.IntegerField(verbose_name="图片高度", default=0)
    image_url_source = models.CharField(verbose_name="图片地址来源(创建、富文本)", max_length=3, choices=MEDIA_IMAGE_URL_SOURCE)
    image_type = models.IntegerField(verbose_name="图片类型", default=IMAGE_TYPE.OTHER)
    image_webp = models.CharField(max_length=128, default="", verbose_name=u'webp格式的图片')

    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)

    def data(self):
        return {
            'image': get_w_path(self.image_url),
        }

    @property
    def image_info_data(self):
        return {
            "image": self.image_url,
            "width": self.width,
            "height": self.height,
            "image_webp": self.image_webp,
        }


class QuestionTag(models.Model):
    class Meta:
        db_table = 'api_questiontag'
        app_label = 'qa'

    question = models.ForeignKey(Question, related_name='qtags')
    tag = fields.MagicField(type=int, manager=TagManager, ttl=60 * 60 * 24, db_column="tag_id")

    def get_name_list(self, tag_list):
        logging.info("get tag_list:%s" % tag_list)
        manager = TagManager()
        name = list()
        ma = manager.__call__(tag_list)
        for i in ma:
            logging.info("get manage_name:%s" % i['name'])
            name.append(i['name'])
        return name

    def get_project_tags(self, tag_list):
        tag_list = TagService._get_by_ids_from_cache_type(list(tag_list))
        return [t.name for t in tag_list if
                t.tag_type in (TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM, TAG_TYPE.ITEM_WIKI)]


class QuestionTagV3(models.Model):
    class Meta:
        db_table = 'api_question_tag_v3'
        app_label = "qa"

    question_id = models.IntegerField(verbose_name="问题id", db_index=True)
    tag_v3_id = models.IntegerField(verbose_name="标签V3", db_index=True)


class QuestionTagV4(models.Model):
    class Meta:
        db_table = 'api_question_tag_v4'
        app_label = "qa"

    question_id = models.IntegerField(verbose_name="问题id", db_index=True)
    tag_v4_id = models.IntegerField(verbose_name="标签V4", db_index=True)


class QuestionVote(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_question_vote'
        verbose_name = u'问答点赞'

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id")
    question = models.ForeignKey(Question, related_name="votes")
    unread = models.BooleanField(default=True)
    is_fake = models.BooleanField(default=False, verbose_name=u"是否机器人点赞")
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)


class Answer(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_answer'
        verbose_name = u'问答回复'

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id")
    content = models.TextField(verbose_name='回答', null=False)
    cover_url = ImgUrlField(img_type=IMG_TYPE.NOWATERMARK, max_length=300, verbose_name=u'原图片地址', null=True,
                            blank=True, default=None)
    question = models.ForeignKey(Question, verbose_name=u"话题回复", related_name='answers')
    is_online = models.BooleanField(verbose_name='是否在线', default=True)
    is_recommend = models.BooleanField(verbose_name='是否推荐', default=False)
    like_num = models.IntegerField(verbose_name='点赞数量', default=0)
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)
    # is_spam字段已弃用，请使用spam_label
    is_spam = models.BooleanField(verbose_name=u'是否为垃圾回答', default=False)

    spam_label = models.SmallIntegerField(default=SPAM_LABEL.NORMAL, choices=SPAM_LABEL, verbose_name=u"spam标签")
    level = models.IntegerField('分级', default=0)
    rank = models.IntegerField('展示序列', default=999)

    platform = models.CharField(u'抓取平台信息', max_length=2, choices=GRABBING_PLATFORM, default=None, null=True)
    platform_id = models.CharField(verbose_name='抓取平台ID', max_length=40, default=None, db_index=True)
    platform_tag = models.CharField(verbose_name='抓取平台标签中文', max_length=100, default=None)
    #  v 7.6.40新加 用于消息 - 回答 计数使用！
    questioner_read = models.BooleanField(verbose_name='提问者是否已读', default=False)
    # v 7.6.70 新增 用户记录:当前回答的用户关联的医生id
    doctor_id = models.CharField(max_length=100, verbose_name="回答用户关联的医生id", default=None, null=True)
    doctor_title = models.CharField(max_length=2, verbose_name="回答用户关联的医生职称", default=None, null=True)
    # v 7.7.10 新增内容类型字段 枚举值 用于数据区分及召回
    content_type = models.CharField(u'内容类型', max_length=2, choices=QA_CONTENT_TYPE, default=QA_CONTENT_TYPE.ORDINARY)

    # from topicreply
    topicreply_id = models.IntegerField(verbose_name='帖子id', null=True, default=None, unique=True)

    low_quality = models.IntegerField(default=0, verbose_name=u'低质量反馈数量', blank=True, null=False)
    low_quality_deal = models.BooleanField(default=False, verbose_name=u'低质量反馈是否处理', blank=True)

    @cached_property
    def tags(self):
        """
        :return:
        """
        return AnswerTag.objects.filter(answer=self)

    @staticmethod
    def _get_view_amount_of_id(answer_id):
        return ViewRecord(const_strings.ANSWER_VIEW)[answer_id] or 0

    @property
    def view_amount(self):
        return int(self._get_view_amount_of_id(self.id))

    @staticmethod
    def set_view_amount_of_id(answer_id, num):
        ViewRecord(const_strings.ANSWER_VIEW)[answer_id] = str(num)

    @cached_property
    def comment_num(self):
        """
        v 7.6.95变更，评论数为：1级评论 + 子评论
        :return:
        """
        return self.replys.filter(is_online=True).count()

    @cached_property
    def first_reply_num(self):
        return self.replys.filter(is_online=True, first_reply=None).count()

    def get_cover_image(self):
        if self.cover_url:
            return [convert_image(self.cover_url, watermark=True)]
        else:
            return []

    def get_all_images(self):
        data = []
        for image in self.images.filter(image_url_source=MEDIA_IMAGE_URL_SOURCE.CREATE).order_by('id'):
            data.append(image.data())
        return data

    def get_images(self):
        """获取所有的图片"""
        data = []
        for image in self.images.all().order_by('id'):
            data.append(image.data())
        return data

    def data_for_es(self, need_view_amount=True):

        data = {
            'title': self.question.title,
            'content': escape(self.content),
            'vote_num': self.like_num,
            'comment_num': self.comment_num,
            'question_id': self.question_id,
            'answer_id': self.id,
        }

        if need_view_amount:
            data['view_num'] = self.view_amount

        return data

    def data_for_list(self, user=None):
        is_vote = False
        if user:
            is_vote = AnswerVote.objects.filter(user=user, answer=self).exists()

        if self.content:
            video_urls = re.findall('(' + settings.VIDEO_HOST + '.*?)\"', self.content)
            video_cover_list = get_video_cover_url(source_id=self.id, source_type=VIDEO_SOURCE_TYPE.ANSWER,
                                                   video_urls=video_urls)
        else:
            video_cover_list = None
        data = {
            'user_id': self.user_id,
            'title': self.question.title,
            'content': escape(self.content) if self.content else "",
            'raw_content': self.content or "",
            'time': get_humanize_datetime(self.create_time),
            'user_name': self.user.nickname,
            'user_portrait': self.user.portrait,
            'membership_level': self.user.membership_level,
            'vote_num': str(self.like_num),
            'comment_num': str(self.comment_num),
            'answer_id': str(self.id),
            'question_id': str(self.question_id),
            'images': self.get_cover_image(),
            'content_images': self.content_images,
            'is_voted': is_vote,
            'user_level': get_user_level(self.user),
            'view_num': self.view_amount,
            'platform': self.platform,
            'question_type': self.question.question_type,
            'video_cover_list': video_cover_list,
        }
        return data

    @property
    def cover_is_dynamic(self):
        # video_cover_list -> question_videos -> answer.cover_url -> header_images ->
        # intact_answer_images -> _question_intact_question_images

        from qa.manager.answer_manager import AnswerManager
        from qa.manager.qa_media_manager import answer_media, question_media

        answer_text_dic = {self.id: self.content}
        answer_videos_dic = answer_media.get_qa_videos(answer_text_dic, source_type=VIDEO_SOURCE_TYPE.ANSWER)
        if answer_videos_dic.get(self.id):
            return True

        question_text_dic = {self.question_id: self.question.content}
        question_videos_dic = question_media.get_qa_videos(question_text_dic, source_type=VIDEO_SOURCE_TYPE.QUESTION)
        if question_videos_dic.get(self.question_id):
            return True

        if PictureTools.is_dynamic(self.cover_url):
            return True

        answer_header_image_dict = AnswerManager.get_header_imgs_by_ids([self.id])
        if answer_header_image_dict:
            imgs = answer_header_image_dict.get(self.id, []) or []
            if imgs and (imgs[0].get('image_webp') or
                         PictureTools.is_dynamic(imgs[0].get('image_url') or imgs[0].get('image'))):
                return True

        answer_images_dic = answer_media.get_qa_images(answer_text_dic, image_url_sources=[VIDEO_SOURCE_TYPE.ANSWER])
        if answer_images_dic:
            imgs = answer_images_dic.get(self.id, []) or []
            if imgs and (imgs[0].get('image_webp') or
                         PictureTools.is_dynamic(imgs[0].get('image_url') or imgs[0].get('image'))):
                return True

        question_images_dic = question_media.get_qa_images(question_text_dic,
                                                           image_url_sources=[VIDEO_SOURCE_TYPE.QUESTION])
        if question_images_dic:
            imgs = question_images_dic.get(self.question_id, []) or []
            if imgs and (imgs[0].get('image_webp') or
                         PictureTools.is_dynamic(imgs[0].get('image_url') or imgs[0].get('image'))):
                return True

        return False

    @staticmethod
    def has_video(answer):

        if answer.content:
            video_urls = re.findall('(' + settings.VIDEO_HOST + '.*?)\"', answer.content)
            if video_urls:
                return True

        if answer.question.content:
            video_urls = re.findall('(' + settings.VIDEO_HOST + '.*?)\"', answer.question.content)
            if video_urls:
                return True

        return False

    def get_base_info(self):

        if self.content:
            video_urls = re.findall('(' + settings.VIDEO_HOST + '.*?)\"', self.content)
            video_cover_list = get_video_cover_url(source_id=self.id, source_type=VIDEO_SOURCE_TYPE.ANSWER,
                                                   video_urls=video_urls)
        else:
            video_cover_list = None

        if self.question.content:
            video_urls = re.findall('(' + settings.VIDEO_HOST + '.*?)\"', self.question.content)
            question_video_cover_list = get_video_cover_url(source_id=self.question.id,
                                                            source_type=VIDEO_SOURCE_TYPE.QUESTION,
                                                            video_urls=video_urls)
        else:
            question_video_cover_list = None

        data = {
            'user_id': self.user_id,
            'title': self.question.title,
            'content': escape(self.content) if self.content else "",
            'raw_content': self.content or "",
            'time': get_humanize_datetime(self.create_time),
            'create_time': get_timestamp_or_none(self.create_time),
            'user_name': "",
            'user_portrait': "",
            'membership_level': "",
            'user_level': get_user_level(None),
            'answer_id': str(self.id),
            'view_num': self.view_amount,
            'question_id': str(self.question_id),
            'content_images': self.content_images,
            'video_cover_list': video_cover_list,
            'cover_url': self.cover_url,

            'platform': self.platform,
            'question_type': self.question.question_type,
            'question_content_images': self.question.content_images,
            'question_answer_num': self.question.answer_num,
            'question_videos': question_video_cover_list,
            'question_content': self.question.content,
        }
        return data

    def _get_cache_key(self):
        return 'qa:answer-v1m:%s' % self.id

    def del_cache(self):
        k = self._get_cache_key()
        answer_cache.delete(k)

    def _get_or_init_cached_data(self):
        k = self._get_cache_key()
        _cached = answer_cache.get(k)
        if _cached:
            return json.loads(_cached)

        answer_data = self.get_base_info()
        answer_cache.setex(k, 10 * 60, json.dumps(answer_data))

        return answer_data

    def data_for_qa(self, user=None):

        is_vote = False
        if user:
            is_vote = AnswerVote.objects.filter(user=user, answer=self).exists()

        data = self._get_or_init_cached_data()
        data.update({
            'vote_num': self.like_num,
            'comment_num': self.comment_num,
            'question_answer_num': self.question.answer_num,
            'last_answer_time': self.question.last_answer_time,
            'is_voted': is_vote,
        })
        return data

    def data_for_detail(self, user):
        is_vote = False
        if user:
            is_vote = AnswerVote.objects.filter(user=user, answer=self).exists()

        if self.user_id == settings.SUOZHANG_UID or self.platform in GRABBING_PLATFORM:
            content = self.content
        else:
            content = escape(self.content)
        return {
            'id': self.id,
            'post_date': self.create_time.strftime('%y-%m-%d'),
            'content': content,
            'vote_count': self.like_num,
            'comment_count': self.comment_num,
            'image': self.get_all_images(),
            'is_online': self.is_online,
            'is_voted': is_vote,
            'tags': [tag.tag for tag in self.question.tags if tag.tag],  # 取其question的tag
            'platform': self.platform,  # 平台信息
            'first_reply_num': self.first_reply_num,  # 回答的一级评论数，用于查看更多一级评论！
            'create_time': self.create_time.timestamp(),
        }

    def get_videos(self):
        if self.content:
            video_urls = re.findall('(' + settings.VIDEO_HOST + '.*?)\"', self.content)
            video_cover_list = get_video_cover_url(source_id=self.id, source_type=VIDEO_SOURCE_TYPE.ANSWER,
                                                   video_urls=video_urls)
        else:
            video_cover_list = []

        return video_cover_list

    def data_for_author(self, user=None):
        user_info = UserConvertService.get_user_info_by_user_id(self.user_id)
        user_info.update({
            'user_type': 0,
            "user_portrait": self.user.portrait,
            'is_following': RPCMixin.call_rpc('api/user/is_following', cuid=user.id,
                                              uid=self.user.id) if user else False,
            'user_level': get_user_level(self.user),
            "college_id": self.user.get("college_id", 0),
            "college_name": self.user.get("college_name", ""),
        })
        return user_info

    @property
    def content_images(self):
        _m = []

        images = self.images.filter(
            image_url_source=MEDIA_IMAGE_URL_SOURCE.CREATE
        ).all()
        for image in images:
            if image.image_url:
                _m.append(image.image_url)

        return _m

    @property
    def get_good_click(self):
        try:
            from talos.models.soft_article.soft_article import PageGoodClick
            results = list(
                PageGoodClick.objects.filter(business_id=self.id, page_name="answer_detail")
                    .order_by('-create_date').values_list("avg_new_gc", flat=True))
            good_click_score = results and results[0] or 0
            return good_click_score
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return 0

    @property
    def con_good_click(self):
        try:
            from talos.models.soft_article.soft_article import GoodClickCom
            today = datetime.datetime.now() - datetime.timedelta(days=1)
            lastday = str(datetime.datetime(today.year, today.month, today.day))
            lastday = lastday.split(" ")
            date_change = lastday[0].replace("-", "")

            results = list(
                GoodClickCom.objects.using("doris").filter(business_id=str(self.id), page_name="answer_detail",
                                                           create_date=str(date_change)).values_list(
                    "goodclick_rate_30", flat=True))

            good_click_score = results and results[0] or 0
            return good_click_score
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return 0

    def data_for_received(self):
        data = {
            "user": {
                'doctor_id': '',
                'user_id': self.user_id,
            },
            "answer": {
                "id": str(self.id),
                "content": self.content,
                "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
                "is_new": not self.questioner_read,
                "user_id": self.user_id,
                "username": self.user.nickname,
                "portrait": self.user.portrait,
            },
            "question": {
                "nickname": self.question.user.nickname,
                "portrait": self.question.user.portrait,
                "title": self.question.title,
                "is_deleted": not self.question.is_online,
            },
        }
        return data

    def _content_score(self):
        if self.level in [CONTENT_CLASS.EXCELLENT, CONTENT_CLASS.OUTSTANDING]:
            return 100
        elif self.level == CONTENT_CLASS.FINE:
            return 70
        elif self.level == CONTENT_CLASS.GENERAL:
            return 10
        elif self.level == CONTENT_CLASS.BAD:
            return 5
        else:
            return 0

    def _social_score(self):
        pass

    def _comment_score(self):
        comment_num = self.comment_num
        if comment_num == 0:
            return 0
        elif 1 <= comment_num <= 10:
            return 20
        elif 11 <= comment_num <= 20:
            return 40
        elif 21 <= comment_num <= 40:
            return 70
        elif 41 <= comment_num:
            return 100
        else:
            raise Impossible

    def _like_score(self):
        like_num = self.like_num
        if like_num == 0:
            return 0
        elif 1 <= like_num <= 10:
            return 20
        elif 11 <= like_num <= 20:
            return 40
        elif 21 <= like_num <= 30:
            return 70
        elif 30 <= like_num:
            return 100
        else:
            raise Impossible

    def _time_score(self):
        created_date = self.create_time.date()
        delta = created_date - settings.FOUNDING_DAY
        return delta.days

    def smart_rank(self):
        content_score = self._content_score()
        social_score = settings.SOCIAL_REPLY_WEIGHT * self._comment_score() + \
                       settings.SOCIAL_LIKE_WEIGHT * self._like_score()
        rank = settings.CONTENT_WEIGHT * content_score + \
               settings.SOCIAL_WEIGHT * social_score + \
               settings.TIME_WEIGHT * self._time_score()
        return rank

    def smart_rank_v2(self):
        try:
            result = ApiAnswerScore.objects.using(settings.DORIS_DB_NAME).filter(answer_id=self.id).first()
            return result.new_score if result else 0.0
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return 0.0

    def smart_rank_v3(self):
        try:
            result = ApiAnswerScoreV2.objects.using(settings.DORIS_DB_NAME).filter(answer_id=self.id).first()
            return result.new_score if result else 0.0
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return 0.0

    def get_hot_score_answer(self):
        try:
            now = datetime.datetime.now()
            three_day = datetime.datetime.now() - datetime.timedelta(days=60)
            yesterday_begin_time = "%s-%s-%s 00:00:00" % (three_day.year, three_day.month, three_day.day)
            # 点赞数+3评论数+5收藏数
            # 获得点赞数
            vote_num = AnswerVote.objects.filter(answer_id=self.id, create_time__gte=yesterday_begin_time,
                                                 create_time__lte=now).count()
            # 获得评论数
            reply_num = AnswerReply.objects.filter(answer_id=self.id, create_time__gte=yesterday_begin_time,
                                                   create_time__lte=now, is_online=True).count()

            return vote_num + 3 * int(reply_num) + 0

        except:

            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return []

    def get_last_any_reply_time(self):
        try:
            result = AnswerReply.objects.filter(answer_id=self.id).values_list("create_time", flat=True).order_by(
                "-create_time").first()

            return result

        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return ''

    def get_has_picture(self):
        # 是否有图片
        try:
            answer_content = self.content
            re_pattern = r'.*[img|IMG|video|VIDEO] src="(.*)" .*'
            src_result = re.findall(re_pattern, answer_content)
            if src_result:
                return True
            else:
                return False
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return False

    def nofake_comment_num(self, answer_id):

        count = AnswerReply.objects.filter(answer_id=answer_id, is_fake=False, is_online=True).values_list("user",
                                                                                                           flat=True).distinct().count()
        return count

    def nofake_vote_number(self, answer_id):

        count = AnswerVote.objects.filter(answer_id=answer_id, is_fake=False).values_list("user",
                                                                                          flat=True).distinct().count()
        return count

    # 获取回答贴最新互动时间
    def get_answer_latest_interaction_time(self, is_online, content_level, create_time):
        try:
            update_time_value = -1
            if is_online:
                update_time_value = int(time.mktime(create_time.timetuple())) if create_time else -1

                # 最新点赞时间
                vote_result = AnswerVote.objects.filter(answer_id=self.id).values_list("update_time",
                                                                                       flat=True).order_by(
                    "-update_time").first()
                vote_time_value = int(time.mktime(tzlc(vote_result).timetuple())) if vote_result else -1
                if vote_time_value > update_time_value:
                    update_time_value = vote_time_value

                # 最新回复时间
                reply_result = AnswerReply.objects.filter(answer_id=self.id).values_list("update_time",
                                                                                         flat=True).order_by(
                    "-update_time").first()
                reply_time_value = int(time.mktime(tzlc(reply_result).timetuple())) if reply_result else -1
                if reply_time_value > update_time_value:
                    update_time_value = reply_time_value

                # 小组创建时间
                answer_group_redis_name = "doris:answer_group_data"
                redis_tractate_group_data = doris_redis_client.hget(answer_group_redis_name, str(self.id))
                answer_group_id_list = json.loads(
                    str(redis_tractate_group_data, encoding="utf-8")) if redis_tractate_group_data else []

                if len(answer_group_id_list) > 0:
                    from talos.models.soft_article.soft_article import Group
                    group_update_time_result = Group.objects.using(settings.ZHENGXING_DB).filter(
                        id__in=answer_group_id_list).values_list(
                        "create_time", flat=True).order_by("-create_time").first()
                    group_update_time_value = int(
                        time.mktime(tzlc(group_update_time_result).timetuple())) if group_update_time_result else -1
                    if group_update_time_value > update_time_value:
                        update_time_value = group_update_time_value + 5

            return update_time_value
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return -1

    def get_new_smart_rank_score(self, answer_id):
        try:
            today = datetime.datetime.now().date()
            delta = datetime.timedelta(days=1)
            n_days = today - delta
            date_change = str(n_days).replace("-", "")
            score = StrategyAnswerSmrScore.objects.using(settings.DORIS_DB_NAME).filter(
                answer_id=answer_id).order_by('-create_date').values_list("smart_rank_score", flat=True).first()
            if score:
                return score
            else:
                return 0
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return 0

    def get_answer_newuser_smr(self, answer_id):
        score = SrategyAnswerSmrScore.objects.using(settings.DORIS_DB_NAME).filter(
            answer_id=answer_id).order_by('-create_date').values_list("smart_rank_score", flat=True).first()
        if score:
            return score
        else:
            return 0

    def get_answer_tagv4_names(self, answer_id):
        try:
            data = AnswerTagV4.objects.using(settings.HERA_READ_DB).filter(answer_id=answer_id).values_list(
                "tag_v4_id", flat=True)
            return data
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return []

    def get_search_new_smart_rank_score(self, answer_id):
        try:
            today = datetime.datetime.now().date()
            delta = datetime.timedelta(days=1)
            n_days = today - delta
            date_change = str(n_days).replace("-", "")
            score = SearchStrategyAnswerSmrScore.objects.using(settings.DORIS_DB_NAME).filter(
                answer_id=answer_id).order_by('-create_date').values("smart_rank_score", "new_goodclick",
                                                                     "smart_rank_v2").first()
            if score:
                return score
            else:
                return {}
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return {}

    def get_answer_show_by_index(self, answer_id):
        try:
            from talos.models.tractate.tractate import StrategyContentExposureIndex
            today = datetime.datetime.now().date()
            delta = datetime.timedelta(days=2)
            n_days = today - delta
            data = StrategyContentExposureIndex.objects.using(settings.DORIS_DB_NAME).filter(
                create_day=n_days, card_id=answer_id, card_content_type="answer").first()
            if data.ctr >= 0.05 and data.preciseexposure_num >= 50 and data.avg_page_stay >= 20:
                return 0  # 0是正常展示  1不展示
            else:
                return 1
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return 1

    def get_answer_update_time_stratific(self, create_time):
        try:
            now = datetime.datetime.now()
            d2 = datetime.datetime.strptime(now.strftime('%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S')
            delta_30 = datetime.timedelta(days=30)
            delta_90 = datetime.timedelta(days=90)
            delta_365 = datetime.timedelta(days=365)
            if d2:
                if d2 - delta_30 <= create_time:
                    return 30
                elif d2 - delta_90 <= create_time:
                    return 90
                elif d2 - delta_365 <= create_time:
                    return 365
                else:
                    return 1000
            else:
                return 1000
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return 1000

    def get_answer_tag(self, tag_list):
        try:
            association_tags_id_list = list()
            tag_list = TagService._get_by_ids_from_cache_type(tag_list)
            for item in tag_list:
                association_tags_id_list.append(
                    {"id": item.id, "tag_name": item.name, "recommend_type": item.recommend_type,
                     "tag_type": item.tag_type})
            return association_tags_id_list
        except:
            logging.error("catch exception,err_msg:%s" % traceback.format_exc())
            return []


class SendAnswer(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_send_answer'
        verbose_name = u'定时发送回答'

    user_id = models.CharField(max_length=32, verbose_name='用户ID')
    content = models.TextField(verbose_name='回答内容', null=False)
    cover_url = ImgUrlField(img_type=IMG_TYPE.NOWATERMARK, max_length=300, verbose_name=u'运营配位图', null=True,
                            blank=True, default=None)
    question = models.ForeignKey(Question, verbose_name=u"问题")
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    push_time = models.DateTimeField(verbose_name=u'定时发送时间')
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)
    # is_spam字段已弃用，请使用spam_label
    level = models.IntegerField('分级', default=0)
    rank = models.IntegerField('展示序列', default=999)
    status = models.SmallIntegerField(default=SEND_ANSWER_STATUS.WAITTING, choices=SEND_ANSWER_STATUS,
                                      verbose_name=u"定时发送回答状态")
    celery_task_id = models.CharField(verbose_name=u'异步任务ID', max_length=64)

    @property
    def data(self):
        return {
            'user': self.user_id,
            'content': self.content,
            'cover_url': self.cover_url,
            'level': self.level,
            'rank': self.rank,
            'question_id': self.question.id,
            'platform': GRABBING_PLATFORM.HERA,  # 默认为hera后台
        }


class AnswerImage(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_answer_image'
        verbose_name = u'问答回复图片'

    answer = models.ForeignKey(Answer, related_name='images')
    image_url = ImgUrlField(img_type=IMG_TYPE.TOPICREPLY, max_length=300, verbose_name=u'图片地址')
    width = models.IntegerField(verbose_name="图片宽度", default=0)
    height = models.IntegerField(verbose_name="图片高度", default=0)
    image_url_source = models.CharField(verbose_name="图片地址来源(创建、富文本)", max_length=3, choices=MEDIA_IMAGE_URL_SOURCE)
    image_type = models.IntegerField(verbose_name="图片类型", default=IMAGE_TYPE.OTHER)
    image_webp = models.CharField(max_length=128, default="", verbose_name=u'webp格式的图片')

    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)

    def data(self):
        return {
            'image': get_w_path(self.image_url),
        }

    @property
    def image_info_data(self):
        return {
            "image": self.image_url,
            "width": self.width,
            "height": self.height,
            "image_webp": self.image_webp,
        }


class AnswerTag(models.Model):
    class Meta:
        db_table = 'api_answer_tag'
        app_label = 'qa'

    answer = models.ForeignKey(Answer, related_name='answer_tags')
    tag = fields.MagicField(type=int, manager=TagManager, ttl=60 * 60 * 24, db_column="tag_id", db_index=True)


class AnswerTagV3(models.Model):
    class Meta:
        db_table = 'api_answer_tag_v3'
        app_label = "qa"

    answer_id = models.IntegerField(verbose_name="回答id", db_index=True)
    tag_v3_id = models.IntegerField(verbose_name="标签V3", db_index=True)


class AnswerTagV4(models.Model):
    class Meta:
        db_table = 'api_answer_tag_v4'
        app_label = "qa"

    answer_id = models.IntegerField(verbose_name="回答id", db_index=True)
    tag_v4_id = models.IntegerField(verbose_name="标签V4", db_index=True)


class AnswerAttrTagV3(models.Model):
    class Meta:
        db_table = 'api_answer_v3_attr_tag'
        app_label = "qa"

    answer_id = models.IntegerField(verbose_name="回答id", db_index=True)
    attr_tag_id = models.IntegerField(verbose_name="属性标签ID", db_index=True)


class AnswerVote(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_answer_vote'
        unique_together = ('user', 'answer')

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id")
    answer = models.ForeignKey(Answer, verbose_name=u"评论")
    unread = models.BooleanField(default=True)
    is_fake = models.BooleanField(default=False, verbose_name=u"是否机器人点赞")
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)

    def to_dict(self):
        data = {
            'answer_id': self.answer_id,
            'nickname': self.user.nickname,
            'user_id': self.user_id,
            'vote_time': get_timestamp_or_none(self.create_time),
            'image': '',
            'content': escape(self.answer.content),
            'type': VOTEOBJECT.ANSWER,
            'membership_level': self.user.membership_level,
            'portrait': self.user.portrait,

        }
        return data


class AnswerReply(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_answer_reply'
        verbose_name = u'问答的评论'

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id")
    content = models.TextField(verbose_name='回答', null=False)
    answer = models.ForeignKey(Answer, verbose_name=u"话题回复", related_name='replys')
    first_reply = models.ForeignKey('self', related_name=u'fir_comment', verbose_name=u'一级回复id', null=True,
                                    blank=True, default=None)
    commented_reply = models.ForeignKey('self', related_name=u'comments', verbose_name=u'被评论的回复', null=True,
                                        blank=True, default=None)
    is_online = models.BooleanField(verbose_name='是否在线', default=True)
    like_num = models.IntegerField(verbose_name='点赞数量', default=0)
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)
    # is_spam字段已弃用，请使用spam_label
    is_spam = models.BooleanField(verbose_name=u'是否为垃圾回答', default=False)
    spam_label = models.SmallIntegerField(default=SPAM_LABEL.NORMAL, choices=SPAM_LABEL, verbose_name=u"spam标签")

    # from topic
    topicreply_id = models.IntegerField(verbose_name='帖子id', null=True, default=None, unique=True)
    is_read = models.BooleanField(verbose_name='是否已读', default=False)
    is_fake = models.BooleanField(default=False, verbose_name=u"是否是灌水数据")

    @property
    def comment_num(self):
        return self.fir_comment.filter(is_online=True).count()

    def get_comment_data(self):
        if self.commented_reply and self.commented_reply.user == self.user:
            at_nickname = u''
        elif self.commented_reply:
            at_nickname = self.commented_reply.user.nickname
        else:
            at_nickname = u''

        return {
            'content': escape(self.content),
            'comment_id': self.id,
            'comment_user_id': self.user.id,
            # 'comment_user_type': get_auth_type_by_userid(self.user_id),
            'comment_user_type': USER_TYPE.NORMAL,
            "comment_user_gm_url": get_user_gm_url(self.user_id),
            'nickname': self.user.nickname,
            'at_user_id': self.commented_reply.user.id if self.commented_reply else 0,
            # 'at_user_type': get_auth_type_by_userid(self.commented_reply.user_id),
            'at_user_gm_url': get_user_gm_url(self.commented_reply.user_id),
            'at_user_type': USER_TYPE.NORMAL,  # hotfix 修复评论名称点击跳转的问题
            'at_nickname': escape(at_nickname),
        }

    def get_all_comments(self, need_newest=False, start_num=0, count=2, new_order=False, return_images=False):
        """
        获取全部子评论，引用时注意传参，默认获取全部的评论(老逻辑)，考虑性能问题不要这么整……
        need_newest 改成了 获取时间最早的两条评论
        :param need_newest: 是否需要最新的
        :param start_num:
        :param count:
        :param return_images:  是否返回评论图片
        :return:
        """
        comments = AnswerReply.objects.filter(first_reply=self, is_online=True)
        if need_newest and not new_order:
            comments = comments.order_by("-id")[start_num: start_num + count]

        if new_order:
            comments = comments.order_by("id")[start_num: start_num + count]

        result = []
        comment_ids = []
        for comment in comments:
            comment_ids.append(comment.id)
            result.append(comment.get_comment_data())
        image_dict = defaultdict(list)
        if return_images:
            images = AnswerReplyImages.objects.filter(reply_id__in=comment_ids).values(
                'reply_id', 'url', 'width', 'height',
            )
            for image in images:
                image_dict[image['reply_id']].append({
                    'image_url': image['url'],
                    'width': image['width'],
                    'height': image['height'],
                })
            for item in result:
                item['images'] = image_dict.get(item['comment_id'], [])

        return result

    def get_data_for_reply(self, user=None, has_comment=False, need_newest=False, new_order=False,
                           top_comment_id=None):
        is_vote = False
        if user:
            is_vote = AnswerVoteReply.objects.filter(user=user.id, answerreply=self).exists()

        data = {
            'content': escape(self.content),
            'reply_id': self.id,
            'reply_date': get_timestamp_or_none(self.create_time),
            'is_liked': is_vote,
            'favor_amount': self.like_num,
            'reply_count': self.comment_num,
            'user_id': self.user.id,
            'user_type': USER_TYPE.NORMAL,
            'gm_url': get_user_gm_url(self.user.id),
            'user_nickname': self.user.nickname,
            'user_portrait': self.user.portrait,
            'membership_level': self.user.membership_level,
            'comments': [],
            'user_level': get_user_level(self.user),
            "college_id": self.user.get("college_id", 0),
            "college_name": self.user.get("college_name", ""),
        }
        if need_newest and not new_order:
            data['comments'] = self.get_all_comments(need_newest=True, return_images=True)  # 取最新两条
        elif has_comment and not new_order:
            data['comments'] = self.get_all_comments(return_images=True)  # 保留原始逻辑
        elif new_order:
            data['comments'] = self.get_all_comments(new_order=new_order, return_images=True)

        # 回答的二级评论置顶
        if top_comment_id:
            second_level_comment = AnswerReply.objects.filter(id=top_comment_id, is_online=True).first()
            if second_level_comment:
                data['comments'] = [item for item in data['comments'] if
                                    item.get("comment_id", 0) != int(top_comment_id)]
                data['comments'].insert(0, second_level_comment.get_comment_data())

        return data

    def reply_data_after_create(self):

        photo = get_thumb_path(u'img%2Fuser_portrait.png')
        portrait = get_thumb_path(self.user.portrait) if self.user.portrait else photo

        user_type = USER_TYPE.NORMAL

        reply_data = {
            'reply_id': self.id,
            'user_type': user_type,
            'user_id': self.user_id,
            'reply_user_id': self.answer.user_id,
            'user_nickname': self.user.nickname or u'昵称未设置',
            'user_portrait': portrait,
            'content': escape(self.content),
            'reply_date': get_humanize_datetime(self.create_time),
            'reply_timestamp': get_timestamp(self.create_time),
            'comments': [],
            'reply_count': self.comment_num,
            'vote_count': self.like_num,
        }
        return reply_data

    def comment_data_after_create(self):
        if self.commented_reply and self.commented_reply.user == self.user:
            at_nickname = u''
        elif self.commented_reply:
            at_nickname = self.commented_reply.user.nickname
        else:
            at_nickname = u''

        user_type = USER_TYPE.NORMAL

        data = {
            'comment_id': self.id,
            'nickname': self.user.nickname,
            'reply_id': self.commented_reply and self.commented_reply.id or '',
            'at_nickname': escape(at_nickname),
            'at_user_id': self.commented_reply.user_id if self.commented_reply else 0,
            'at_user_type': user_type,
            'comment_user_id': self.user.id,
            'comment_user_type': get_auth_type_by_userid(self.user_id),
            'content': escape(self.content),
            'reply_count': self.comments.count(),
            'vote_count': self.like_num,
        }
        return data

    def get_reply_for_mine(self):
        reply_data = {
            'user': {
                'doctor_name': '',
                'user_name': '',
                'doctor_id': '',
                'user_id': self.user_id,
                'user_portrait': '',
                'doctor_portrait': '',
            },
            'reply': {
                'replied_topic_id': self.commented_reply and self.commented_reply.id or '',
                'content': escape(self.content),
                'reply_id': self.id,
                'commented_reply_id': self.commented_reply and self.commented_reply.id or '',
                'reply_date': get_timestamp_or_none(self.create_time),
                'image': '',
                'is_new': not self.is_read,
                'commented_content': escape(self.commented_reply.content) if self.commented_reply else None,
                'commented_author_id': self.commented_reply.user_id if self.commented_reply else None,
            },
            'answer': {
                'user': {
                    'nickname': '',
                    'id': self.answer.user_id,
                    'portrait': '',
                },
                'id': self.answer_id,
                'title': self.answer.content,
                'is_deleted': not self.answer.is_online
            },
            'topic': None,
            "diary": None,
        }
        return reply_data

    @classmethod
    def get_replys(cls, answer_id, last_id, size):
        """
        获取一级评论和次级评论，一级评论按照时间先后排序，次级评论紧跟一级评论
        :return:
        """
        replys = list()
        top_replys = list(cls.objects.filter(
            answer_id=answer_id,
            first_reply__isnull=True,
            is_online=True,
            id__gt=last_id
        )[0: size])

        # 依次查一级评论下二级评论，总数够size则返回
        for top_reply in top_replys:
            replys.append(top_reply)
            if len(replys) == size:
                break

            # 查询当前一级评论的次级评论
            sub_replys = list(cls.objects.filter(
                answer_id=answer_id,
                first_reply_id=top_reply.id,
                is_online=True,
            )[0: size - len(replys)])

            replys.extend(sub_replys)

            if len(replys) == size:
                break

        return replys

    def to_dict(self):
        return {
            'id': self.id,
            'user_id': self.user_id,
            'user_name': self.user.nickname,
            'content': escape(self.content),
            'answer_id': self.answer_id,
            'first_reply': self.first_reply_id,
            'commented_reply': self.commented_reply_id,
            'is_online': self.is_online,
            'like_num': self.like_num,
            'create_time': get_timestamp_or_none(self.create_time),
            'topicreply_id': self.topicreply_id,
            'is_read': self.is_read,
            'is_fake': self.is_fake,
        }

    @classmethod
    def get_sub_reply_ids(cls, reply_id):
        reply_ids = cls.objects.filter(commented_reply_id=reply_id, is_online=True).values_list('id', flat=True)
        sub_reply_ids = []
        sub_reply_ids.extend(reply_ids)
        for reply_id in reply_ids:
            sub_reply_ids.extend(cls.get_sub_reply_ids(reply_id))
        return sub_reply_ids


class AnswerVoteReply(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_answerreply_vote'
        unique_together = ('user', 'answerreply')

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id")
    answerreply = models.ForeignKey(AnswerReply, verbose_name=u"评论")
    unread = models.BooleanField(default=True)
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)

    def to_dict(self):
        data = {
            'reply_id': self.answerreply_id,
            'nickname': "",
            'user_id': self.user_id,
            'vote_time': get_timestamp_or_none(self.update_time),
            'membership_level': "",
            'type': VOTEOBJECT.ANSWER_REPLY,
            'portrait': "",
        }

        return data


class UserAnswerQuestion(models.Model):
    """
    过渡方案，用于用户的问题与答案混排
    """

    class Meta:
        app_label = 'qa'
        db_table = 'api_user_question_answer'
        verbose_name = u'用户问答混排表'

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id")
    answer = models.ForeignKey(Answer, verbose_name=u'用户回答', blank=True, null=True)
    question = models.ForeignKey(Question, verbose_name=u'用户提问', blank=True, null=True)
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    is_online = models.BooleanField(verbose_name=u'问答及其相关是否在线', default=True)


class AnswerTop(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_answer_top'

    question = models.ForeignKey(Question, default=None)
    answer = models.ForeignKey(Answer, default=None)
    order = models.IntegerField('展示顺序', unique=True, default=0)
    top_type = models.IntegerField('置顶类型', default=0)
    start_time = models.DateTimeField('上线时间', default=None)
    end_time = models.DateTimeField('下线时间', default=None)
    enable = models.BooleanField('是否上线', default=True)
    create_time = models.DateTimeField(auto_now_add=True)


class ZoneManager(RPCMixin):
    def __call__(self, pk_list):
        return self.call_rpc('api/zone/list', pks=pk_list)


class OverHeadAnswer(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = "api_overheadanswer"
        verbose_name = u'圈子推荐回答'

    zone = fields.MagicField(type=int, manager=ZoneManager, ttl=60, db_column="zone_id")
    answer = models.ForeignKey(Answer, help_text=u'回答', null=False, default='')
    rank = models.IntegerField(help_text=u'排序顺序', null=False, default=0)
    created_time = models.DateTimeField(help_text=u'创建时间', auto_now_add=True)
    deleted = models.BooleanField(help_text=u'是否已删除', default=False)


class OverHeadQuestion(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = "api_overheadquestion"
        verbose_name = u'圈子推荐问题'

    zone = fields.MagicField(type=int, manager=ZoneManager, ttl=60, db_column="zone_id")
    question = models.ForeignKey(Question, help_text=u'问题', null=False, default='')
    rank = models.IntegerField(help_text=u'排序顺序', null=False, default=0)
    created_time = models.DateTimeField(help_text=u'创建时间', auto_now_add=True)
    deleted = models.BooleanField(help_text=u'是否已删除', default=False)


class QuestionDoctor(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_question_doctor'
        verbose_name = u'问题tag相关联的美购智能推荐前20个医生'

    question = models.ForeignKey(Question)
    doctor_id = models.CharField(max_length=100, verbose_name=u'医生ID')
    unread = models.BooleanField(default=True, verbose_name=u'消息是否已读')
    create_time = models.DateTimeField(auto_now_add=True)


class QuestionInviter(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = "api_question_inviter"
        verbose_name = u'邀请回答'

    user = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="user_id", verbose_name="邀请人")  # 邀请人
    inviter = fields.MagicField(type=int, manager=UserManager, ttl=60 * 5, db_column="inviter_id",
                                verbose_name="被邀请人")  # 被邀请人
    invite_time = models.DateTimeField(verbose_name="邀请时间", auto_now_add=True)
    question = models.ForeignKey(Question, verbose_name="被邀请的问题", related_name="invite_question", null=True, blank=True)
    answer = models.ForeignKey(Answer, verbose_name="被邀请的问题回答", related_name="invited", null=True, blank=True)
    user_read = models.BooleanField(verbose_name="邀请者对被邀请人的回答，是否已读", default=False)


class QuestionAnswer(models.Model):
    class Meta:
        app_label = 'doris'
        db_table = "question_answer"
        verbose_name = u'最佳回答'

    question_id = models.CharField(max_length=64, verbose_name="问题id")
    answer_id = models.CharField(max_length=64, verbose_name="回答id")


class InterestForContentUser(models.Model):
    class Meta:
        app_label = "qa"
        db_table = "interest_for_content_user"
        verbose_name = u'对内容有兴趣的用户'

    business_id = models.IntegerField(verbose_name="业务id")
    business_type = models.CharField(verbose_name="业务类型", max_length=64)
    handle_date = models.DateField(verbose_name="业务产生日期")
    push_type = models.IntegerField(
        verbose_name="push 类型", default=AUTOMATED_PUSH.BASE_USER_INTEREST_FOR_CONTENT)
    device_id = models.CharField(verbose_name="设备id", max_length=128)
    related_id = models.IntegerField(verbose_name="关联id")
    related_type = models.CharField(verbose_name="关联业务类型", max_length=64)
    score = models.FloatField(verbose_name="smart_rank")
    sort_index = models.IntegerField(verbose_name="排序")
    status = models.IntegerField(verbose_name="推送状态", default=0)
    create_time = models.DateTimeField(verbose_name=u'创建时间', auto_now_add=True)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)


class QuestionFavor(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = "api_question_favor"
        verbose_name = u'问题收藏'
        index_together = ['user_id', 'question_id']

    user_id = models.IntegerField(verbose_name='收藏者id')
    question_id = models.IntegerField(db_index=True)
    create_time = models.DateTimeField(default=timezone.now())
    update_time = models.DateTimeField(auto_now=True)
    is_online = models.BooleanField(default=True)
    is_read = models.BooleanField(default=False)


class AnswerFavor(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = "api_answer_favor"
        verbose_name = u'回答收藏'
        index_together = ['user_id', 'answer_id']

    user_id = models.IntegerField(verbose_name='收藏者id')
    answer_id = models.IntegerField(db_index=True)
    create_time = models.DateTimeField(default=timezone.now())
    update_time = models.DateTimeField(auto_now=True)
    is_online = models.BooleanField(default=True)
    is_read = models.BooleanField(default=False)


class AnswerReplyImages(models.Model):
    class Meta:
        app_label = 'qa'
        db_table = 'api_answer_reply_images'
        verbose_name = u'回答评论图片'

    url = ImgUrlField(img_type=IMG_TYPE.TOPICREPLY, max_length=300, verbose_name=u'图片地址')
    width = models.IntegerField(verbose_name="图片宽度", default=0)
    height = models.IntegerField(verbose_name="图片高度", default=0)
    create_time = models.DateTimeField(verbose_name=u'创建时间', default=timezone.now)
    update_time = models.DateTimeField(verbose_name=u'更新时间', auto_now=True)
    reply_id = models.IntegerField('评论ID', db_index=True)


class StrategyAnswerSmrScore(models.Model):
    class Meta:
        db_table = 'strategy_answer_new_smart_rank_score'

    answer_id = models.IntegerField(unique=True)
    smart_rank_score = models.FloatField(verbose_name=u'新的smr_score', blank=True, default=0)
    create_date = models.BigIntegerField(max_length=50, verbose_name="时间")


class SrategyAnswerSmrScore(models.Model):
    class Meta:
        db_table = 'strategy_answer_smr_score'

    answer_id = models.IntegerField(unique=True)
    smart_rank_score = models.FloatField(verbose_name=u'新的smr_score', blank=True, default=0)
    create_date = models.BigIntegerField(max_length=50, verbose_name="时间")


class SearchStrategyAnswerSmrScore(models.Model):
    class Meta:
        db_table = 'search_strategy_answer_new_smart_rank_score'

    answer_id = models.IntegerField(unique=True)
    smart_rank_score = models.FloatField(verbose_name=u'新的smr_score', blank=True, default=0)
    create_date = models.BigIntegerField(max_length=50, verbose_name="时间")
    new_goodclick = models.FloatField(verbose_name=u'新的点击分', blank=True, default=0)
    smart_rank_v2 = models.FloatField(verbose_name=u'新的smr_score', blank=True, default=0)
