# -*- 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'.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 get ruhui url for dev 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 get ruhui url for dev 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 get ruhui url for dev 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': '
即将自动转到广告页面
请谨慎交易!如遇欺诈广告请联系平台投诉
' } 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 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 '' % (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)