# coding=utf8 from __future__ import unicode_literals, absolute_import, print_function from django.conf import settings from django.db import models from django.db.models import Q from django.utils import timezone from django.utils.html import escape from gm_types.gaia import DOCTOR_TYPE from gm_types.gaia import PROBLEM_FLAG_CHOICES from gm_types.gaia import TOPIC_TYPE from gm_types.gaia import USER_TYPE from gm_types.mimas import TRACTATE_SORT_TYPE from gm_upload import IMG_TYPE from gm_upload import ImgUrlField from talos.libs.datetime_utils import get_humanize_datetime from talos.libs.datetime_utils import get_timestamp from talos.libs.datetime_utils import get_timestamp_or_none from talos.models.diary.diary import Diary from talos.models.topic.topic import Problem from talos.rpc import CODES from talos.rpc import gen from talos.rpc import logging_exception from talos.services.doctor import DoctorService from talos.services.hospital import HospitalService from talos.services.user import UserService class TopicReply(models.Model): """ 回复的内容 """ class Meta: verbose_name = u'011.话题回复' verbose_name_plural = u'011. 话题回复' db_table = 'api_topicreply' app_label = 'talos' diary = models.ForeignKey(Diary, verbose_name=u"关联的日记本", null=True, blank=True, default=None) problem = models.ForeignKey(Problem, verbose_name=u"话题", null=True, blank=True, default=None) user_id = models.IntegerField(verbose_name=u'用户外键id') doctor_id = models.CharField(max_length=100, null=True, blank=True, verbose_name=u'医生外键id') hospital_id = models.CharField(max_length=100, null=True, blank=True, verbose_name=u'关联的医院外键id') replied_topic = models.ForeignKey("self", default=None, null=True, blank=True, verbose_name=u"被回复的信息") commented_reply = models.ForeignKey('self', related_name=u'comments', verbose_name=u'被评论的回复', null=True, blank=True, default=None) content = models.TextField(verbose_name=u"回复内容") image = ImgUrlField(img_type=IMG_TYPE.TOPICREPLY, max_length=100, default="", verbose_name=u"图片", null=True, blank=True) audio = models.TextField(default="", verbose_name=u"音频", null=True, blank=True) is_new = models.BooleanField(default=True, verbose_name=u"是否新的回复", db_index=True) is_spam = models.BooleanField(verbose_name=u'是否为疑似广告', default=False, db_index=True) like_num = models.IntegerField(default=0, verbose_name=u"赞的数目") dislike_num = models.IntegerField(default=0, verbose_name=u"踩的数目") # FIXME: reply_date重命名成reply_time,避免混淆含义 reply_date = models.DateTimeField(verbose_name=u"回复时间", db_index=True, default=timezone.now) is_topic_append = models.BooleanField(verbose_name=u'是否是更新', default=False, db_index=True) is_online = models.BooleanField(default=True, help_text=u"是否可以上线", verbose_name=u"上线") is_fake = models.BooleanField(default=False, verbose_name=u"是否是灌水数据") def is_voted(self, user, reply): # TODO CR from .topicreplyvote import TopicReplyVote """ NOTE: 对一级评论进行点赞 """ is_voted = TopicReplyVote.objects.filter(user_id=user.id, topic_reply=reply).exists() if user else False return is_voted def get_reply_info(self): data = { 'id': self.id, 'uid': self.user_id, 'nickname': self.user.nickname, 'content': escape(self.content), 'reply_time': get_timestamp_or_none(self.reply_date), 'images': [], 'is_online': self.is_online, 'can_reply': not self.replied_topic, } return data def get_reply_info_for_doctor(self): data = { 'topic_id': self.problem.id, 'reply_date': get_timestamp_or_none(self.reply_date), 'like_num': self.like_num, 'my_content': escape(self.content), "old_content": self.replied_topic.content if self.replied_topic_id else ( self.problem.ask or self.problem.answer) } return data def __unicode__(self): return self.content def reply_data_after_create(self): if self.user_id: portrait = self.user.portrait else: portrait = u'' user_type = USER_TYPE.NORMAL user_id = self.user_id if self.doctor_id: d = DoctorService.get_doctor_from_doctor_id(self.doctor_id) if d.doctor_type == DOCTOR_TYPE.OFFICER: user_type = USER_TYPE.OFFICER user_id = d.hospital_id elif d.doctor_type == DOCTOR_TYPE.DOCTOR: user_type = USER_TYPE.EXPERT user_id = self.doctor_id reply_data = { 'reply_id': self.id, 'user_type': user_type, 'user_id': user_id, 'user_nickname': escape(self.user.nickname) or u'昵称未设置', 'user_portrait': portrait, 'content': escape(self.content), 'reply_date': get_humanize_datetime(self.reply_date), 'reply_timestamp': get_timestamp(self.reply_date), 'comments': [], 'reply_count': self.comments.count(), 'vote_count': self.like_num, } return reply_data def comment_data_after_create(self): if self.replied_topic and self.replied_topic.user_id == self.user_id: at_nickname = u'' elif self.replied_topic: at_nickname = self.replied_topic.user.nickname else: at_nickname = u'' user_type = 0 if self.doctor_id: d = DoctorService.get_doctor_from_doctor_id(self.doctor_id) if d.doctor_type == DOCTOR_TYPE.OFFICER: user_type = 2 elif d.doctor_type == DOCTOR_TYPE.DOCTOR: user_type = 1 data = { 'comment_id': self.id, 'nickname': escape(self.user.nickname) or u'昵称未设置', 'reply_id': self.replied_topic and self.replied_topic.id or '', 'at_nickname': escape(at_nickname), 'at_user_id': self.replied_topic.user_id if self.replied_topic else 0, 'at_user_type': user_type, 'comment_user_id': self.user_id, 'comment_user_type': '', 'content': escape(self.content), 'reply_count': self.comments.count(), 'vote_count': self.like_num, } return data def is_elite(self): """ 判断是否是热门回复 """ return self.like_num >= settings.ELITE_LIKE_NUM and True or False def get_reply_detail(self, user_id=None): from .replyheadline import ReplyHeadline # 避免循环引用 if self.user_id: city_name = self.user.city_name portrait = self.user.portrait else: city_name = u'' portrait = u'' user_type = 0 doctor_info = UserService.get_doctor_from_user_id(self.user_id) if doctor_info is not None and doctor_info.is_online: doctor = doctor_info if doctor.doctor_type == DOCTOR_TYPE.OFFICER: user_type = USER_TYPE.OFFICER hospital_id = doctor_info.hospital_id else: user_type = USER_TYPE.EXPERT hospital_id = None else: doctor = None hospital_id = None hospital = HospitalService.get_hospital_from_doctor_id(self.doctor_id) if hospital is not None: hospital_name_full = hospital.name or u'' else: hospital_name_full = u'' hospital_name = hospital_name_full[:14] if len(hospital_name_full) <= 14 else hospital_name_full[:14] + '...' reply_data = { 'user': { 'user_id': self.user_id, 'user_name': self.user.nickname, 'is_doctor': True if doctor else False, 'is_hospital': True if hospital_id is not None else False, 'doctor_id': doctor.id if doctor else '', 'doctor_name': doctor and doctor.name, 'hospital_id': hospital_id if hospital_id is not None else '', 'portrait': portrait, 'user_type': user_type, 'membership_level': self.user.membership_level, }, 'is_private': bool(user_id) and user_id == self.user_id, 'doctor_portrait': DoctorService.get_doctor_from_doctor_id(self.doctor_id).portrait if self.doctor_id else '', 'hospital_name': hospital_name, 'is_recommend_doctor': False, 'recommend_doctor_title': '', 'favored': False, 'images': [], 'is_star_user': False, 'city': city_name, 'is_topic_append': self.is_topic_append, 'reply': { 'reply_id': self.id, 'content': escape(self.content), 'favor_amount': self.like_num, 'reply_time': get_timestamp_or_none(self.reply_date), 'is_voted': bool(user_id) and self.topicreplyvote_set.filter(user_id=user_id, topic_reply=self).exists(), 'image': self.image, 'is_new': self.is_new, 'replied_topic_id': self.replied_topic and self.replied_topic.id or '', 'reply_date': get_timestamp_or_none(self.reply_date), 'commented_reply_id': ( self.replied_topic and self.commented_reply and self.commented_reply.id or '' ), 'like_num': self.like_num, 'is_best_reply': self.is_best_reply(), 'is_recommand': ReplyHeadline.is_recommend(self.id), # TODO CR 修改这个函数 'is_elite': self.is_elite(), 'comments_num': self.comments.count(), } } try: reply_data['diary'], reply_data['topic'], reply_data['answer'] = None, None, None if self.problem: topic_info = self.get_reply_topic_info() reply_data['topic'] = topic_info if self.diary: diary_info = self.get_reply_diary_info() reply_data['diary'] = diary_info author_id = self.problem and self.problem.user_id or self.diary.user_id if self.user_id == author_id: reply_data['user_titles'] = [{'title_type': 0, 'title_text': u'楼主'}, ] else: reply_data['user_titles'] = [] if self.is_topic_append: # 增加术后第几天的展示 if self.problem.operation_time: interval = self.reply_date.date() - self.problem.operation_time if interval.days >= 0: reply_data['interval'] = u'{}'.format(interval.days + 1) else: reply_data['interval'] = u'以前' else: reply_data['interval'] = u'' except: logging_exception() return reply_data def get_reply_topic_info(self): """ NOTE: 获取回复相关的帖子topic信息 :return: """ topic_info = { 'id': self.problem_id, 'title': ( self.replied_topic and self.replied_topic.id and escape(self.replied_topic.content) or escape(self.problem.answer) ), 'problem_ask': escape(self.problem.ask), 'problem_answer': escape(self.problem.answer), 'is_deleted': ( self.problem.flag == PROBLEM_FLAG_CHOICES.MARK_DELETED or not self.problem.is_online ), 'user': { 'id': self.problem.user_id, 'nickname': self.replied_topic and self.replied_topic.user.nickname or self.user.nickname }, } if self.problem.topic_type == TOPIC_TYPE.WEIXIN_NUM: topic_info['title'] = self.problem.get_title() return topic_info def get_reply_diary_info(self): """ NOTE: 获取回复相关的日记本信息 其中 显示的title做特殊处理 title = 'tag'+'日记' :return: """ title = escape(self.diary.title) tags_new_era = self.diary.tags_new_era if tags_new_era: title = tags_new_era[0]['name'] + u'日记' diary_info = { 'id': self.diary.id, 'title': title, 'is_deleted': not self.diary.is_online, 'user': { 'id': self.diary.user_id, 'nickname': UserService.get_user_by_user_id(self.diary.user_id).nickname, }, } return diary_info def comment_data(self): if self.replied_topic: if self.replied_topic.user_id == self.user_id: at_nickname = u'' at_doctor = None at_hospital_id = None else: if self.replied_topic.user_id is not None: replied_doctor_info = UserService.get_doctor_from_user_id(self.replied_topic.user_id) at_doctor = replied_doctor_info if at_doctor and at_doctor.is_online and at_doctor.doctor_type == DOCTOR_TYPE.OFFICER: at_hospital_id = at_doctor.hospital_id else: at_hospital_id = None else: at_doctor = None at_hospital_id = None if self.replied_topic.doctor_id: u = UserService.get_user_id_by_doctor_id(self.replied_topic.doctor_id) at_nickname = u and UserService.get_user_by_user_id(u).nickname or u'昵称未设置' else: at_nickname = self.replied_topic.user.nickname or u'昵称未设置' else: at_nickname = u'昵称未设置' at_doctor = None at_hospital_id = None if self.user_id: portrait = self.user.portrait else: portrait = u'' doctor_info = UserService.get_doctor_from_user_id(self.user_id) if doctor_info is not None and doctor_info.is_online: doctor = doctor_info if doctor.doctor_type == DOCTOR_TYPE.OFFICER: hospital_id = doctor.hospital_id else: hospital_id = None else: doctor = None hospital_id = None data = { 'type': 0, 'content': escape(self.content), 'at_nickname': at_nickname, 'at_doctor_id': at_doctor.id if at_doctor else '', 'at_hospital_id': at_hospital_id if at_hospital_id is not None else '', 'user_portrait': portrait, 'at_user_id': self.replied_topic.user_id if self.replied_topic else '', 'nickname': '', 'comment_id': self.id, 'comment_time': self.reply_date.strftime('%m.%d'), 'comment_expert_id': doctor.id if doctor else '', 'at_expert_id': at_doctor.id if at_doctor else '', 'user_id': self.user_id, 'user_is_doctor': True if doctor else False, 'user_is_hospital': True if hospital_id is not None else False, 'doctor_id': doctor.id if doctor else '', 'hospital_id': hospital_id if hospital_id is not None else '', 'membership_level': self.user.membership_level, 'is_best_reply': self.is_best_reply(), } if self.doctor_id is not None: d = DoctorService.get_doctor_from_doctor_id(self.doctor_id) data['nickname'] = d.name if d.user != settings.SUOZHANG_UID: data['nickname'] += u' 医生' else: data['nickname'] = self.user.nickname or u'昵称未设置' return data @classmethod def get_by_id(cls, id): try: return TopicReply.objects.get(pk=id) except: return None def is_best_reply(self): """ 是否是优质问答 """ return self.replyheadline_set.filter(is_online=True).exists() def update_reply_num(self, is_online=False): real_replynum = 1 + TopicReply.objects.filter(commented_reply=self).count() if self.problem: if is_online: self.problem.reply_num += real_replynum self.problem.update_reply_num(is_online=True, reply_num=real_replynum) else: self.problem.reply_num -= real_replynum self.problem.update_reply_num(is_online=False, reply_num=real_replynum) if self.problem.reply_num < 0: return False self.problem.save() elif self.diary: if is_online: self.diary.reply_num += real_replynum else: self.diary.reply_num -= real_replynum if self.diary.reply_num < 0: return False self.diary.save() return True @classmethod def get_replies_info(cls, user, replies, start_num=0, count=10, comment_id=None, new_sub_comment_count=False, is_xcx=False): from talos.manager import comment_data_by_reply_ids, get_reply_details_by_reply_ids result = [] need_add_reply_id = None if comment_id: top_comment = TopicReply.objects.filter(id=comment_id, commented_reply_id__isnull=True).first() replies = list(replies) if not top_comment: reply = TopicReply.objects.get(id=comment_id) replies = [item for item in replies if item.id != reply.commented_reply.id] replies = [reply.commented_reply] + replies if is_xcx and start_num == 0: need_add_reply_id = reply.commented_reply_id else: replies = [item for item in replies if item.id != top_comment.id] replies = [top_comment] + replies _replies = replies[start_num: start_num + count] reply_ids = [r.id for r in _replies] comments_ids = list(TopicReply.objects.filter(Q(commented_reply__id__in=reply_ids) & Q(is_online=True)) .values_list('id', flat=True)) comments_data = comment_data_by_reply_ids(comments_ids) topicreplys_data = get_reply_details_by_reply_ids(reply_ids, user.id) for reply in _replies: replies_data = None for _reply in topicreplys_data: if _reply['reply']['reply_id'] == reply.id: replies_data = _reply break if replies_data is None: continue reply_info = replies_data['reply'] if reply.replyheadline_set.count() == 0: replies_data['reply']['is_recommand'] = False else: replies_data['reply']['is_recommand'] = True replies_data['reply']['is_elite'] = (1 if reply_info['favor_amount'] >= settings.ELITE_LIKE_NUM else 0) replies_data['comments'] = [] if new_sub_comment_count: _reply_comments_ids = list(reply.comments.filter( is_online=True ).order_by('-id').values_list('id', flat=True))[:2] # 取最新两条 else: _reply_comments_ids = list(reply.comments.filter( is_online=True ).values_list('id', flat=True)) second_top_comment = None for c in comments_data: if c['comment_id'] in _reply_comments_ids: replies_data['comments'].append(c) if comment_id and not second_top_comment and c['comment_id'] == int(comment_id): second_top_comment = c # 将二级评论 置顶 if need_add_reply_id and need_add_reply_id == reply.id and second_top_comment: replies_data['comments'] = [item for item in replies_data['comments'] if item['comment_id'] != int(comment_id)] replies_data['comments'].insert(0, second_top_comment) result.append(replies_data) return result @classmethod def get_replies_list_info( cls, user, query, start_num=0, count=10, comment_id=None, new_sub_comment_count=False ): """ NOTE: 首先获取热门评论数目 如果不够,则继续获取最新评论 获取 日记本 帖子 评论列表信息 :param user: :param start_num: :param count: :param new_sub_comment_count: 子评论展示个数控制, 7130 以及评论下展示两个子评论 :return: """ recommand_reply = Q(like_num__gte=settings.ELITE_LIKE_NUM) recommand_reply &= query replies = TopicReply.objects.filter(recommand_reply) hot_count = replies.count() if comment_id: myreplies = TopicReply.objects.filter(id=comment_id) replies = myreplies | replies result = [] if hot_count > start_num: replies = replies.order_by('-like_num', '-reply_date') result = cls.get_replies_info( user, replies, start_num=start_num, count=count, comment_id=comment_id, new_sub_comment_count=new_sub_comment_count ) if len(result) < count: if start_num < hot_count: start_num = 0 else: start_num = start_num - len(result) - hot_count count -= len(result) last_query = Q(like_num__lt=settings.ELITE_LIKE_NUM) last_query &= query last_replies = cls.objects.filter(last_query).order_by('-id').exclude(recommand_reply) last_result = cls.get_replies_info( user, last_replies, start_num=start_num, count=count, comment_id=comment_id, new_sub_comment_count=new_sub_comment_count, ) result.extend(last_result) return result @classmethod def reply_detail_diary(cls, user, reply_id, start_num=0, count=10): from talos.manager.topic_reply import comment_data_by_reply_ids try: reply = TopicReply.objects.get(pk=reply_id) except TopicReply.DoesNotExist: return gen(CODES.TOPIC_NOT_FOUND) topic = reply.problem result = { 'comments': [], 'topic_id': topic.id, 'title': topic.diary.title, 'comment_count': topic.topicreply_set.count(), 'reply_id': reply.id, 'is_private': False if not user else user.id == reply.user_id, 'doctor_id': '', 'doctor_portrait': '', 'doctor_name': '', 'hospital_name': '', 'is_recommend_doctor': False, 'recommend_doctor_title': '', 'favored': False, 'favor_amount': topic.vote_amount, 'content': topic.answer, 'images': [i.get_image_data() for i in topic.images.all()], 'user_id': reply.user_id, 'user_nickname': reply.user.nickname, 'user_portrait': reply.user.portrait, 'is_star_user': False, 'reply_date': get_timestamp_or_none(topic.created_time), 'city': reply.user.city_name, 'is_topic_append': True, } doctor_reply_sql = """ (case when api_topicreply.doctor_id !='' then 1 else 0 end) """ comments = topic.topicreply_set.extra( select={'doctor_reply': doctor_reply_sql}) comments = comments.extra(order_by=['-doctor_reply', 'id']) comments = comments[start_num: start_num + count] _comments_ids = [c.id for c in comments] result['comments'] = comment_data_by_reply_ids(_comments_ids) return result @property def user(self): if hasattr(self, '_user_obj'): return self._user_obj self._user_obj = UserService.get_user_by_user_id(self.user_id) return self._user_obj @property def doctor(self): return DoctorService.get_doctor_from_doctor_id(self.doctor_id) @property def hospital(self): return HospitalService.get_hospital_from_hospital_id(self.hospital_id) def get_reply_for_mine(self): return { 'reply': { 'replied_topic_id': self.replied_topic_id or '', 'content': escape(self.content), 'reply_id': self.id, 'commented_reply_id': self.replied_topic_id and self.commented_reply_id or '', 'reply_date': get_timestamp_or_none(self.reply_date), 'image': self.image, 'is_new': self.is_new, 'commented_content': self.replied_topic.content if self.replied_topic else None, 'commented_user_id': self.replied_topic.user_id if self.replied_topic else None, }, 'user': { 'doctor_name': '', 'user_name': '', 'doctor_id': '', 'user_id': self.user_id, 'user_portrait': '', 'doctor_portrait': '', }, 'topic': { 'user': { 'nickname': '', 'id': self.problem.user_id, 'portrait': '', }, 'id': self.problem_id, 'problem_ask': escape(self.problem.ask), 'title': self.replied_topic_id and escape(self.replied_topic.content) or escape(self.problem.answer), 'is_deleted': self.problem.flag == PROBLEM_FLAG_CHOICES.MARK_DELETED or not self.problem.is_online, } if self.problem_id else None, 'diary': { 'user': { 'nickname': '', 'id': self.diary.user_id, 'portrait': '', }, 'id': self.diary_id, 'title': escape(self.diary.title), 'is_deleted': not self.diary.is_online, } if self.diary_id else None, 'answer': None, }