# -*- coding: utf8 -*- from __future__ import unicode_literals, absolute_import, print_function import datetime import re from django.conf import settings from django.db.models import Q from django.utils.html import escape from gm_types.gaia import ( FILTER_WORD_TYPE, DIARY_OPERATION, ) from gm_types.user_hierarchy import EventType from gm_rpcd.all import bind from gm_types.error import ERROR as CODES from talos.rpc import bind_context from utils.rpc import gen, rpc_client, logging_exception, get_current_user from talos.decorators import list_interface from talos.cache.takesofacache import take_sofa_cache from talos.libs.datetime_utils import get_timestamp_or_none from talos.libs.diaryrank_utils import DiaryRankTool from talos.libs.reply import is_reply_num_gt_limit from talos.manager.diary import diary_list_manager from talos.manager.topic_reply import comment_data_by_reply_ids from talos.manager.topic_reply import get_reply_details_by_reply_ids from talos.models.diary.diary import Diary from talos.models.topic import ReplyHeadline from talos.models.topic import TopicReply from talos.services import AntiSpamService from talos.services import UserService, get_user_from_context from talos.tools.filterword_tool import filterword_by_custom from talos.tasks import push_diary_reply_message, applet_diary_replied_push, applet_diary_reply_summary_push from user_hierarchy.portal import process_task @bind('diary/reply_create') def create_diary_reply(diary_id, content, reply_id=None, device_id=None): """ NOTE: DESC: 用户对日记本进行评论和回复 :param ctx: :param diary_id: :param reply_id: :param content: :return: """ user = get_current_user() if not user: return gen(CODES.LOGIN_REQUIRED) result = { 'comment_data': {}, 'reply_data': {}, 'diary': {}, 'doctor': {}, # if user is a doctor, add doctor.id } if not diary_id: return gen(CODES.DIARY_NOT_FOUND) # content not null if not content.strip(): return gen(CODES.REPLY_CAN_NOT_BE_EMPTY) replied_reply = None if reply_id: replied_reply = TopicReply.objects.filter(id=reply_id) if replied_reply: replied_reply = replied_reply[0] else: replied_reply = None if replied_reply and replied_reply.diary: diary = replied_reply.diary else: try: diary = Diary.objects.get(id=diary_id) except Diary.DoesNotExist: return gen(CODES.DIARY_NOT_FOUND) result['tags'] = diary.tags_new_era result['diary']['id'] = diary_id doctor_id = UserService.get_doctor_id_from_user_id(user.id) if doctor_id: result['doctor']['id'] = doctor_id # 检查用户在一分钟内回复(日记本/帖子)是否大于limit条 if is_reply_num_gt_limit(user=user): return gen(CODES.COMMENT_BUSY) # 检查日记贴评论敏感词,以前的代码进行了数字的过滤,本次沿用 content = content if content else "" filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_REPLY, content=content) # 查看最近用户有没有回复内容一样的留言 try: reply = TopicReply.objects.filter(diary_id=diary.id, user_id=user.id) last_id = reply.latest('id') previous_reply = TopicReply.objects.get(id=last_id.id) if previous_reply.content == content: return gen(CODES.REPLY_CAN_NOT_BE_REPEATED) except TopicReply.DoesNotExist: pass if diary.user_id == user.id: if reply_id: d = DiaryRankTool(diary.id) t = datetime.datetime.now() replied_user_id = replied_reply.user.id d.add_heat(DIARY_OPERATION.SELF_REPLY, t, extra_id=replied_user_id) elif device_id: d = DiaryRankTool(diary.id) t = datetime.datetime.now() d.add_heat(DIARY_OPERATION.OTHER_REPLY, t, extra_id=device_id) diary.reply_num += 1 commented_reply = ( replied_reply.commented_reply if replied_reply and replied_reply.commented_reply else replied_reply ) topic_reply = TopicReply() topic_reply.diary = diary topic_reply.user_id = user.id topic_reply.doctor_id = doctor_id topic_reply.content = content topic_reply.replied_topic = replied_reply topic_reply.commented_reply = commented_reply topic_reply.save() diary.save() # 发送小程序推送 # applet_diary_replied_push(topic_reply.id) # applet_diary_reply_summary_push(topic_reply.id) # 给客户端返回最近回复的信息 try: reply_data = topic_reply.reply_data_after_create() except: reply_data = {} logging_exception() try: comment_data = topic_reply.comment_data_after_create() except: logging_exception() comment_data = {} # push 异步消息通知 # replied_reply 被回复的TopicReply对象 diary_user_id = diary.user_id if diary else None replied_reply_user_id = replied_reply.user_id if replied_reply else None push_diary_reply_message(user_id=user.id, diary_user_id=diary_user_id, reply_id=reply_id, content=content, replied_reply_user_id=replied_reply_user_id) take_sofa_cache.del_diary_cache_data(diary_id) # add point,growth growth_value, point_value, submit_count = 0, 0, 0 if not replied_reply: event_data = process_task(user_id=user.id, event_type=EventType.COMMENT_OTHER, related_item=topic_reply.id) growth_value, point_value, submit_count = event_data['growth_value'], event_data['point_value'], event_data[ 'submit_count'] result['reply_data'] = reply_data result['comment_data'] = comment_data result['point_value'] = point_value result['growth_value'] = growth_value result['submit_count'] = submit_count return result def get_diary_replies_info(user, replies, start_num=0, count=10): return TopicReply.get_replies_info(user, replies, start_num, count) @bind_context('diary/replies') @list_interface(offset_name='start_num', limit_name='count') def get_diary_replies(ctx, diary_id, start_num=0, count=10): """ NOTE: DESC: 获取日记本的评论列表 :param ctx: :param diary_id: :param start_num: :param count: :return: ``` 排序的规则: 先热门按照点赞数排序(热门定义为 点赞数大于5) 然后非热门的按照回复时间排序 重点说明: 其中start_num计算的地方 假设某一个diary 中热门评论15条,普通评论40条 1 取第一分页,则直接可以从热门评论中直接取到(start_num=0, count=10) 2 取第二分页(start_num=11, count=10), 返回的数据中既有热门, 也有非热门 所以需要进行start_num的重新点位, start_num = 0 if (start_num < hot_reply_num + count) else (start_num - hot_reply_num) 3 取第三页 start_num = start_num - hot_reply_num ``` """ user = get_user_from_context(ctx) if not diary_id: return gen(CODES.DIARY_NOT_FOUND) try: diary = Diary.objects.get(id=diary_id) if not diary.is_online: return gen(CODES.DIARY_NOT_FOUND) except Diary.DoesNotExist: return gen(CODES.DIARY_NOT_FOUND) query = Q(diary_id=diary_id) query &= Q(commented_reply__isnull=True) query &= Q(is_online=True) result = TopicReply.get_replies_list_info(user, query, start_num=start_num, count=count) return result @bind_context('diary/top_replies') @list_interface(offset_name='start_num', limit_name='count') def get_elite_reply_list(ctx, diary_id, start_num=0, count=3): """ get elite replies or recently created replies. changelog: 20160603 老逻辑:有热门评论 按照赞数倒序返回热门评论(不管几条) 没有热门评论 按照时间倒序返回非热门(3条) 新逻辑:总共返回四条评论 如果有热门评论 按照时间倒序返回热门评论 不足4条 按照时间倒序返回非热门评论 """ user = get_user_from_context(ctx) if not diary_id: return gen(CODES.DIARY_NOT_FOUND) try: diary = Diary.objects.get(id=diary_id) except Diary.DoesNotExist: return gen(CODES.DIARY_NOT_FOUND) if diary.is_online: hot_query = Q(diary=diary) hot_query &= Q(commented_reply__isnull=True) hot_query &= Q(is_online=True) hot_query &= (Q(replyheadline__is_online=True) | Q(like_num__gte=settings.ELITE_LIKE_NUM)) # 新逻辑 def build_comments(replies): local_comments = get_reply_details_by_reply_ids(replies, user.id) return local_comments comments = [] elite_replies = TopicReply.objects.filter(hot_query).values_list('id', flat=True).order_by( '-replyheadline__is_online', '-like_num', '-reply_date' ) if elite_replies: comments.extend(build_comments(list(elite_replies)[:4])) is_elite = 1 if elite_replies else 0 if elite_replies.count() < count: query = Q(diary=diary) query &= Q(commented_reply__isnull=True) query &= Q(is_online=True) query &= Q(like_num__lt=settings.ELITE_LIKE_NUM) replies = TopicReply.objects.filter(query).values_list('id', flat=True).order_by('-id').exclude(hot_query) comments.extend(build_comments(list(replies)[:count - elite_replies.count()])) total_count = TopicReply.objects.filter(diary=diary, is_online=True).count() result = { 'comments': comments, 'is_elite': is_elite, 'total_count': total_count } return result return gen(CODES.DIARY_NOT_FOUND) @bind('diary/comment_info') def get_comment_info(id): """get reply detail. args: id: topicreply.id NOTE: 获取评论的详细信息 """ try: topicreply = TopicReply.objects.get(pk=id) except TopicReply.DoesNotExist: return gen(CODES.TOPIC_REPLY_NOT_FOUND) comment_info = {} comment_info['content'] = escape(topicreply.content) comment_info['comment_time'] = get_timestamp_or_none( topicreply.reply_date ) user = UserService.get_user_by_user_id(topicreply.user_id) comment_info['user_name'] = user.nickname comment_info['user_portrait'] = user.portrait comment_info['user_id'] = topicreply.user_id comment_info['membership_level'] = user.membership_level comment_info['comment_id'] = topicreply.id comment_info['is_hot'] = topicreply.is_elite() comment_info['is_recommend'] = ReplyHeadline.is_recommend(topicreply.id) user_bz_info = UserService.get_doctor_hospital_id_by_user_id(topicreply.user_id) if user_bz_info: comment_info.update(user_bz_info) return comment_info # @bind_context('talos/diary/reply') @bind_context('diary/get_reply') def get_diary_reply_by_id(ctx, reply_id): """ :param ctx: :param reply_id: :return: """ user = get_user_from_context(ctx) try: reply = TopicReply.objects.get(pk=reply_id) except TopicReply.DoesNotExist: return gen(CODES.TOPIC_REPLY_NOT_FOUND) replies_data = reply.get_reply_detail(user.id) 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) comments = reply.comments.filter(is_online=True) comments_ids = list(comments.values_list('id', flat=True)) comments_data = comment_data_by_reply_ids(comments_ids) replies_data[u'comments'] = comments_data return replies_data @bind('diary/diary_smart_index') def get_diary_smart_index(id, comment_id): """ NOTE: 定位评论在那个页面, 定位锚点 :param id: diary id :param comment_id: topicreply id :return: """ try: diary = Diary.objects.get(id=id) except Diary.DoesNotExist: return gen(CODES.DIARY_NOT_FOUND) try: specific_reply = TopicReply.objects.get(pk=comment_id, diary=diary) except TopicReply.DoesNotExist: return gen(CODES.TOPIC_REPLY_NOT_FOUND) except ValueError: # http://sentry.gengmei.cc/sentry/prod-gaia/issues/592/ return gen(CODES.UNKNOWN_ERROR) query = Q(commented_reply__isnull=True) query &= Q(is_online=True) query &= Q(diary=diary) if specific_reply.like_num >= settings.ELITE_LIKE_NUM: query &= Q(like_num__gte=specific_reply.like_num) replies = TopicReply.objects.filter(query) query = Q(reply_date__gte=specific_reply.reply_date) query |= Q(like_num__gte=specific_reply.like_num) count = replies.filter(query).count() else: query &= (Q(like_num__gte=settings.ELITE_LIKE_NUM) | Q(reply_date__gte=specific_reply.reply_date)) count = TopicReply.objects.filter(query).count() return count