123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import logging
- from django.conf import settings
- from typing import TYPE_CHECKING
- from apps.web.ad.models import Advertisement, Advertiser
- from apps.web.core.mathematics import get_haversine_by_km
- if TYPE_CHECKING:
- from apps.web.user.models import MyUser
- logger = logging.getLogger(__name__)
- AD_SET_KEY = 'ads'
- INDEX_AD_RECORD_TIMESTAMP = 'idx:ad-record:timestamp'
- #: (logicalCode) -> ZSET[adIds]
- def device_ad_match_set_key(logicalCode):
- # type:(str)->str
- return 'device:%s:ad-matched' % (logicalCode,)
- #: (openId, adId) -> adRecordKey
- ad_record_key = lambda openId, adId: 'user:%s:adId:%s' % (openId, adId)
- ad_key = lambda adId: 'ad:adId:%s' % (adId,)
- user_ad_converted_set_key = lambda openId: 'user:%s:ad-converted' % (openId,)
- user_ad_view_key = lambda openId: 'user:%s:ad-viewed' % (openId,)
- user_ad_match_key = lambda openId: 'user:%s:ad-matched' % (openId,)
- #: 广告关联集合,记录其与代理商,经销商,组,设备的关系
- AD_ASSOC_AGENT_UNION = 'ad-assoc:agent'
- AD_ASSOC_DEALER_UNION = 'ad-assoc:dealer'
- AD_ASSOC_GROUP_UNION = 'ad-assoc:group'
- AD_ASSOC_DEVICE_UNION = 'ad-assoc:device'
- AD_ASSOC = [AD_ASSOC_AGENT_UNION, AD_ASSOC_DEALER_UNION, AD_ASSOC_GROUP_UNION, AD_ASSOC_DEVICE_UNION]
- def consume_advertiser_quota(advertiserId, consumed_quota=1):
- # type: (str, int) -> None
- if not advertiserId:
- logger.info('advertiserId is null, skipped consuming quota')
- return
- advertiser = Advertiser.objects(id=advertiserId).get()
- updated = advertiser.update(dec__quota=consumed_quota)
- logger.info('{0!r} updated quota({1!r}), updated={2}'.format(advertiser, consumed_quota, updated))
- if advertiser.reload().quota == 0:
- status_updated = Advertisement.objects(advertiserId=advertiserId).update(status=False)
- logger.info('{0!r}\'s quota reached 0. set all his ads\' status to be False, updated={1}'
- .format(advertiser, status_updated))
- #: math
- def cpc_to_ecpm(views, clicks, cpc):
- return 1000. * cpc * clicks / views
- def cpa_to_ecpm(views, actions, cpa):
- return 1000. * cpa * actions / views
- def calc_user_device_distance(device, user):
- # type: (dict, MyUser) -> int
- user_location = user.locations.pop().coordinates
- device_location = (device['lng'], device['lat'])
- distance = get_haversine_by_km(user_location, device_location)
- return distance
- def user_near_device(device, user):
- # type: (dict, MyUser) -> bool
- """
- 检测是否用户靠近设备,目前只与广告相关
- :param device:
- :param user:
- :return:
- """
- if not settings.AD_LOCATION_LIMIT:
- return True
- if not len(user.locations):
- logger.debug('no user({0!r}) location available'.format(user))
- return False
- else:
- distance = calc_user_device_distance(device, user)
- if distance > settings.MAX_DISTANCE_TO_SHOW_AD:
- logger.debug('user({0!r}) is too far from the device(logicalCode={1})'.format(user, device['logicalCode']))
- return False
- else:
- return True
- def get_available_ads_by_user(device, user):
- # type: (dict, MyUser) -> list
- """
- 在设备侧准备好的广告用于匹配用户的特征
- :param device:
- :param user:
- :return:
- """
- logger.debug('getting available ads by user, device=(logicalCode=%s), user=%s'
- % (device['logicalCode'], repr(user)))
- if not all([device, user]):
- return []
- else:
- return []
- def user_has_available_ads(device, user):
- # type: (dict, MyUser) -> bool
- """
- 支付前广告
- 为了不影响业务主流程,这里catch所有的异常
- :param device:
- :param user:
- :return:
- """
- return False
|