# coding=utf8 from __future__ import unicode_literals, absolute_import, print_function import datetime import random import time from django.conf import settings from django.db.models import Q from django.db import transaction from gm_types.gaia import ( TAG_TYPE, POINTS_TYPE, DIARY_OPERATE, DIARY_OPERATION, TOPIC_TYPE, ) from gm_upload.utils.image_utils import Picture from talos.libs.diaryrank_utils import DiaryRankTool from talos.logger import info_logger from talos.manager.diary import ( set_diary_cover, set_diary_postoperation_cover, ) from talos.models.diary import ( Diary, DiaryTag, DiaryTagV3, PreOperationImage, DiaryExtra, ) from talos.models.topic import ( Problem, TopicImage, ProblemTag, ) from talos.services import ( TagService, DoctorService, HospitalService, OrderService, ) from talos.services.other import add_points from talos.tasks.topic import async_diary_change_tags_to_topic from utils.rpc import logging_exception def get_diary_title(tags): tags.sort(key=lambda i: i.id) def get_type_tags(tags, tag_type): return list(filter(lambda i: i.tag_type == tag_type, tags)) tag_list = get_type_tags(tags, TAG_TYPE.ITEM_WIKI) if not tag_list: tag_list = get_type_tags(tags, TAG_TYPE.BODY_PART_SUB_ITEM) if not tag_list: tag_list = get_type_tags(tags, TAG_TYPE.BODY_PART) if tag_list: return tag_list[0].name + '日记' return None def update_diary_extra_info(diary_ids: list): """更新日记本点统计信息。""" if not diary_ids: return diaries = Diary.objects.filter(Q(extra_info__isnull=False) & Q(id__in=diary_ids)) if not diaries: return for diary in diaries: diary.extra_info.vote_count = diary.vote_num diary.extra_info.topic_count = diary.topic_num diary.extra_info.total_pv = diary.view_num diary.extra_info.reply_count = diary.reply_num diary.extra_info.save() TAG_CITY_TYPE_LIST = [TAG_TYPE.COUNTRY, TAG_TYPE.PROVINCE, TAG_TYPE.CITY] # 城市类型标签 TAG_ITEM_TYPE_LIST = [TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM, TAG_TYPE.ITEM_WIKI] # 1、2、3级标签 def common_func_for_create_diary( user, title="", comment=[], order_id=None, tag_ids=[], operation_timestamp=None, doctor_id=None, doctor_name=None, hospital_id=None, hospital_name=None, service_id=None, price=0, rating=None, operation_effect_rating=0, doctor_attitude_rating=0, hospital_env_rating=0, pre_operation_images=[], cover=None, create_for_draft=False, new_version_create=False ): """ 创建日记本的公共方法 :param user: :param title:日记本的标题,7.7.20日记本可编辑 :param comment: :param order_id: :param tag_ids: :param operation_timestamp: :param doctor_id: :param doctor_name: :param hospital_id: :param hospital_name: :param service_id: :param price: :param rating: :param operation_effect_rating: :param doctor_attitude_rating: :param hospital_env_rating: :param pre_operation_images: :param cover: :param create_for_draft: 是否为新草稿创建的日记本 :param new_version_create: 是否是新版创建 :return: """ diary = Diary() diary.user_id = user.id diary.price = price diary, tag_ids, update_order_info = _update_diary_operation_info( diary, tag_ids=tag_ids, order_id=order_id, doctor_id=doctor_id, doctor_name=doctor_name, hospital_id=hospital_id, hospital_name=hospital_name ) if operation_timestamp: operation_time = datetime.date.fromtimestamp( float(operation_timestamp) ) diary.operation_time = operation_time diary.is_fake_operation_time = False else: diary.operation_time = datetime.datetime.now() diary.is_fake_operation_time = True if comment: diary.comment = comment if comment or diary.rating: add_points(reason_type=POINTS_TYPE.REPLY_DIARY) diary.save() has_geo_tag = False tags = TagService.get_tags_by_tag_ids(tag_ids) # 新版创建日记本,最多绑定 10个 标签 if new_version_create: exclude_city_tags = list(filter( lambda t: t.tag_type not in TAG_CITY_TYPE_LIST, tags))[:settings.DIARY_TAGS_MAX_COUNT] tags = exclude_city_tags + list(filter(lambda x: x.tag_type == TAG_TYPE.CITY, tags)) for t in tags: try: if t.tag_type in (TAG_TYPE.COUNTRY, TAG_TYPE.PROVINCE): continue if t.tag_type == TAG_TYPE.CITY: has_geo_tag = True DiaryTag.create_bind(diary_id=diary.id, tag_id=t.id) except: logging_exception() info_logger.info('tag_id: {} not found'.format(t.id)) if not has_geo_tag: user_city_tagid = user.city_tag_id if user_city_tagid: try: DiaryTag.create_bind(diary_id=diary.id, tag_id=user_city_tagid) except: logging_exception() # 关于日记本标题的改动 if not title: title = get_diary_title(tags) if tags else None if title: diary.title = title # 评分 兼容老接口 try: rating = int(rating) except: rating = None if rating and 1 <= rating <= 5: diary.rating = rating record_rating = True for _rating in (operation_effect_rating, doctor_attitude_rating, hospital_env_rating): if _rating < 1 or _rating > 5: record_rating = False break if record_rating: # 如果有非法的星级评价 此次作废 # 如果comment和星级评价都传过来 以星级评价为准 diary.operation_effect_rating = operation_effect_rating diary.doctor_attitude_rating = doctor_attitude_rating diary.hospital_env_rating = hospital_env_rating if cover: diary.pre_operation_image = cover # 添加术前图 改成批量 pre_operation_images_list = [] if pre_operation_images: taken_time = datetime.datetime.now() for image in pre_operation_images: if not image.get('image'): continue is_cover = False if image['image'] != cover else True pre_operation_images_list.append(PreOperationImage( diary=diary, image_url=image['image'], taken_time=taken_time, is_cover=is_cover, cover_image_url=image.get('modified_image_url'))) PreOperationImage.objects.bulk_create(pre_operation_images_list) # 记录操作 UPDATE_INFO diary.set_diary_operate(DIARY_OPERATE.UPDATE_INFO) diary.is_online = False if create_for_draft else True diary.save() # 更新日记本封面图 diary.cache_cover() return diary def update_diary_operation_info_func( user, diary, title='', tag_ids=[], doctor_id=None, doctor_name=None, order_id=None, hospital_id=None, hospital_name=None, operation_timestamp=None, pre_cover='', post_cover='', only_update_order_info=False ): """ 更新日记本信息操作。 客户端会传递所有字段,所以这里做一次比较 :param user: :param diary: :param title: 日记本标题 :param tag_ids: :param doctor_id: :param doctor_name: :param order_id: :param hospital_id: :param hospital_name: :param operation_timestamp: :param pre_cover: 术前封面图,已经是处理过的图片地址 :param post_cover:术后封面图,已经是处理过的图片地址 :param only_update_order_info:仅更新绑定订单的数据 :return: """ diary_title_update_status = False # 日记本标题更新状态 diary_pre_cover_update_status = False # 日记本术前封面图更新状态 diary_post_cover_update_status = False # 日记本术后封面图更新状态 diary_update_operation_time = False # 日记本手术时间更新状态 diary, tag_ids, update_order_info = _update_diary_operation_info( diary, tag_ids=tag_ids, order_id=order_id, doctor_id=doctor_id, doctor_name=doctor_name, hospital_id=hospital_id, hospital_name=hospital_name ) # 更新日记本标签 diary, diary_diff_add_tag_ids, diary_diff_remove_tag_ids = _update_diary_tags(user, diary, tag_ids, update_order_info) topic_ids = list(Problem.objects.filter(diary_id=diary.id, is_online=True).values_list('id', flat=True)) # 日记本标签变动同步到帖子上 async_diary_change_tags_to_topic.delay( topic_ids=topic_ids, add_tags=diary_diff_add_tag_ids, del_tags=diary_diff_remove_tag_ids ) if not only_update_order_info: _old_diary_pre_cover = diary.pre_operation_image _old_diary_post_cover = diary.post_operation_image _old_diary_operation_time = diary.operation_time # 修改日记本治疗时间 if operation_timestamp: _date = datetime.date.fromtimestamp(float(operation_timestamp)) if not _old_diary_operation_time: # 日记本无治疗时间 diary_update_operation_time = True elif _old_diary_operation_time and _old_diary_operation_time.date() != _date: # 更新治疗时间 diary_update_operation_time = True if diary_update_operation_time: diary.operation_time = _date diary.is_fake_operation_time = False else: diary.operation_time = datetime.datetime.now() diary.is_fake_operation_time = True diary_update_operation_time = True # 修改日记本标题 if not title: tags = TagService.get_tags_by_tag_ids(tag_ids) title = get_diary_title(tags) if tags else None if title: diary.title = title diary_title_update_status = True else: diary_old_title = diary.title if title != diary_old_title: diary.title = title diary_title_update_status = True # 修改术前、术后封面图 if pre_cover and pre_cover != Picture.get_short_path(_old_diary_pre_cover): PreOperationImage.objects.filter(diary_id=diary.id, image_url=pre_cover).update(cover_image_url=pre_cover) set_diary_cover(pre_cover, diary) diary.pre_operation_image = pre_cover diary_pre_cover_update_status = True if post_cover and post_cover != Picture.get_short_path(_old_diary_post_cover): TopicImage.objects.filter(topic_id__in=topic_ids, image_url=post_cover).update(cover_image_url=post_cover) set_diary_postoperation_cover(post_cover, diary) diary.post_operation_image = post_cover diary_post_cover_update_status = True if any([ diary_pre_cover_update_status, diary_post_cover_update_status, diary_title_update_status, diary_diff_add_tag_ids, diary_update_operation_time, ]): d = DiaryRankTool(diary.id) d.add_heat(DIARY_OPERATION.MODIFY_DIARY, datetime.datetime.now()) if any([ diary_pre_cover_update_status, diary_post_cover_update_status, diary_title_update_status, diary_diff_add_tag_ids, ]): # 记录操作 UPDATE_INFO diary.set_diary_operate(DIARY_OPERATE.UPDATE_INFO) # 更换封面图、或是更新日记本信息,都会更新日记本的更改时间 diary.last_modified = datetime.datetime.now() diary.save() # 更新日记本封面图 diary.cache_cover() return diary def _update_diary_tags(user, diary, tag_ids, update_order_info=False): """ 更新日记本标签 处理标签 由于存储的是 项目标签 + 运营标签 还需要对数量做限制!!! 当前逻辑是仅处理 1、2、3级 标签的更新 or 删除。其他不管 :param user: :param diary: :param tag_ids: :return: """ # 日记本已有的标签 diary_old_tag_ids = list(DiaryTag.objects.filter(diary=diary).values_list("tag_id", flat=True)) diary_old_tag_v3_ids = list(DiaryTagV3.objects.filter(diary_id=diary.id).values_list("tag_v3_id", flat=True)) # 如果日记本有视频标签,则移除视频标签不作为比较项 if settings.VIDEO_TAG_ID in diary_old_tag_ids: diary_old_tag_ids.remove(settings.VIDEO_TAG_ID) if settings.VIDEO_TAG_V3_ID in diary_old_tag_v3_ids: diary_old_tag_v3_ids.remove(settings.VIDEO_TAG_V3_ID) diary_old_tags_obj_list = TagService.get_tags_by_tag_ids(diary_old_tag_ids) # 外部传入的标签 _new_tag_obj_list = TagService.get_tags_by_tag_ids(list(set(tag_ids))) new_tag_obj_list = sorted(filter(lambda t: t.tag_type not in TAG_CITY_TYPE_LIST, _new_tag_obj_list), key=lambda tag_item: tag_ids.index(tag_item.id))[:settings.DIARY_TAGS_MAX_COUNT] need_update_tag_ids = [tag.id for tag in new_tag_obj_list] # 最终需要更新的标签 # 关于城市标签的处理 # 在日记本已绑定的标签中 筛选 城市标签 diary_exist_city_tag_list = list(filter(lambda x: x.tag_type == TAG_TYPE.CITY, diary_old_tags_obj_list)) # 查询外部传入的标签中,城市类型的标签 new_city_tag_obj_list = list(filter(lambda x: x.tag_type == TAG_TYPE.CITY, _new_tag_obj_list)) # 可以更新地域标签的状态 need_update_diary_city_tag = update_order_info or (not update_order_info and not diary_exist_city_tag_list) if need_update_diary_city_tag: # 城市标签补齐策略 _city_tag_obj_list = new_city_tag_obj_list or diary_exist_city_tag_list if _city_tag_obj_list: tag = _city_tag_obj_list[0] need_update_tag_ids.append(tag.id) else: if user.city_tag_id: need_update_tag_ids.append(user.city_tag_id) #标签的替换 获取日记本的1、2、3级标签, 若更新订单信息,需要把地域标签替换 if need_update_diary_city_tag: TAG_ITEM_TYPE_LIST.extend([TAG_TYPE.CITY, ]) diary_old_item_tag_ids = [tag.id for tag in diary_old_tags_obj_list if tag.tag_type in TAG_ITEM_TYPE_LIST] # 比较1、2、3级标签进行处理 or add city tag diary.add_tags(need_update_tag_ids) diary.del_tags(list(set(diary_old_item_tag_ids) - set(need_update_tag_ids))) # 计算日记本tag修改后新增和删除的tag,注意这些数据需要同步到帖子上! diary_diff_add_tag_ids = set(need_update_tag_ids) - set(diary_old_item_tag_ids) diary_diff_remove_tag_ids = set(diary_old_item_tag_ids) - set(need_update_tag_ids) return diary, list(diary_diff_add_tag_ids), list(diary_diff_remove_tag_ids) def _update_diary_operation_info( diary, order_id=None, tag_ids=[], doctor_id=None, doctor_name=None, hospital_id=None, hospital_name=None ): """ 更新日记本的手术信息 :param diary: :param order_id: :param tag_ids: :param doctor_id: :param doctor_name: :param hospital_id: :param hospital_name: :return: """ # 如果有订单,按照订单的信息去更新,没有按照用户选择的去更新 update_order_info = False if order_id: order_info = OrderService.get_order_data_for_update_diary_operation_info(order_id=order_id) else: order_info = None if order_info: diary.order_id = order_id diary.doctor_id = order_info['doctor_id'] diary.raw_doctor = order_info['doctor_name'] diary.hospital_id = order_info['hospital_id'] diary.raw_hospital = order_info['hospital_name'] diary.service_id = order_info['service_id'] # 评分 diary.rating = order_info.get("rating", 0) diary.operation_effect_rating = order_info.get("operation_effect", 0) diary.doctor_attitude_rating = order_info.get("doctor_attitude", 0) diary.hospital_env_rating = order_info.get("hospital_env", 0) tag_ids.append(order_info["city_tag_id"]) update_order_info = True else: if doctor_id: diary.doctor_id = doctor_id else: diary.doctor_id = None if doctor_name: diary.raw_doctor = doctor_name if hospital_id: diary.hospital_id = hospital_id else: diary.hospital_id = None if hospital_name: diary.raw_hospital = hospital_name return diary, tag_ids, update_order_info def create_diary(diary_list, services_tags): """ 创建日记本 关联美购 :param diary_list: :param services_tags: :return: """ def get_random_time(base_time): # 生成随机时间 start = datetime.datetime.fromtimestamp( time.mktime(time.strptime(base_time, '%Y-%m-%d %H:%M:%S')) ) delta = datetime.datetime.now() - start inc = random.randrange(int(delta.total_seconds())) return start + datetime.timedelta(seconds=inc) topic_obj_list = [] with transaction.atomic(): for diary_dict in diary_list: service_id = diary_dict.get('service_id') start_time = diary_dict.get('start_time') operation_time = get_random_time(start_time) if start_time else datetime.datetime.now() # 美购关联的标签 tag = services_tags.get(str(service_id)) title = (tag + '日记') if tag else '' diary_info = { "user_id": diary_dict.get('user_id'), "title": title, "created_time": operation_time, "last_modified": operation_time, "last_topic_add_time": operation_time, "operation_time": operation_time, "is_fake_operation_time": True, "doctor_id": diary_dict.get('doctor_id'), "raw_doctor": diary_dict.get('doctor_name'), "raw_hospital": diary_dict.get('hospital_name'), "hospital_id": diary_dict.get('hospital_id'), "is_import": True, "service_id": service_id } try: diary = Diary.objects.create(**diary_info) DiaryExtra.objects.create(diary=diary) topic_dict = { "answer": diary_dict.get('content'), "diary_id": diary.id, "service_id": service_id, "link_doctor_id": diary_dict.get('doctor_id'), "raw_doctor": diary_dict.get('doctor_name'), "link_hospital_id": diary_dict.get('hospital_id'), "raw_hospital": diary_dict.get('hospital_name'), "user_id": diary_dict.get('user_id'), "topic_type": TOPIC_TYPE.SHARE, "operation_date": datetime.datetime.now(), } topic_obj_list.append(Problem(**topic_dict)) except: raise Exception("美购:{}创建日记本失败".format(service_id)) Problem.objects.bulk_create(topic_obj_list) return