# -*- coding: utf8 -*- from __future__ import unicode_literals, absolute_import, print_function import datetime from collections import defaultdict, namedtuple from django.db.models import Q, Count from django.utils.html import escape from gm_types.error import ERROR as CODES from gm_types.gaia import TOPIC_ORDER_IN_DIARY, DIARY_AUDIT_STATUS from gm_types.gaia import PROBLEM_FLAG_CHOICES, TAG_TYPE from gm_rpcd.all import bind from talos.rpc import bind_context from talos.statistic import diary_view_increase_num, topic_view_increase_num from talos.libs.datetime_utils import get_timestamp_or_none from talos.libs.image_utils import get_full_path from talos.libs.datetime_utils import get_timestamp from talos.manager.topic import topic_list_manager from talos.manager.diary import ( diary_list_manager, get_diary_tagv3_info_by_diary_ids, get_diary_tag_info_by_diary_ids, ) from talos.models.diary import Diary, DiaryFavor, DiaryTag, BDDiary, DiaryCheck from talos.models.topic import Problem from talos.services import ( get_user_from_context, UserService, GoodsService, TagService, UserConvertService, ) from talos.tasks.diary_xml import get_diaries_score from utils.rpc import gen from utils.group_routine import GroupRoutine def _get_topic_pageview(id): return Problem.get_view_amount_of_id(id) def get_diary_card_info_by_ids(ids): if len(ids) == 0: return [] diaries = Diary.objects.filter(id__in=ids, is_online=True) result = [] user_ids = [d.user_id for d in diaries] diary_users = UserService.get_users_by_user_ids(user_ids) for d in diaries: diary_user = diary_users[d.user_id] if d.user_id in diary_users else None result.append({ 'id': d.id, 'tags': d.wiki_info, "images": d.cover, # (-half) "view_count": d.view_num, "vote_count": diary_user.vote_count if diary_user else 0, "comment_count": d.reply_num, "author_name": diary_user.nickname if diary_user else '', # (发布该日记的作者) "city": diary_user.city_name if diary_user else '', # (发布该日记的作者的地区) }) return result @bind_context('diary/detail') def diary_detail(ctx, id, need_order=False, simple_data=False): group_diary_factor = 3.7 from talos.models.topic import TopicImage try: diary = Diary.objects.get(pk=id, is_online=True) except Diary.DoesNotExist: return None user = get_user_from_context(ctx) id = diary.id author_id = diary.user_id service_id = diary.service_id view_num = diary.view_num images = diary.cover topic_num = diary.topic_num tags = diary.tags_new_era tag_v3_infos = get_diary_tagv3_info_by_diary_ids(diary_ids=[diary.id]) pre_operation_image_num = diary.pre_operation_images.count() post_operation_image_query = { 'topic__diary_id': id, 'topic__is_online': True, } post_operation_image_num = TopicImage.objects.filter(**post_operation_image_query).count() operation_info = {} online_service_id = second_tag_id = None if not simple_data: operation_info = diary.operation_info() # 如果关联美购下线, 则取关联订单的医生名下同项目tag的美购(在线美购) online_service_id = GoodsService.get_diary_detail_related_service_id(diary.service_id, diary.order_id) second_tag_id = TagService.get_second_tag_id_by_diary_tags(tags) # 7715改版 筛选出日记本关联tag---只有一个 tag_data = GoodsService.get_tag_data(diary.all_tags) #日记本取出来的一级tag diary_count = Problem.objects.filter(user_id=diary.user_id, is_online=True).count() group_diary_num = DiaryTag.objects.filter(tag_id=tag_data.get('tag_id')).count() if tag_data.get('tag_id') else 0 recent_topic = diary.latest_topic if recent_topic is not None: recent_topic_content = escape(recent_topic.answer) else: recent_topic_content = None social_info = None ownership = False is_author = False is_login = False if user: from social.models import SocialInfo social_info = SocialInfo(uid=user.id) if user.id == diary.user_id: ownership = True is_author = True if user.id == author_id else False is_login = True is_following = social_info and social_info.is_following_user(uid=author_id) or False topic_reply_count = Problem.objects.filter(diary_id=id).all().aggregate(reply_num=Count('reply_num')) reply_num_for_diary = diary.topicreply_set.filter(is_online=True).count() # 日记本评论 vote_num = diary.vote_num is_voted = diary.is_voted_by(user) try: favord_count = diary.favor_diary_diary.filter(is_deleted=False).count() except DiaryFavor.DoesNotExist: favord_count = 0 is_favored = diary.favor_diary_diary.filter(user_id=user.id, is_deleted=False).exists() if user else False if diary.is_fake_operation_time or diary.operation_time is None: is_fake_operation_time = True else: is_fake_operation_time = False oldest_topic_create_time = diary.diary_first_topic and diary.diary_first_topic.created_time if oldest_topic_create_time and diary.operation_time: """ 需要使用 unix 基准时间来比较 防止出现边界问题 example: d1 = datetime.datetime(2017, 1, 2, 11, 11) d2 = datetime.datetime(2017, 1, 3, 10, 11) (d2 - d1).days = 0 会出现24小时跨天时间边界问题 """ unix_1970_datetime = datetime.datetime(1970, 1, 1) op_day = (diary.operation_time - unix_1970_datetime).days topic_day = (oldest_topic_create_time - unix_1970_datetime).days if op_day > topic_day: show_operation_day_info = False else: show_operation_day_info = True else: show_operation_day_info = True raw_hospital = diary.raw_hospital raw_doctor = diary.raw_doctor diary_user = UserService.get_user_by_user_id(diary.user_id) new_diary_convert_user = UserConvertService.get_user_info_by_user_id(diary.user_id) # 新的日记本用户信息处理逻辑 _diary_all_tags = diary.all_tags area_tags = diary.get_tags_from_tags_by_types(_diary_all_tags, [TAG_TYPE.CITY]) city_tag_id = None if area_tags: city_tag_id = area_tags[0].id # 日记本项目类型的标签 diary_item_tags = diary.get_tags_from_tags_by_types( _diary_all_tags, [TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM, TAG_TYPE.ITEM_WIKI] ) diary_item_tags = [tag.to_dict() for tag in diary_item_tags] diary_data = { 'id': id, 'author_id': author_id, 'service_id': service_id, 'view_num': view_num, 'images': images, 'topic_num': topic_num, 'tags': tags, 'tags_v3': tag_v3_infos.get(diary.id, []), 'city_tag_id': city_tag_id, 'operation_info': operation_info, 'pre_operation_image_num': pre_operation_image_num, 'post_operation_image_num': post_operation_image_num, 'online_service_id': online_service_id, 'recent_topic_content': recent_topic_content, 'cover_post': get_full_path(diary.post_operation_image) if diary.post_operation_image else '', 'ownership': ownership, 'is_following': is_following, 'is_favored': is_favored, 'is_voted': is_voted, 'reply_num_for_diary': reply_num_for_diary, 'vote_num': vote_num, 'is_fake_operation_time': is_fake_operation_time, 'second_tag_id': second_tag_id, 'show_operation_day_info': show_operation_day_info, 'raw_hospital': raw_hospital, 'raw_doctor': raw_doctor, 'city': diary_user.city_name, 'user_nickname': diary_user.nickname, 'user_portrait': diary_user.portrait, 'user_id': diary.user_id, 'membership_level': diary_user.membership_level, 'level_icon': diary_user.level_icon, 'membership_icon': diary_user.membership_icon, 'pre_operation_images': diary.pre_operation_images_info, 'title': diary.title or '', 'created_time': get_timestamp_or_none(diary.created_time), 'doctor_id': diary.doctor_id or '', 'hospital_id': diary.hospital_id or '', 'sticky_post': diary.sticky_post, 'favord_count': favord_count, 'tag_info': tag_data, 'is_author': is_author, 'count': diary_count, 'order_id': diary.order_id, 'polymer_tag': tag_data, 'all_comments_num': topic_reply_count.get('reply_num', 0) + reply_num_for_diary, 'group_diary_num': int(group_diary_num * group_diary_factor), 'is_login': is_login, 'share_count': diary.share_num, 'diary_item_tags': diary_item_tags, 'new_diary_convert_user': new_diary_convert_user, # 新增字段,用户信息整合 'last_topic': recent_topic and recent_topic.id or 0, # 最新一篇日记ID } if need_order: diary_data['order_id'] = diary.order_id return diary_data @bind('diary/detail/v2') def get_diary_detail_v2(diary_id): """日记本信息,仅包含基本内容 TODO tags, tags_v3应仅返回tag_id """ try: diary = Diary.get_by_id(diary_id=diary_id) except Diary.DoesNotExist: return None if not diary.is_online: return None diary_data = { 'diary_id': diary.id, 'title': diary.title, 'compare_image': diary.cover, 'author_uid': diary.user_id, 'tags_v3': diary.tags_v3, 'tags': diary.tags_new_era, 'tag_info': GoodsService.get_tag_data(diary.tags), 'order_id': diary.order_id, 'service_id': diary.service_id, 'doctor_id': diary.doctor_id, 'hospital_id': diary.hospital_id, 'rating': diary.rating, 'created_time': get_timestamp_or_none(diary.created_time), 'vote_num': diary.vote_num, 'cover_post': get_full_path(diary.post_operation_image) if diary.post_operation_image else '', 'is_fake_operation_time': bool(diary.is_fake_operation_time or diary.operation_time is None), 'operation_timestamp': get_timestamp(diary.operation_time), } return diary_data @bind('diary/more_detail') def get_diary_more_detail(diary_id): topics = Diary.get_topic_query(diary_id) last_topic = topics.select_related('video').order_by('-operation_date', '-id').first() diary_aggr = { 'topic_num': topics.count(), 'last_topic_id': last_topic.id if last_topic else 0, 'last_topic_content': escape(last_topic.answer if last_topic else ''), } return diary_aggr @bind('diary/author_info') def get_diary_author_info(user_id): new_diary_convert_user = UserConvertService.get_user_info_by_user_id(user_id) return new_diary_convert_user @bind_context('diary/diary_info', deprecated=True) # @bind_context("topic/diary/get", deprecated=True) def get_diary_info(ctx, id, admin=False, without_activity_topics=False, simple_data=False, topic_sort=TOPIC_ORDER_IN_DIARY.LATEST, add_view=True): """get diary infomation. .. changelog. add param without_activity_topics at 5.5 add param simple_data at 5.6 return diary simple data TODO: delete without_activity_topics when mweb support activities NOTE: deprecated @7.0.0 """ user = get_user_from_context(ctx) try: diary = Diary.objects.get(pk=id, is_online=True) except Diary.DoesNotExist: return None if simple_data: return { 'result': diary.simple_data() } diary_user = UserService.get_user_by_user_id(diary.user_id) diary_data = diary.get_diary_info(user=user) diary_data.update({ 'topics': [], 'pre_operation_images': diary.pre_operation_images_info, 'cover_pre': get_full_path(diary.pre_operation_image) if diary.pre_operation_image else '', 'cover_post': get_full_path(diary.post_operation_image) if diary.post_operation_image else '', 'user_id': diary.user_id, 'user_portrait': diary_user.portrait, 'user_nickname': diary_user.nickname, 'city': diary_user.city_name, 'wiki_list': diary.wiki_info, 'view_num': diary.view_num, 'tags': diary.tags_new_era, 'membership_level': diary_user.membership_level, 'zone_list': [], }) if admin: topic_filter = Q() else: topic_filter = Q(flag=PROBLEM_FLAG_CHOICES.NORMAL, is_online=True) if without_activity_topics: topic_filter &= Q(activity__isnull=True) if topic_sort == TOPIC_ORDER_IN_DIARY.OLDEST: order_by = 'created_time' else: order_by = '-created_time' topics = diary.topics.filter(topic_filter).order_by(order_by) topics_id = list(diary.topics.filter(topic_filter).order_by('created_time').values_list('id', flat=True)) topic_ids = [] topics_data = topic_list_manager.get_list_data_by_topic_objs(topics, viewer_user_id=user.id) for t_data in topics_data: problem = t_data['problem'] if diary.operation_time: interval = datetime.datetime.fromtimestamp(int(problem['created_time'])) - diary.operation_time if interval.days >= 0: interval = interval.days + 1 else: interval = '' else: interval = '' user = t_data['user'] topic_data = { 'interval': interval, 'user_nickname': user['user_name'], 'user_portrait': user['portrait'], 'membership_level': user['membership_level'], } topic_id = problem['id'] try: # topic.id不在topics_id中或者topics_id为空的情况 topic_data['diary_num'] = topics_id.index(topic_id) + 1 except: topic_data['diary_num'] = 1 topic_data.update(problem) topic_ids.append(topic_id) diary_data['topics'].append(topic_data) if add_view: topic_view_increase_num(topic_ids) # keep original logic diary_view_increase_num([id]) diary_view_increase_num([id]) return diary_data @bind('diary/get_num_and_cover_by_ids') def diary_num_and_cover(ids): """ :param ids: diary_id list :return: [ { 'id': 1, 'data': { 'diary_amount': 2, 'view_num': 3, 'vote_num': 4, 'cover': xxx } } ] """ if len(ids) == 0: return [] diaries = Diary.objects.filter(id__in=ids) result = [] for diary in diaries: diary_amount = diary.topic_num view_num = diary.view_num vote_num = diary.vote_num cover = diary.cover data = { 'diary_amount': diary_amount, 'view_num': view_num, 'vote_num': vote_num, 'cover': cover, } d = { 'id': diary.id, 'data': data, } result.append(d) return result @bind('diary/get_diary_info_by_ids') def get_diary_info_by_ids(ids, simple=False): """ :param ids: diary_id list simple: :return: [ { 'id': 1, 'data': diary_info 一个大字典 } ] """ if len(ids) == 0: return [] diaries = Diary.objects.filter(id__in=ids) result = [] for diary in diaries: d = diary.get_diary_info(simple=simple) data = { 'id': diary.id, 'data': d, } result.append(data) return result @bind_context('diary/get_index_banner_diary_by_ids') def get_index_banner_diary_by_ids(ctx, diary_ids): user = get_user_from_context(ctx) diary_infos = diary_list_manager.get_diary_list_by_diary_ids(diary_ids, user.id) _info = {} for diary_info in diary_infos: _info.update({str(diary_info['diary_id']): { 'vote_num': diary_info['vote_num'], 'reply_num': diary_info['reply_num'], 'view_num': diary_info['view_num'], 'user_portrait': diary_info['user_portrait'], 'user_id': diary_info['user_id'], 'user_nickname': diary_info['user_nickname'], 'is_voted': diary_info['is_voted'], }}) return _info @bind('diary/tags') def get_diary_tags(diary_id): tags = DiaryTag.objects.filter(diary_id=diary_id).values_list('tag_id', flat=True) return list(tags) @bind('diary/tags/v1') def get_diary_tags_v1(diary_ids): mp = defaultdict(list) for obj in DiaryTag.objects.filter(diary_id__in=diary_ids).iterator(): mp[obj.diary_id].append(obj.tag_id) rs = [] for k, v in mp.items(): rs.append({ 'id': k, 'tags': v }) return rs @bind("diary/mip/detail") def get_diary_info_for_mip(diary_id): """ mip 百度合作,日记本详情页信息 :param diary_id: :return: """ try: _ = BDDiary.objects.get(diary_id=diary_id, is_submit=True) # 防止接口使用非推荐的id获取数据,校验 diary_id 需在 bd_diary 表中。 diary = Diary.objects.get(pk=diary_id, is_online=True) except (BDDiary.DoesNotExist, Diary.DoesNotExist): return gen(CODES.DIARY_NOT_FOUND) operation_info = diary.operation_info() diary_user = UserService.get_user_by_user_id(diary.user_id) latest_topic = diary.latest_topic result = { "id": diary.id, "created_time": get_timestamp_or_none(diary.created_time), "update_time": get_timestamp_or_none(diary.last_topic_add_time), "operation_info": operation_info, "tags": diary.tags_new_era, "topic_num": diary.topic_num, "view_num": diary.view_num, "reply_num": diary.reply_num, "vote_num": diary.vote_num, 'pre_operation_images': diary.pre_operation_images_info, "first_topic_id": latest_topic and latest_topic.id or 0, "first_topic_content": latest_topic and escape(latest_topic.answer) or "", "cover": diary.cover, "user": { "user_id": diary.user_id, "nickname": diary_user.nickname, "portrait": diary_user.portrait, } } return result @bind('diary/simple_data') def get_diary_simple_data(diary_id, for_doctor=False): """ 获取diary的一些基础信息 """ query = Q(pk=diary_id) if not for_doctor: query &= Q(is_online=True) try: diary = Diary.objects.get(query) except Diary.DoesNotExist: return None if diary.is_fake_operation_time or diary.operation_time is None: is_fake_operation_time = True else: is_fake_operation_time = False tag_data = GoodsService.get_tag_data(diary.all_tags) diary_count = Problem.objects.filter(user_id=diary.user_id, is_online=True).count() info = {'operation_timestamp': get_timestamp(diary.operation_time)} tag_v3_infos = get_diary_tagv3_info_by_diary_ids(diary_ids=[diary.id]) return { 'title': diary.title, 'diary_id': diary.id, 'author_id': diary.user_id, 'service_id': diary.service_id, 'order_id': diary.order_id, 'view_num': diary.view_num, 'images': diary.cover, 'topic_num': diary.topic_num, 'tag_id': tag_data.get('tag_id', None), 'operation_info': info, 'is_fake_operation_time': is_fake_operation_time, 'tags': diary.tags_new_era, 'diary_total': diary_count, 'tag_v3': tag_v3_infos.get(diary.id, []) } @bind("diary/share") def diary_share(diary_id): try: diary = Diary.objects.get(pk=diary_id, is_online=True) except Diary.DoesNotExist: return False diary.share_num += 1 diary.save() return True @bind("diary/new_share_data") def get_diary_new_share_data_by_id(diary_id): try: diary = Diary.objects.get(pk=diary_id, is_online=True) except Diary.DoesNotExist: return gen(CODES.DIARY_NOT_FOUND) # 用户相关数据 user = UserService.get_user_by_user_id(diary.user_id) # 美购相关数据 service_id = diary.service_id service_info = (service_id and GoodsService.get_diary_show_info_by_service_ids([service_id]) or {}).get(service_id, {}) lastest_topic = diary.latest_topic diary_operation_time = diary.operation_time or diary.created_time lastest_topic_operate_time = lastest_topic and lastest_topic.operation_date or None _data = { "diary_id": diary.id, "content": lastest_topic and lastest_topic.content or "", "cover": diary.cover, "diary_operation_timestamp": int(diary_operation_time.strftime("%s")), "topic_operation_timestamp": lastest_topic_operate_time and int(lastest_topic_operate_time.strftime("%s")) or 0, "city_name": service_info.get("city_name", "") or user.city_name or "", "vote_num": diary.vote_num, "tags": diary.tags_new_era, "user_name": user.nickname, } return _data @bind("diary/get_diary_id_by_audit_status") def get_diary_id_by_audit_status(diary_id, near_status): """ 通过日记id,审核状态获取相近的日记本id if status == done: if au_cnt == 1: 待初审 elif au_cnt > 1: 待再审 elif status == 初审: 待初审 elif status == 再审: 待再审 :param diary_id: :param near_status: :return: """ _nt = namedtuple("DIARY_NEAR_STATUS", ['PREV', "AFTER"]) DIARY_NEAR_STATUS = _nt(PREV=0, AFTER=1) base_query = Q(is_online=True) diary_obj = Diary.objects.filter(pk=diary_id).first() if diary_obj: audit_status = diary_obj.audit_status content_level = diary_obj.content_level if audit_status == DIARY_AUDIT_STATUS.AUDITED: _diary_check_count = DiaryCheck.objects.filter(diary_id=diary_id).count() if _diary_check_count == 1: base_query &= Q(audit_status=DIARY_AUDIT_STATUS.UNAUDITED) elif _diary_check_count > 1: # 待再审 + 当前日记本审核的内容等级 base_query &= Q(audit_status=DIARY_AUDIT_STATUS.REAUDIT, content_level=content_level) elif audit_status == DIARY_AUDIT_STATUS.UNAUDITED: base_query &= Q(audit_status=DIARY_AUDIT_STATUS.UNAUDITED) elif audit_status == DIARY_AUDIT_STATUS.REAUDIT: base_query &= Q(audit_status=DIARY_AUDIT_STATUS.REAUDIT) if near_status == DIARY_NEAR_STATUS.PREV: # 前一条 filter_query = base_query & Q(pk__lt=diary_id) jump_diary_id = Diary.objects.filter(filter_query).order_by("-id").values_list("id", flat=True).first() elif near_status == DIARY_NEAR_STATUS.AFTER: # 后一条 filter_query = base_query & Q(pk__gt=diary_id) jump_diary_id = Diary.objects.filter(filter_query).values_list("id", flat=True).first() else: jump_diary_id = 0 else: jump_diary_id = 0 _diary_id = jump_diary_id and jump_diary_id or 0 return _diary_id @bind('diary/wiki/relation_diary_info') def get_wiki_diary(tag_id): result = [] data_murl = 'https://m.igengmei.com/diary_book/{}/' data_url = 'https://www.igengmei.com/diary_book/{}/' diary_ids = list(DiaryTag.objects.filter(tag_id=tag_id).values_list('diary_id', flat=True)) if not diary_ids: return [{}] #剔除不带有效日记贴的日记 diary_topics = Problem.objects.using('slave').filter(is_online=1, flag='n', topic_type__in=['0', '1', '2'], diary_id__in=diary_ids).only('diary_id') diary_ids = set(diary_topics.values_list("diary_id", flat=True)) #按照日记星级排序,选前20条,在通过smark_rank排序 diarys = Diary.objects.filter(id__in=diary_ids).order_by('-content_level')[:10] diary_recommend_scores = get_diaries_score(list(diarys.values_list('id', flat=True))) diary_ids = [] for item in diary_recommend_scores: diary_ids.append(item['diary_id']) diarys = [] for pk in diary_ids: try: diary_obj = Diary.objects.get(id=pk, is_online=1) diarys.append(diary_obj) except: pass if len(diarys) > 3: break for obj in diarys: obj_topics = Problem.objects.filter(is_online=1, flag='n', topic_type__in=['0', '1', '2'], diary_id=obj.id).order_by('-created_time') description = obj_topics.first().answer url = data_url.format(obj.id) mUrl = data_murl.format(obj.id) items = {'id': str(obj.id), 'title': obj.title, 'url': url, 'mUrl': mUrl, 'firstTime': str(obj.created_time.timestamp()), 'updateTime': str(obj.created_time.timestamp()), 'description': description, 'viewCount': obj.view_num} image_ls = obj.cover imageAfter = imageBefore = 'https://heras.igengmei.com/service_home/2020/03/17/a99b291047' for image in image_ls: if image.get('desc') == 'After': imageAfter = image.get('image') or imageAfter else: imageBefore = image.get('image') or imageBefore items.update(imageAfter=imageAfter, imageBefore=imageBefore) result.append(items) if result: return result return [{}] @bind('diary/get_service_id_by_diary') def talos_service_diary_id(id, type_): # 获取日记本关联的美购id if type_ == 2: ## 日记帖 problem = Problem.objects.filter(id=id).first() id = problem.diary_id if problem else '' diary = Diary.objects.filter(id=id).first() service_id = diary.service_id if diary else '' return {'service_id': service_id} @bind("diary/get_diary_rel_infos") def get_diary_rel_infos(diary_id): """ 获取日记本关联的信息 :param diary_id: :return: """ result = {} if not diary_id: return result routine = GroupRoutine() _map_list = [ ("v1_rel_tags_info", get_diary_tag_info_by_diary_ids), ("v3_rel_tags_info", get_diary_tagv3_info_by_diary_ids), ] for key, func in _map_list: routine.submit(key, func, [diary_id]) routine.go() for key, _ in _map_list: _data = routine.results.get(key, {}) result.update({ key: _data.get(diary_id, []) }) return result