activity.py 12.4 KB
# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function

import datetime

from django.db import transaction
from django.db.models import Q
from django.utils import timezone
from django.conf import settings

from gm_rpcd.internals.proxy_object import unproxy
from gm_types.gaia import (
    ACTIVITY_PARTICIPATE_REASON, TOPIC_TYPE, POINTS_OPERATION,
    POINTS_TYPE, FILTER_WORD_TYPE, ACTIVITY_STATUS
)
from gm_types.push import AUTOMATED_PUSH, PUSH_INFO_TYPE
from gm_rpcd.all import bind
from helios.rpc import create_default_invoker

from talos.libs.datetime_utils import get_timestamp_or_none
from utils.rpc import get_current_user, rpc_client
from utils.protocol import gm_protocol

from talos.libs.datetime_utils import get_timestamp_epoch
from talos.rpc import bind_context
from talos.decorators import cache_page, list_interface
from talos.rpc import CODES, gen, logging_exception
from distutils.version import LooseVersion

from utils.push import push_task_to_user_multi
from talos.models.topic import Problem
from talos.models.topic import Activity, ActivityAlert
from talos.models.topic import TopicImage
from talos.models.topic import VotePK
from talos.services import get_user_from_context, UserService

from talos.tools.filterword_tool import filterword_by_custom


@bind_context('topic/activity/list')
@list_interface(offset_name='start_num', limit_name='count', element_model=Activity)
def activity_list(ctx, start_num=0, count=10, doctor_id=None, status=None, is_online=False):
    """get activity list.
    .. versionadded:: 5.5
    update v 7.6.25 add param is_online 针对医生详情页,获取医生在线的且在当前时间之内的免费活动
    """

    user = get_user_from_context(ctx)
    order = '-start_time'

    if doctor_id:
        q = Q(doctor_id=doctor_id)
        if is_online:
            q = Q(doctor_id=doctor_id, is_online=True, start_time__lte=timezone.now())
    else:
        q = Q(is_online=True)
        if status == ACTIVITY_STATUS.ON:
            q &= Q(start_time__lte=timezone.now(), end_time__gte=timezone.now())
            order = 'end_time'
        elif status == ACTIVITY_STATUS.CLOSED:
            q &= Q(end_time__lt=timezone.now())
            order = '-end_time'
        elif status == ACTIVITY_STATUS.NOT_START:
            q &= Q(start_time__gt=timezone.now())
            order = 'start_time'

    objs = Activity.objects.filter(q).order_by(order)[start_num:start_num + count]

    alerts = []
    if user:
        activity_ids = [item.id for item in objs]
        alerts = list(ActivityAlert.objects.filter(activity_id__in=activity_ids, person_id=user.person_id).values_list(
            "activity_id", flat=True))

    res = []
    for obj in objs:
        data = obj.to_dict()
        data["is_alert"] = True if obj.id in alerts else False
        res.append(data)

    return res


@bind_context('topic/activity/alert')
def activity_alert(ctx, activity_id):
    """设置活动提醒"""

    user = get_user_from_context(ctx)

    if not user:
        gen(CODES.MESSAGE_UNKNOWN_USER)

    try:
        activity = Activity.objects.get(pk=activity_id, is_online=True)
    except Activity.DoesNotExist:
        return gen(CODES.ACTIVITY_NOT_EXIST)

    obj, created = ActivityAlert.objects.get_or_create(activity=activity, person_id=user.person_id)

    if created:
        from six.moves import urllib_parse as urlparse
        url_new = urlparse.urljoin(
            gm_protocol.api_host,
            'hybrid/activity/{}'.format(obj.activity_id)
        )
        push_url = gm_protocol.get_webview(url_new)
        extra = {
            'type': PUSH_INFO_TYPE.GM_PROTOCOL,
            'msgType': 4,
            'pushUrl': push_url,
            'push_url': push_url,
        }
        alert = u'您报名的{}活动已经开始了,快打开更美免费招募报名呀~'.format(activity.title)
        push_time = activity.start_time-datetime.timedelta(minutes=10)
        push_task_to_user_multi(user_ids=[user.id], alert=alert, push_type=AUTOMATED_PUSH.ACTIVITY,
                                eta=push_time.strftime('%Y-%m-%d %H:%M:%S'), extra=extra)


