1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- """
- web.ad.models
- ~~~~~~~~~ad
- """
- import datetime
- import logging
- import sys
- from collections import OrderedDict
- import simplejson as json
- import user_agents
- from bson.objectid import ObjectId
- from django.contrib.auth.hashers import make_password
- from django.utils.module_loading import import_string
- from mongoengine import StringField, DateTimeField, BooleanField, ListField, IntField, FloatField, DictField, \
- EmbeddedDocument, DynamicDocument
- from typing import Union, List, AnyStr, Dict, Any, Optional, TYPE_CHECKING
- from apilib.utils_datetime import today_format_str, yesterday_format_str
- from apps import serviceCache
- from apps.thirdparties.aliyun import AlipayYunMaV3
- from apps.web.ad import AdUser
- from apps.web.common.models import UserSearchable, District
- from apps.web.constant import Const, AdType, AdSpace
- from apps.web.core.db import CustomizedSequenceField
- from apps.web.core.db import Searchable
- from apps.web.device.models import Device
- from apps.web.utils import CustomizedValidationError, detect_app_from_ua, is_user
- logger = logging.getLogger(__name__)
- if TYPE_CHECKING:
- from apps.web.dealer.models import Dealer
- from apps.web.user.models import MyUser
- from apps.web.device.models import DeviceDict
- from library.mongo_django_auth_backport.auth import User
- def nullable(obj, nil=''):
- class _(object):
- def __getattr__(self, item): return nil
- if obj is None:
- return _()
- else:
- return obj
- class Advertiser(UserSearchable):
- """
- 广告主
- """
- balance = FloatField(verbose_name="余额", default=0)
- #: 额度由厂商配置
- quota = IntField(verbose_name="残留额度", default=0)
- #: 该属性由厂商配置,限定广告商可能投放的范围,另一些支撑场景为代理商
- devCondition = ListField(verbose_name="选取广告设备的过滤条件",
- default=[{"selectAgents": [], "selectDealers": [], "selectGroups": []}])
- companyName = StringField(verbose_name="公司名称", default="")
- managerId = StringField(verbose_name="所属厂商ID", default="")
- meta = {
- 'collection': 'advertisers',
- 'db_alias': 'default',
- 'indexes': [
- {
- 'fields': ['username'],
- 'unique': True
- }
- ],
- }
- search_fields = ('username', 'nickname', 'remarks')
- def to_dict(self):
- rv = super(Advertiser, self).to_dict()
- rv.update({
- 'id': str(self.id),
- 'balance': self.balance,
- 'quota': self.quota,
- 'devCondition': self.devCondition,
- 'companyName': self.companyName
- })
- return rv
- @property
- def preAllocatedDeviceConditions(self):
- return \
- [
- {
- 'agentIdList': [_['id'] for _ in cond['selectAgents']],
- 'dealerIdList': [_['dealerId'] for _ in cond['selectDealers']],
- 'groupIdList': [_['id'] for _ in cond['selectGroups']]
- }
- for cond in self.devCondition
- ]
- def recharge(self, amount): return self.update(inc__balance=amount)
- class AdvertisementConfig(EmbeddedDocument):
- """
- 广告配置
- """
- offlineFansNumber = IntField(verbose_name='每日最多粉丝量', default=-1)
- class Advertisement(UserSearchable):
- """
- ..modified 2018/05/24
- 目前广告模式
- """
- #: 广告基本属性
- name = StringField(verbose_name="广告名称", max_length=100)
- adType = StringField(verbose_name="广告的类型", default="")
- adSpace = StringField(verbose_name="广告位", default="payAfter")
- adId = CustomizedSequenceField(verbose_name="内置数字自增ID", value_decorator=lambda _: int(_) + 100000)
- managerId = StringField(verbose_name="管理员ID", null=False)
- startTime = DateTimeField(verbose_name="开始时间", default=datetime.datetime.now)
- endTime = DateTimeField(verbose_name="结束时间", default=datetime.datetime.now() + datetime.timedelta(days=365))
- groupName = StringField(verbose_name="地址组名称", default="")
- provinceId = StringField(verbose_name="省份ID", default="")
- cityId = StringField(verbose_name="城市ID", default="")
- areaId = StringField(verbose_name="区域ID", default="")
- address = StringField(verbose_name="地址", default="", max_length=200)
- addressType = StringField(verbose_name="地址类型", default="")
- #: 广告创意
- img = StringField(verbose_name="图片url", default="")
- link = StringField(verbose_name="定向链接", default="")
- word = StringField(verbose_name="广告词", max_length=100)
- script = StringField(verbose_name="插入脚本文本段")
- scriptType = StringField(verbose_name='脚本类型')
- showType = StringField(verbose_name=u"展现方式", default="jump")
- #: 广告过滤器
- #:: 面向投放环境的过滤器(目前为设备)
- publishers = ListField(verbose_name="特定的发布主 == 经销商", default=['*'])
- agents = ListField(verbose_name="代理商", default=[])
- addressTypeList = ListField(verbose_name="地址类型的列表", default=[])
- devTypeList = ListField(verbose_name="设备类型的列表", default=[])
- devCondition = ListField(verbose_name="设备的过滤条件",
- default=[{"selectAgents": [], "selectDealers": [], "selectGroups": []}])
- #:: 面向用户的广告过滤器
- targetSex = StringField(verbose_name="定位投放性别", default='*')
- targetPhoneOS = ListField(verbose_name="用户手持终端", default=[])
- gateway = ListField()
- #: 接受广告投递的设备
- #: `example` [{"logicalCode" : "32320", "devTypeName" : "纸巾机"}]
- devList = ListField(verbose_name="接受广告投递的设备", default=[])
- #: 定价
- price = FloatField(verbose_name="推送一次的价格", default=0.0)
- agentPrice = FloatField(verbose_name="推送一次的代理商分成价格", default=0.0)
- dealerPrice = FloatField(verbose_name="推送一次的经销商分成价格", default=0.0)
- #: 配置项
- configs = DictField(verbose_name="配置", default={})
- offlineFansNumber = IntField(verbose_name='每日最多粉丝量', default=-1)
- PIN = StringField(verbose_name="广告主登录密码", default='1234')
- fansType = StringField(verbose_name="吸粉类型", default='official')
- advertiserId = StringField(verbose_name="广告主ID", default='')
- online = BooleanField(verbose_name=u"广告是否上线", default=False)
- #: 为了确保不产生脏数据,广告的删除并不做实质的删除,以后随着数据量增加,可考虑转储
- deleted = BooleanField(verbose_name="是否删除的flag", default=False)
- deletedTime = DateTimeField()
- meta = {
- "collection": "ads",
- "db_alias": "default",
- 'indexes': [
- 'advertiserId',
- 'managerId',
- 'adId'
- ]
- }
- search_fields = ('word', 'adId', 'name', 'link')
- def __str__(self):
- return 'Advertisement<id={} adId={}>'.format(str(self.id), self.adId)
- def count_cache_key(self, date=None):
- if date is None:
- date = datetime.datetime.now().strftime(Const.DATE_FMT)
- return 'adId-{adId}-count-{date}'.format(adId=self.adId, date=date)
- def is_authenticated(self):
- return True
- def set_password(self, raw_password):
- self.password = make_password(raw_password)
- self.save()
- return self
- def check_password(self, raw_password):
- return raw_password == self.password
- @staticmethod
- def get_md5_password(raw_password):
- return make_password(raw_password)
- @classmethod
- def get_available_ads(cls, managerId=None, **kwargs):
- """
- only return un-deleted ads
- :param managerId:
- :return:
- """
- if managerId:
- return cls.objects(managerId=managerId, deleted=False, **kwargs)
- else:
- return cls.objects(deleted=False, **kwargs)
- @classmethod
- def filter_pay_after(cls, **kwargs):
- """
- :param kwargs:
- :return:
- """
- return cls.get_available_ads(adSpace=AdSpace.PAYAFTER, endTime__gte=datetime.datetime.now(), **kwargs)
- @classmethod
- def filter_by_top_show(cls, **kwargs):
- """
- :param kwargs:
- :return:
- """
- return cls.get_available_ads(adSpace=AdSpace.TOPSHOW, showType=AdType.BANNER, **kwargs)
- @classmethod
- def get_by_adId(cls, adId):
- # type: (int) -> Union[Advertisement,None]
- if adId is None:
- return None
- return cls.objects(adId=int(adId), deleted=False).first()
- @classmethod
- def get_by_ids(cls, ids):
- return cls.objects.filter(id__in=ids)
- def flag_deleted(self):
- return self.update(deleted=True, deletedTime=datetime.datetime.now())
- def to_dict(self):
- rv = super(Advertisement, self).to_dict()
- if ObjectId.is_valid(self.advertiserId):
- advertiser = Advertiser.objects(id=self.advertiserId).first()
- if advertiser is None:
- logger.warn(u'广告主未找到, adId=(%d), advertiserId(%s)' % (self.adId, self.advertiserId,))
- advertiserInfo = ''
- else:
- advertiserInfo = u'%s(%s)额度[%d]' % (advertiser.nickname, advertiser.username, advertiser.quota)
- else:
- advertiserInfo = ''
- rv.update({
- #: 广告基本属性
- 'id': str(self.id),
- 'name': self.name,
- 'adType': self.adType,
- 'img': self.img,
- 'word': self.word,
- 'link': self.link,
- 'adId': self.adId,
- 'startTime': self.startTime.strftime("%Y-%m-%d %H:%M"),
- 'endTime': self.endTime.strftime("%Y-%m-%d %H:%M"),
- # 广告主
- 'advertiserInfo': advertiserInfo,
- #: 过滤器
- 'devCondition': self.devCondition,
- 'devTypeList': self.devTypeList,
- 'addressTypeList': self.addressTypeList,
- #: 接受广告投递的设备
- 'devList': self.devList,
- #:: 面向用户的广告过滤器
- 'targetPhoneOS': self.targetPhoneOS,
- 'targetSex': self.targetSex,
- #: 定价
- 'price': self.price,
- 'agentPrice': self.agentPrice,
- 'dealerPrice': self.dealerPrice,
- 'online': self.online,
- #: 配置项
- 'configs': self.configs,
- 'PIN': self.PIN,
- 'login': False,
- 'loginUsername': '',
- 'fansType': self.fansType,
- 'offlineFansNumber': self.offlineFansNumber,
- 'script': self.script,
- 'scriptType': self.scriptType
- })
- return rv
- @property
- def allocated_device_logicalCodes(self):
- return [_['logicalCode'] for _ in self.devList]
- @property
- def allocations(self):
- # type: ()->Dict[str, List[str]]
- allocation_map = {'agent': [], 'dealer': [], 'group': [], 'device': self.allocated_device_logicalCodes}
- for cond in self.devCondition:
- allocation_map['agent'].extend([_['id'] for _ in cond['selectAgents']])
- allocation_map['dealer'].extend([_['dealerId'] for _ in cond['selectDealers']])
- allocation_map['group'].extend([_['id'] for _ in cond['selectGroups']])
- return allocation_map
- def to_json(self):
- return json.dumps(self.to_dict())
- def to_redis(self):
- return
- def clean(self):
- if self.startTime > self.endTime:
- raise CustomizedValidationError(u'开始时间必须大于结束时间')
- @property
- def proxy(self):
- # return AdProxy(self.adId)
- return
- def set_proxy(self):
- # return AdProxy(self.adId, **self.to_dict()).to_redis()
- return
- def set_offline(self):
- return self.update(status=False)
- #: 目前的规则做简单一点,每天一次的
- @staticmethod
- def is_show_huawei_auth_web(openId):
- ads = AdRecord.objects.filter(openId=openId, adId=0)
- if ads.count() > 0:
- return False
- return True
- def match(self, feature, against):
- if feature == 'sex':
- if self.targetSex == '*' or not self.targetSex:
- return True
- else:
- # TODO to remove, to change data to english
- _ = {u'男': 'male', u'女': 'female'}
- return _[self.targetSex] == against
- elif feature == 'phoneOS':
- if not self.targetPhoneOS:
- return True
- else:
- return against in self.targetPhoneOS
- elif feature == 'gateway':
- if not self.gateway:
- return True
- else:
- return against in self.gateway
- @property
- def selected_user_feature_keys(self):
- return ['phoneOS', 'sex', 'gateway']
- @property
- def selected_user_feature_map(self):
- return {_: getattr(self, _) for _ in self.selected_user_feature_keys if getattr(self, _)}
- def match_user(self, user):
- """
- 广告匹配用户
- :param user:
- :return:
- :rtype: bool
- """
- #: 理论上来讲匹配用户的特征应该是用户本身特征的子集
- if not set(self.selected_user_feature_keys).issubset(user.feature_keys):
- return False
- #: 去除所有空项,两者特征吻合,即配对成功
- else:
- for feature, against in user.feature_map.iteritems():
- if not self.match(feature, against):
- return False
- return True
- @property
- def ad_show_dict(self):
- """
- 获取广告信息. link字段是和以前的版本兼容, 后续修改后可以去掉
- :return:
- """
- rv = {'adShow': 'show', 'showType': self.showType, 'url': '', 'link': ''}
- if self.showType == 'jump':
- rv.update({'url': self.link, 'title': self.word})
- elif self.showType == 'float':
- rv.update({'img': self.img, 'url': self.link, 'title': self.word})
- elif self.showType == 'banner':
- rv.update({'img': self.img, 'url': self.link})
- elif self.showType == 'floatJump':
- rv.update({'img': self.img, 'url': self.link, 'title': self.word})
- return rv
- @classmethod
- def get_ali_cpm_url_v3(cls, adUser, logicalCode):
- # type: (AdUser, str)->Optional[dict]
- openId = adUser.openId
- if not openId:
- return None
- if not logicalCode:
- return None
- dev = Device.get_dev_by_logicalCode(logicalCode)
- if not dev:
- return None
- devObj = DevRegToAli.objects(logicalCode=logicalCode).first()
- if not devObj:
- regResult = DevRegToAli.reg_to_aliyun_v3(dev)
- if not regResult:
- return None
- try:
- result = AlipayYunMaV3().get_cpm_body(openId, logicalCode)
- logger.info(result)
- if result.body.success == True:
- # # 广告添加日志
- # try:
- # adLog = AdLog()
- # adLog.bidid = result.body.result.bidid
- # adLog.version = '3.0'
- # adLog.responseData = json.dumps(result.body.result.to_map(), ensure_ascii=False)
- # adLog.name = 'alipay_cpm'
- # adLog.adType = 'payafter'
- # adLog.openId = adUser.openId
- # adLog.logicalCode = logicalCode
- # adLog.gateway = 'alipay'
- # adLog.save()
- # except:
- # logger.error(traceback.format_exc())
- if len(result.body.result.seatbid[0].bid[0].ads):
- curl = result.body.result.seatbid[0].bid[0].ads[0].crurl
- return curl
- except Exception as e:
- logger.exception(e)
- @classmethod
- def get_pay_after_ad(cls, adUser, **kwargs):
- # type: (AdUser, dict)->dict
- ads = cls.filter_pay_after(**kwargs)
- for ad in ads: # type: Advertisement
- if ad.match_user(adUser):
- ad_dict = ad.ad_show_dict
- AdStatistics.inc_make_count(
- adSpace=AdSpace.PAYAFTER,
- showType='payAfter_{}'.format(ad_dict['showType']), gateway=adUser.feature_map['gateway'])
- return ad_dict
- return None
- @classmethod
- def get_ruhui_url(cls, adUser, dealer, logicalCode):
- if 'disable_ruhui_after_pay' in dealer.features:
- logger.debug('user<openid={}> get ruhui url for dev<l={}> is disable'.format(adUser.openId, logicalCode))
- return None
- from apps.web.user.utils import RedpackBuilder
- ruhui = RedpackBuilder.get_alipay_cpa_by_ruhui(openId=adUser.openId, logicalCode=logicalCode, showType='floatRedpack')
- if ruhui:
- ad_dict = {
- 'adShow': 'show',
- 'url': ruhui['url'],
- 'showType': 'floatRedpack',
- 'img': 'https://cdn.washpayer.com/promotion/redpack2.png',
- 'title': '点击入会天猫领取红包',
- }
- logger.debug(
- 'user<openid={}> get ruhui url for dev<l={}> is {}'.format(adUser.openId, logicalCode, ad_dict))
- AdStatistics.inc_make_count(adSpace=AdSpace.PAYAFTER, showType='payAfter_ruhui', gateway='alipay')
- return ad_dict
- else:
- logger.debug('user<openid={}> get ruhui url for dev<l={}> is null'.format(adUser.openId, logicalCode))
- return None
- @classmethod
- def get_default_ad_img(cls, showType):
- if showType == 'jump':
- return None
- elif showType in ['floatJump', 'float']:
- return 'https://cdn.washpayer.com/promotion/payafter_float_jump_1.png'
- else:
- return 'https://cdn.washpayer.com/promotion/payafter_banner_2.png'
- @classmethod
- def _fetch_payafter_ad(cls, ua, user, dealer, device=None):
- # type: (str, MyUser, Dealer, DeviceDict)->Optional[dict]
- try:
- gateway = detect_app_from_ua(ua)
- phoneOS = user_agents.parse(ua).os.family
- if gateway == 'alipay':
- showType = 'jump'
- else:
- if dealer.ad_show == 'show':
- showType = 'jump'
- else:
- showType = dealer.ad_show
- if is_user(user):
- adUser = AdUser(
- openId=user.openId,
- feature_keys=['phoneOS', 'sex', 'gateway'],
- feature_map={'phoneOS': phoneOS, 'sex': user.sex_in_en, 'gateway': gateway})
- else:
- adUser = AdUser(
- openId=None,
- feature_keys=['phoneOS', 'sex', 'gateway'],
- feature_map={'phoneOS': phoneOS, 'sex': 'male', 'gateway': gateway})
- if gateway == 'alipay' and device:
- support_ali = serviceCache.get('supportAliAd')
- if support_ali is None or support_ali != 'no':
- support_ali = True
- else:
- support_ali = False
- if support_ali:
- ali_cmp_url = cls.get_ali_cpm_url_v3(adUser=adUser, logicalCode=device.logicalCode)
- if ali_cmp_url:
- img = cls.get_default_ad_img(showType)
- ad_dict = {
- 'adShow': 'show',
- 'url': ali_cmp_url,
- 'showType': showType,
- 'title': '<div class="title">即将自动转到广告页面</div><div class="sub-title">请谨慎交易!如遇欺诈广告请联系平台投诉</div>'
- }
- if img:
- ad_dict.update({'img': img})
- return ad_dict
- return cls.get_pay_after_ad(adUser=adUser, showType=showType)
- except Exception as e:
- logger.exception(e)
- return None
- @classmethod
- def fetch_payafter_ad(cls, ua, user, dealer, device=None):
- # type: (str, User, Dealer, DeviceDict)->Optional[dict]
- ad_dict = None
- try:
- while True:
- gateway = detect_app_from_ua(ua)
- if gateway == 'alipay':
- pass
- # ad_dict = cls.get_ruhui_url(user, dealer, device.logicalCode)
- # if ad_dict:
- # break
- else:
- if dealer.ad_show == 'noshow':
- logger.debug('dealer<id={}> close ad.'.format(str(dealer.id)))
- break
- if device:
- now = datetime.datetime.now()
- if device.disableADExpireDate and device.disableADExpireDate >= now:
- break
- ad_dict = cls._fetch_payafter_ad(ua, user, dealer, device)
- break
- if not ad_dict:
- AdStatistics.inc_make_count(adSpace=AdSpace.PAYAFTER, showType='payAfter_noshow', gateway=gateway)
- ad_dict = {
- 'adShow': 'noshow'
- }
- return ad_dict
- except Exception as e:
- logger.exception(e)
- return None
- class AliAdLog(Searchable):
- uid = StringField(verbose_name=u"用户标识")
- uidType = StringField(verbose_name=u"用户标识类型")
- campaignId = StringField(verbose_name=u"⼴告计划ID")
- bid = StringField(verbose_name=u"链路ID")
- tradeTS = IntField(verbose_name = u'交易时间')
- commission = StringField(verbose_name = u'参考收入')
- meta = {
- 'collection': 'ali_ad_log',
- 'db_alias': 'logdata',
- }
- AD_RECORD_FILTER_FIELDS = ['adId',
- 'openId',
- 'groupId',
- 'devNo',
- 'dealerId',
- 'agentId',
- 'devNo',
- 'managerId',
- 'advertiserId'] # type: List[AnyStr]
- class AdRecord(Searchable):
- adType = StringField()
- clicks = IntField(verbose_name="点击次数", default=0)
- shows = IntField(verbose_name="展示次数", default=0)
- rewarded = BooleanField(verbose_name="是否领取奖励成功", default=False)
- converted = BooleanField(verbose_name="是否转换成功", default=False)
- agentPrice = IntField(verbose_name='代理商广告价格')
- dealerPrice = IntField(verbose_name='经销商广告价格')
- price = IntField(verbose_name='广告价格')
- adName = StringField(verbose_name='广告名称')
- agentName = StringField(verbose_name='代理商名称')
- dealerName = StringField(verbose_name='经销商名称')
- nickname = StringField(verbose_name='用户昵称')
- sex = IntField(verbose_name='用户性别')
- devType = StringField(verbose_name='设备类型')
- address = StringField(verbose_name='设备所在地址')
- groupName = StringField(verbose_name='设备所在组名称')
- #: filters
- adId = IntField(verbose_name="广告ID")
- openId = StringField(verbose_name="点击者微信ID", default="")
- groupId = StringField(verbose_name="设备地址编号", default="")
- dealerId = StringField(verbose_name="经销商ID", default="")
- agentId = StringField(verbose_name="代理商ID", default="")
- devNo = StringField(verbose_name="设备号", default="")
- logicalCode = StringField(verbose_name="设备逻辑编码", default="")
- managerId = StringField(verbose_name="厂商ID", default="")
- advertiserId = StringField(verbose_name="广告主ID", default="")
- dateTimeAdded = DateTimeField(default=datetime.datetime.now, verbose_name='添加进来的时间')
- createdDate = StringField(verbose_name='添加日期',
- default=lambda: datetime.datetime.now().strftime(Const.DATE_FMT))
- dIncome = FloatField(verbose_name="经销商收入", default=0.0)
- aIncome = FloatField(verbose_name="代理商收入", default=0.0)
- mIncome = FloatField(verbose_name="厂家收入", default=0.0)
- features = DictField(verbose_name="额外特征", default={})
- remark = StringField(verbose_name="备注", default='')
- meta = {
- 'collection': 'ad_records',
- 'db_alias': 'logdata',
- 'indexes': [
- #: filters
- 'adId',
- 'openId',
- 'groupId',
- 'devNo',
- 'dealerId',
- 'agentId',
- 'devNo',
- 'managerId',
- 'advertiserId',
- #: date
- 'createdDate',
- '-dateTimeAdded',
- #: unique key
- {'fields': ['adId', 'openId'], 'unique': True}
- ]
- # ,'shard_key':('openId',)
- }
- def __repr__(self): return '<AdRecord adId=%s, openId=%s>' % (self.adId, self.openId)
- @classmethod
- def get_converted_records(cls, **kwargs):
- return cls.objects(converted=True, **kwargs)
- @classmethod
- def filter_pay_after(cls, **kwargs):
- return cls.objects(adType=AdType.PAY_AFTER, **kwargs)
- def income_for_whom(self, whom):
- _ = {
- 'dealer': self.dIncome,
- 'agent': self.aIncome,
- 'manager': self.mIncome
- }
- return _.get(whom)
- @staticmethod
- def filter_fields(): return AD_RECORD_FILTER_FIELDS
- def convert(self): return self.update(converted=True)
- def reward(self): return self.update(rewarded=True)
- @property
- def amount(self): return 0
- @property
- def dealer(self):
- Dealer = import_string('apps.web.dealer.models.Dealer')
- return Dealer.objects(self.dealerId).get()
- @classmethod
- def get_by_user_and_adId(cls, openId, adId): return cls.objects(openId=openId, adId=adId).first()
- @classmethod
- def user_today_already_rewarded(cls, openId):
- """
- 用户受限一天只能参与一次广告活动
- :param openId:
- :return:
- """
- todayDate = datetime.datetime.now().strftime(Const.DATE_FMT)
- return cls.objects(openId=openId, converted=True, createdDate=todayDate).count() > 0
- @classmethod
- def count_today_by_adId(cls, adId):
- # type:(str) -> int
- """
- 一些广告会有每日效果完成需要数额的限制
- TODO 也许需要提供可以修改的campaignTime
- :param adId:
- :return:
- """
- ad = Advertisement.get_by_adId(int(adId)) # type: Advertisement
- now = datetime.datetime.now()
- ad_campaign_started_datetime = ad.dateTimeAdded
- def _replace(dt):
- return dt.replace(
- hour=ad_campaign_started_datetime.hour,
- minute=ad_campaign_started_datetime.minute,
- second=ad_campaign_started_datetime.second)
- startTime = _replace(now)
- return cls.objects(adId=adId, dateTimeAdded__gte=startTime).count()
- def to_dict(self):
- sexMap = {0: '-', 1: u'男', 2: u'女', '': '-'}
- return {
- 'adId': self.adId,
- 'adName': self.adName,
- 'agentPrice': self.agentPrice,
- 'dealerPrice': self.dealerPrice,
- 'price': self.price,
- 'agentName': self.agentName,
- 'dealerName': self.dealerName,
- 'logicalCode': self.logicalCode,
- 'nickname': self.nickname,
- 'sex': sexMap.get(self.sex, '-'),
- 'openId': self.openId,
- 'timestamp': self.to_js_timestamp(self.dateTimeAdded),
- 'devType': self.devType,
- 'address': self.address,
- 'groupName': self.groupName
- }
- def to_dict_in_cn(self):
- sexMap = {0: '-', 1: u'男', 2: u'女'}
- return OrderedDict([
- (u'广告编号', self.adId),
- (u'广告名称', self.adName),
- (u'代理商价格', self.aIncome),
- (u'经销商价格', self.dIncome),
- (u'广告总价', self.mIncome),
- (u'点击时间', self.dateTimeAdded.strftime('%Y-%m-%d %H:%M:%S')),
- (u'代理商', self.agentName),
- (u'经销商', self.dealerName),
- (u'设备编码', self.logicalCode),
- (u'粉丝昵称', self.nickname),
- (u'粉丝性别', sexMap.get(self.sex, '-')),
- (u'粉丝ID', self.openId),
- (u'粉丝转化时间', self.dateTimeAdded.strftime('%Y-%m-%d %H:%M:%S')),
- (u'设备类型', self.devType),
- (u'设备地址', self.address),
- (u'设备地址名', self.groupName)
- ])
- def to_detail(self):
- # type: ()->dict
- """
- :return:
- """
- return self.to_dict()
- class AdPV(Searchable):
- dateTimeAdded = DateTimeField(default=datetime.datetime.now, verbose_name='添加进来的时间')
- createdDate = StringField(verbose_name='添加日期',
- default=lambda: datetime.datetime.now().strftime(Const.DATE_FMT))
- extra = DictField()
- meta = {
- 'collection': 'ad_pv',
- 'db_alias': 'logdata'
- }
- class AdWord(DynamicDocument):
- title = StringField()
- promotionUrl = StringField()
- dateTimeAdded = DateTimeField(default=datetime.datetime.now, verbose_name=u'添加进来的时间')
- dateTimeUpdated = DateTimeField(default=datetime.datetime.now, verbose_name=u'添加进来的时间')
- expireAt = DateTimeField(default=None, verbose_name=u'过期时间')
- dayQuota = IntField(default=sys.maxint, verbose_name=u'日限额')
- # totalQuota = IntField(default = sys.maxint, verbose_name = u'总共限额')
- used = DictField(default={}, verbose_name=u'使用计数')
- account = StringField(default=None, verbose_name=u'第三方推广平台账号')
- itemId = StringField(verbose_name=u'淘宝商品id', default=None)
- autoPass = BooleanField(verbose_name=u'是否需要生成口令', default=True)
- meta = {
- 'collection': 'ad_words',
- 'db_alias': 'logdata',
- 'ordering': ['-dateTimeAdded']
- }
- def to_dict(self):
- return {
- 'id': self.id,
- 'title': self.title,
- 'promotionUrl': self.promotionUrl,
- 'createdTime': self.dateTimeAdded
- }
- @classmethod
- def get_random_one(cls, **kwargs):
- # type: (**Any)->Optional[AdWord]
- today = today_format_str()
- kwargs.update({
- 'expireAt__gte': datetime.datetime.now(),
- '__raw__': {
- '$expr': {
- '$lt': ['$used.{}'.format(today), '$dayQuota']
- }
- }
- })
- return cls.objects(**kwargs).order_by('used.{}'.format(today)).first()
- def incr_count(self, count):
- today = today_format_str()
- yesterday = yesterday_format_str()
- update = {
- 'inc__used__total': count,
- 'inc__used__{}'.format(today): count,
- 'unset__used__{}'.format(yesterday): 0,
- 'dateTimeUpdated': datetime.datetime.now()
- }
- self.update(**update)
- class AdReportDay(Searchable):
- adId = IntField(verbose_name="广告ID")
- date = DateTimeField(verbose='date 统计运行的时间', default=datetime.datetime.now())
- reportDate = StringField(verbose='date 数据对应的时间', default='')
- requestCount = IntField(verbose_name=u'请求数目', default=0)
- meta = {'collection': 'ad_report_day', 'db_alias': 'logdata'}
- class DevRegToAli(Searchable):
- logicalCode = StringField(verbose_name=u'设备编号')
- responseData = StringField(verbose='注册回复的数据', default='')
- meta = {
- "collection": "dev_reg_aliyun",
- 'db_alias': 'logdata',
- "index": [
- "logicalCode"
- ]
- }
- sceneDict = {
- 'school': (u'学校'),
- }
- @staticmethod
- def get_school_scene(desc):
- if u'中' in desc or u'学校' in desc or u'卫校' in desc:
- return u'高中'
- if u'大学' in desc or u'院' in desc:
- return u'普通高等学校'
- else:
- return u'普通高等学校'
- @staticmethod
- def reg_to_aliyun_v3(dev):
- logger.debug('reg ali ad: {}'.format(dev.logicalCode))
- group = dev.group
- grpType = group['addressType']
- if grpType == 'school':
- first = u'学校'
- second = DevRegToAli.get_school_scene(group['groupName'] + group['address'])
- elif grpType == 'apartment':
- first = u'社区'
- second = u'普通社区'
- elif grpType == 'workshop':
- first = u'办公场所/园区'
- second = u'工业园区'
- elif grpType == 'mall':
- first = u'百货百货//购物中心购物中心'
- second = u'商业街'
- elif grpType == 'hospital':
- first = u'医院/医疗机构'
- second = u'综合医院'
- elif grpType == 'cafe':
- first = u'休闲娱乐'
- second = u'KTV/酒吧'
- elif grpType == 'KTV':
- first = u'休闲娱乐'
- second = u'KTV/酒吧'
- elif grpType == 'hotel':
- first = u'酒店'
- second = u'经济连锁'
- elif grpType == 'bar':
- first = u'休闲娱乐'
- second = u'KTV/酒吧'
- elif grpType == 'parking_lot':
- first = u'交通出行'
- second = u'停车场'
- elif grpType == 'square':
- first = u'公共区域/设施'
- second = u'城市公园/景区'
- elif grpType == 'bath_center':
- first = u'休闲娱乐'
- second = u'按摩/足疗/洗浴/汗蒸'
- else:
- first = u'社区'
- second = u'普通社区'
- try:
- areas = District.get_district(group['districtId']).split(' ')
- province, city, zone = areas[0], areas[1], areas[2]
- except Exception as e:
- # TODO: 没有先随便返回一个
- logger.exception(e)
- province, city, zone = u'山东省', u'滨州市', u"滨城区"
- dic = {
- 'first_scene': first,
- 'second_scene': second,
- 'outer_code': dev.logicalCode,
- 'province': province,
- 'city': city,
- 'district': zone,
- 'location_name': group['groupName'],
- 'floor': '1',
- 'device_model_number': dev.devTypeName or dev.majorDeviceType,
- }
- try:
- result = AlipayYunMaV3().reg_dev(dic)
- logger.debug(result.to_map())
- if result.body.code in ['0', '13002']:
- try:
- DevRegToAli.objects(logicalCode = dev.logicalCode).upsert_one(
- logicalCode = dev.logicalCode, responseData = json.dumps(result.to_map(), ensure_ascii = False)
- )
- except:
- pass
- return True
- else:
- return False
- except:
- return False
- class AdStatistics(DynamicDocument):
- adSpace = StringField(verbose_name=u"广告位")
- showType = StringField(verbose_name=u"广告类型")
- gateway = StringField(verbose_name=u'展示平台')
- makeCount = IntField(verbose_name=u"url生成次数", default=0)
- sDate = StringField(verbose_name=u'统计日期')
- meta = {
- "collection": "ad_statistics",
- "db_alias": "logdata",
- }
- @classmethod
- def inc_make_count(cls, adSpace, showType, gateway):
- cls.objects(sDate=datetime.datetime.now().strftime("%Y-%m-%d"), adSpace=adSpace, showType=showType,
- gateway=gateway).update_one(
- upsert=True, inc__makeCount=1)
|