@bind_context('topic/activity/detail')
def activity_detail(ctx, id):
    """get activity detail.
    .. versionadded:: 5.5
    """
    try:
        activity = Activity.objects.get(pk=id, is_online=True)
    except Activity.DoesNotExist:
        return gen(CODES.ACTIVITY_NOT_EXIST)

    user = get_user_from_context(ctx)

    is_participation = False
    if user:
        activity_topics = Problem.objects.filter(user_id=user.id, activity_id=activity.id).count()
        if activity_topics >= activity.participate_limit:
            is_participation = True

    result = activity.to_dict(simple=False)
    result['is_participation'] = is_participation

    return result


_reason_codes_map = {
    ACTIVITY_PARTICIPATE_REASON.CLOSED: CODES.ACTIVITY_CLOSED,
    ACTIVITY_PARTICIPATE_REASON.NOT_STARTED: CODES.ACTIVITY_NOT_STARTED,
    ACTIVITY_PARTICIPATE_REASON.PARTICIPATE_LIMIT: CODES.ACTIVITY_PARTICIPATE_LIMIT,
    ACTIVITY_PARTICIPATE_REASON.NOT_ENOUGH_POINTS: CODES.DO_NOT_HAS_ENOUGH_POINTS_TO_JOIN_ACTIVITY,
}


@bind('topic/activity/participants')
@list_interface(offset_name='start_num', limit_name="count")
def get_recent_participants(activity_id, start_num=0, count=10):
    """获取最近参加该活动的用户.

    :param id:
    :return:

    .. versionadded:: 5.5
    """
    try:
        all_problems = Problem.objects.filter(activity_id=activity_id).order_by('-id')

        user_infos = []
        for problem in all_problems[start_num:start_num + count]:
            user_infos.append(problem.user.to_dict())
        return user_infos

    except:
        logging_exception()
        return []


@bind_context('topic/activity/topics')
@list_interface(offset_name='start_num', limit_name='count')
def get_problem_list(ctx, activity_id=None, start_num=0, count=10):
    """根据活动的ID,获取帖子列表.

    :param activity_id:
    :param start_num:
    :param count:
    :return:

    .. versionadded:: 5.5
    """
    try:
        all_problems = Problem.objects.filter(
            activity_id=activity_id, is_online=True,
        )
        all_problems = all_problems.order_by('-created_time')
        all_problems = all_problems[start_num:start_num + count]
        user = get_user_from_context(ctx)

        problem_infos = []
        for problem in all_problems:
            info = problem.get_topic_info(user)
            problem_infos.append(info)

        return problem_infos
    except:
        logging_exception()

    return []


@bind_context('topic/activity/create_topic')
def create_activity_topic(ctx, activity_id, content, images=[], device_id=None, version='0.0.0',
            apply_phone=None):
    """create activity topic.

    .. versionadded:: 5.5
    """

    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    try:
        activity = Activity.objects.get(pk=activity_id)
    except Activity.DoesNotExist:
        return gen(CODES.ACTIVITY_NOT_EXIST)

    if not images and activity.must_image and LooseVersion(version) >= LooseVersion('7.7.65'):
        return gen(CODES.ACTIVITY_IMAGES_NONE)

    # 活动报名内容敏感词过滤,以前的代码进行了数字的过滤,本次沿用
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_CONTENT, content=content)
    if not content.strip():
        return gen(CODES.TOPIC_CONTENT_CAN_NOT_BE_EMPTY)

    # session_key = unproxy(ctx)._Context__request.session_id
    # check can participate this activity
    # 去掉免费活动参加门槛
    # user_points = create_default_invoker(debug=settings.DEBUG).with_config(
    #     dump_curl=True, session_key=session_key
    # )['api/points/user_point']().unwrap()

    ok, reason = activity.can_participate(user_id=user.id)
    if not ok:
        code = _reason_codes_map[reason]
        return gen(code)

    # enter safe zone
    with transaction.atomic():
        p = Problem()
        p.answer = content
        p.topic_type = TOPIC_TYPE.ACTIVITY
        p.activity = activity
        p.user_id = user.id
        p.apply_phone = apply_phone

        if device_id:
            p.device_id = device_id

        p.save()

        p.activity.participants += 1
        p.activity.save()

        images = images or []
        for image_url in images:
            if image_url:
                t = TopicImage(topic=p, image_url=image_url)
                t.save()

        # rpc_client['api/points/change'](
        #     user_id=user.id,
        #     number=activity.points,
        #     operation=POINTS_OPERATION.REMOVE,
        #     reason_type=POINTS_TYPE.JOIN_ACTIVITY
        # ).unwrap()

    return {'id': p.id}


@bind('topic/activity/vote_pk')
def vote_pk(topic_id, doctor_id=None, doctors_id=None, user_id=None, users_id=None):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    try:
        topic = Problem.objects.get(pk=topic_id)
    except Problem.DoesNotExist:
        return gen(CODES.TOPIC_NOT_FOUND)

    today = datetime.date.today()
    vote_info = VotePK.objects.filter(topic=topic)

    result = {
        'message': ''
    }
    if vote_info.filter(topic=topic, person_id=user.person_id, vote_date=today):
        # 当天已经投过
        # 只使用一次 不放到CODES中去
        result['message'] = 'voted'
    else:
        # 插入投票数据
        if doctor_id and not user_id:
            doctors = rpc_client['doctor/user/get_doctors'](doctor_ids=[doctor_id]).unwrap()
            if not doctors['doctors']:
                return gen(CODES.DOCTOR_NOT_FOUND)

            do_vote = VotePK(topic=topic, person_id=user.person_id, vote_date=today, vote_doctor_id=doctor_id)
            do_vote.save()
            result['vote_info'] = VotePK.vote_info(topic=topic, doctors_id=doctors_id, user=user)
        elif user_id and not doctor_id:

            vote_user = UserService.get_user_by_user_id(user_id)
            if not vote_user:
                return gen(CODES.USER_NOT_FOUND)
            do_vote = VotePK(topic=topic, person_id=user.person_id, vote_date=today, vote_user_id=vote_user.id)
            do_vote.save()
            result['vote_info'] = VotePK.vote_info(topic=topic, users_id=users_id, user=user)

    return result


@bind_context("topic/activity/list_by_ids")
def get_activity_list_by_ids(ctx, activity_ids):
    """
    通过免费活动id列表获取免费活动数据
    :param ctx:
    :param activity_ids:  免费活动列表
    :return:
    """
    assert len(activity_ids) <= settings.COUNT_LIMIT, 'too many activity_ids'

    result = {
        "activity_list": [],
    }

    if not activity_ids:
        return result

    activity_objs = Activity.objects.filter(
        is_online=True,
        pk__in=activity_ids
    )

    user = get_user_from_context(ctx)
    if user:
        _effective_ids = (activity_objs.values_list("id", flat=True))
        alerts = list(ActivityAlert.objects.filter(
            activity_id__in=_effective_ids,
            person_id=user.person_id
        ).values_list("activity_id", flat=True))
    else:
        alerts = []

    activity_list = []
    for obj in activity_objs:
        _id = obj.id
        data = obj.to_dict()
        data["is_alert"] = bool(_id in alerts)
        data["doctor_id"] = obj.doctor_id

        # 因时区问题,时间戳替换
        data.update({
            "start_time": get_timestamp_epoch(obj.start_time),
            "end_time": get_timestamp_epoch(obj.end_time)
        })

        activity_list.append(data)
    result["activity_list"] = activity_list

    return result


@bind('topic/activity/users')
@list_interface(offset_name='start_num', limit_name='count')
def get_problem_users(activity_id, start_num=0, count=5):
    """
    根据活动的ID,获取报名用户.
    :param activity_id:
    :param start_num:
    :param count:
    :return:
    """
    result = []
    problems = Problem.objects.filter(
        activity_id=activity_id, is_online=True
    ).values('user_id', 'created_time').order_by('-id')[start_num: start_num+count]
    if not problems:
        return {'result': result}

    user_ids = [problem['user_id'] for problem in problems]
    user_dic = UserService.get_users_by_user_ids(user_ids)

    for problem in problems:
        create_time = get_timestamp_or_none(problem['created_time'])
        nickname = user_dic[problem['user_id']].nickname
        result.append({'user_id': problem['user_id'], 'nickname': nickname, 'create_time': create_time})

    # result = sorted(result, key=lambda user: -(user.get('create_time')))
    return {'result': result}