views.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. """
  4. web.ad.views
  5. ~~~~~~~~~
  6. """
  7. import datetime
  8. import itertools
  9. import logging
  10. import random
  11. import traceback
  12. import simplejson as json
  13. import xmltodict
  14. from bson.objectid import ObjectId
  15. from django.core.handlers.wsgi import WSGIRequest
  16. from django.views.decorators.http import require_POST, require_GET
  17. from mongoengine.errors import NotUniqueError
  18. from typing import List, Union, Optional, Iterable, cast, TYPE_CHECKING, Dict
  19. from apilib import utils_datetime
  20. from apilib.utils import flatten
  21. from apilib.utils_datetime import dt_to_timestamp, date_to_datetime_floor, date_to_datetime_ceiling
  22. from apilib.utils_json import JsonResponse
  23. from apps import serviceCache
  24. from apps.web.ad import OfflineTaskType, MyJsonOkResponse
  25. from apps.web.ad.models import Advertisement, AdRecord, Advertiser, AdWord, AliAdLog
  26. from apps.web.ad.utils import (get_available_ads_by_user)
  27. from apps.web.agent.models import Agent
  28. from apps.web.common.validation import PASSWORD_RE
  29. from apps.web.core import ROLE
  30. from apps.web.core.db import prepare_conditions, prepare_query
  31. from apps.web.core.exceptions import InvalidPermission
  32. from apps.web.core.models import OfflineTask
  33. from apps.web.core.utils import DefaultJsonErrorResponse, parse_json_payload
  34. from apps.web.core.utils import JsonErrorResponse, JsonOkResponse
  35. from apps.web.dealer.models import Dealer
  36. from apps.web.device.models import Device
  37. from apps.web.device.models import Group
  38. from apps.web.management.models import Manager
  39. from apps.web.user.models import MyUser, Redpack
  40. from apps.web.user.utils import RedpackBuilder
  41. from apps.web.utils import advertiser_login, \
  42. is_advertiser, is_manager, is_advertisement
  43. from apps.web.utils import error_tolerate, permission_required
  44. from taskmanager.mediator import task_caller
  45. if TYPE_CHECKING:
  46. from apps.web.common.models import UserSearchable
  47. logger = logging.getLogger(__name__)
  48. @error_tolerate(nil = DefaultJsonErrorResponse)
  49. @require_POST
  50. @permission_required(ROLE.manager, ROLE.advertiser)
  51. def getAdPreAllocatedDevice(request):
  52. # type:(WSGIRequest)->JsonResponse
  53. """
  54. 根据多种多样的查询条件获取供分配设备列表List[Dict]
  55. {"devCondition":[{"agentIdList":[],"dealerIdList":[],"groupIdList":[]}],"addressTypeList":[],"devTypeList":[]}
  56. get_devices query =
  57. dealerIdList => Device.objects(ownerId__in=dealerIdList)
  58. | agentIdList => Device.objects(Dealer.objects(agentId__=agentIdList)
  59. | groupIdList => Device.objects(groupId__in=groupIdList)
  60. devices.filter( _ => _.devType in devTypeList & _ => _.group.addressType in addressTypeList)
  61. return [ { 'logicalCode': x, 'devType': 'y' } ]
  62. TODO:
  63. 优化方向,
  64. 0. 优化解析过程
  65. 1. 建立query解析缓存
  66. 2. 运用mongodb pipeline
  67. 3. 本地化部分数据(将厂商ID绑定到设备端,解绑记得清除,将地址类型也绑定到设备,解绑记得清除)
  68. :param request:
  69. :return:
  70. :rtype: JsonResponse
  71. """
  72. query = json.loads(request.body)
  73. currentUser = request.user #type: cast(Union[Manager, Advertiser])
  74. if is_manager(currentUser):
  75. devCondition, addressTypeList, devTypeList = query['devCondition'], query['addressTypeList'], query['devTypeList']
  76. elif is_advertiser(currentUser):
  77. devCondition, addressTypeList, devTypeList = currentUser.preAllocatedDeviceConditions, query['addressTypeList'], query['devTypeList']
  78. else:
  79. raise InvalidPermission(u'只有广告主或厂商才能查询广告投放设备')
  80. hasDevCondition = bool(len(list(itertools.ifilter(lambda _ : _ != [], flatten(_.values() for _ in devCondition)))))
  81. hasAddressTypeList = bool(len(addressTypeList))
  82. hasDevTypeList = bool(len(devTypeList))
  83. def imerge_dicts(iterable):
  84. """
  85. Given any number of dicts, shallow copy and merge into a new dict,
  86. precedence goes to key value pairs in latter dicts.
  87. """
  88. result = {}
  89. for dictionary in iterable:
  90. result.update(dictionary)
  91. return result
  92. to_query = lambda _ : imerge_dicts( x.to_query(None) for x in prepare_conditions(_) )
  93. def acc_or_append(q, data):
  94. """
  95. :param q: [{}]
  96. :param data:
  97. :return:
  98. """
  99. if not len(q):
  100. q.append(data)
  101. else:
  102. for item in q:
  103. if data == item: break
  104. elif item['field'] == data['field'] and item['operator'] == data['operator']:
  105. item['value'] = list(set(item['value'] + data['value']))
  106. break
  107. else:
  108. q.append(data)
  109. break
  110. def parse_devCondition(conditions):
  111. """
  112. :param conditions: [{..conditions}]
  113. :return:
  114. """
  115. queries = {
  116. 'device': [],
  117. 'dealer': [],
  118. 'group': []
  119. }
  120. for cond in conditions:
  121. # 优先级 groupIdList > dealerIdList > agentIdList
  122. if cond['groupIdList']:
  123. acc_or_append(queries['group'], {"field": "_id", "operator": "in", "value": [ ObjectId(_) for _ in cond['groupIdList']] })
  124. continue
  125. elif cond['dealerIdList']:
  126. acc_or_append(queries['group'], {"field": "ownerId", "operator": "in", "value": map(str, cond['dealerIdList']) })
  127. continue
  128. elif cond['agentIdList']:
  129. acc_or_append(queries['dealer'], {"field": "agentId", "operator": "in", "value": cond['agentIdList']})
  130. continue
  131. return queries
  132. #: 生成,拼装请求,延迟请求Device到最后, 减少IO
  133. queries = parse_devCondition(devCondition)
  134. #: 默认只能选择某厂商下的所有设备
  135. #acc_or_append(queries['device'],{"field": "managerId", "operator": "", "value": managerId})
  136. #: 不选择的时候,选择所有组的设备
  137. if not hasDevCondition:
  138. if is_manager(currentUser):
  139. agentIds = [ str(_['_id']) for _ in Agent.get_collection().find({'managerId': str(currentUser.id)})]
  140. acc_or_append(queries['dealer'], {"field": "agentId", "operator": "in", "value": agentIds})
  141. elif is_advertiser(currentUser):
  142. agentIds = [ str(_.id) for _ in Agent.objects(managerId=currentUser.managerId) ]
  143. acc_or_append(queries['dealer'], {"field": "agentId", "operator": "in", "value": agentIds})
  144. if hasDevTypeList:
  145. acc_or_append(queries['device'], {"field": "devType.id", "operator": "in", "value": devTypeList})
  146. if hasAddressTypeList:
  147. acc_or_append(queries['group'], {"field": "addressType", "operator": "in", "value": addressTypeList})
  148. if queries['dealer']:
  149. dealers = [ str(_['_id']) for _ in Dealer.get_collection().find(to_query(queries['dealer']), {'_id': 1}) ]
  150. acc_or_append(queries['group'], {"field": "ownerId", "operator": "in", "value": dealers})
  151. if queries['group']:
  152. groups = [ str(_['_id']) for _ in Group.get_collection().find(to_query(queries['group']), {'_id': 1}) ]
  153. acc_or_append(queries['device'], {"field": "groupId", "operator": "in", "value": groups})
  154. if queries['device']:
  155. dataList = [ {'logicalCode': _['logicalCode'], 'devTypeName' : _['devType'].get('name')} for _ in
  156. Device.get_collection()
  157. .find(to_query(queries['device']), {'logicalCode': 1, 'devType.name': 1})]
  158. return JsonResponse({'result': 1, 'payload': {'dataList': dataList}})
  159. return JsonResponse({'result': 1, 'payload': {'dataList': []}})
  160. ###: 广告增删查改
  161. @require_GET
  162. @error_tolerate(nil = DefaultJsonErrorResponse)
  163. @permission_required(ROLE.manager, ROLE.advertisement, ROLE.advertiser)
  164. def getAdList(request):
  165. # type:(WSGIRequest)->JsonResponse
  166. """
  167. 获取广告列表
  168. :param request:
  169. :return:
  170. """
  171. return JsonResponse({'result': 1, 'description': '', 'payload': {'total': 0, 'dataList': []}})
  172. @require_GET
  173. @error_tolerate(nil=JsonErrorResponse(u'获取广告分配设备列表失败'))
  174. @permission_required(ROLE.manager, ROLE.advertiser)
  175. def getDevListByAd(request):
  176. # type:(WSGIRequest)->JsonResponse
  177. """
  178. 为了加快广告加载速度,不默认返回设备列表,只有在用户点击广告的时候才会请求设备列表
  179. :param request:
  180. :return:
  181. """
  182. adId = request.GET.get('adId')
  183. devList = Advertisement.get_by_adId(adId=adId).devList
  184. return JsonResponse({'result': 1, 'description': '', 'payload': {'total': len(devList), 'dataList': devList }})
  185. @require_POST
  186. @error_tolerate(nil = JsonErrorResponse(u'添加广告失败'))
  187. @permission_required(ROLE.manager, ROLE.advertiser)
  188. def addAd(request):
  189. # type:(WSGIRequest)->JsonResponse
  190. """
  191. 添加广告
  192. 广告主和厂商都可以添加广告
  193. 广告主只可配置 publicAdAvailable的广告主
  194. :param request:
  195. :return:
  196. """
  197. return JsonResponse({'result': 1, 'description': '', 'payload': {}})
  198. @require_POST
  199. @error_tolerate(nil = JsonErrorResponse(u'编辑广告失败'))
  200. @permission_required(ROLE.manager, ROLE.advertiser)
  201. def editAd(request):
  202. # type:(WSGIRequest)->JsonResponse
  203. """
  204. 编辑广告
  205. :param request:
  206. :return:
  207. """
  208. return JsonResponse({'result': 1, 'description': '', 'payload': {}})
  209. @require_POST
  210. @error_tolerate(nil = JsonErrorResponse(u'删除广告失败'))
  211. @permission_required(ROLE.manager, ROLE.advertiser)
  212. def deleteAd(request):
  213. # type:(WSGIRequest)->JsonResponse
  214. """
  215. 删除广告
  216. :param request:
  217. :return:
  218. """
  219. return JsonResponse({'result': 1, 'description': '', 'payload': {}})
  220. ###: 各种过滤器
  221. @error_tolerate(nil = JsonErrorResponse(u'获取经销商列表失败'))
  222. @permission_required(ROLE.manager)
  223. def getDealerList(request):
  224. # type:(WSGIRequest)->JsonResponse
  225. """
  226. 获取经销商列表
  227. :param request:
  228. :return:
  229. """
  230. agentId = request.GET.get('agentId', None)
  231. if agentId is None:
  232. return JsonResponse({'result': 0, 'description': u'请传入正确的参数', 'payload': {}})
  233. dealers = [{'value': str(dealer.id), 'label': dealer.nickname} for dealer in Dealer.objects(agentId = str(agentId))]
  234. return JsonResponse({'result': 1, 'description': '', 'payload': dealers})
  235. @error_tolerate(nil = JsonErrorResponse(u'获取地址列表失败'))
  236. @permission_required(ROLE.manager)
  237. def getAddressList(request):
  238. # type:(WSGIRequest)->JsonResponse
  239. """
  240. dealer->group.address
  241. :param request:
  242. :return:
  243. """
  244. dealerId = request.GET.get('id')
  245. groupIds = Group.get_group_ids_of_dealer(dealerId)
  246. groups = Group.get_groups_by_group_ids(groupIds)
  247. if groups is None:
  248. return JsonResponse({'result': 1, 'description': '', 'payload': []})
  249. else:
  250. groupIdAddressPair = [{'value': k, 'label': v['address']} for k, v in groups.iteritems()]
  251. return JsonResponse({'result': 1, 'description': '', 'payload': groupIdAddressPair})
  252. @error_tolerate(nil = JsonErrorResponse(u'获取设备列表失败'))
  253. @permission_required(ROLE.manager)
  254. def getDevList(request):
  255. # type:(WSGIRequest)->JsonResponse
  256. """
  257. 广告系统获取设备列表
  258. :param request:
  259. :return:
  260. """
  261. groupId = request.GET.get('id')
  262. deviceNos = Device.get_devNos_by_group([groupId])
  263. devs = Device.get_dev_by_nos(deviceNos)
  264. return JsonResponse(
  265. {
  266. 'result': 1,
  267. 'description': '',
  268. 'payload':
  269. [
  270. {'value': deviceNo, 'label': dev['logicalCode']}
  271. for deviceNo, dev in devs.items()
  272. ] if deviceNos else []
  273. }
  274. )
  275. ### 查看设备被分配的广告列表
  276. @error_tolerate(nil = JsonErrorResponse(u'查看设备广告列表失败'))
  277. @permission_required(ROLE.manager)
  278. def getDeviceAllocatedAds(request):
  279. # type:(WSGIRequest)->JsonResponse
  280. """
  281. :param request:
  282. :return:
  283. """
  284. logicalCode = request.GET.get('logicalCode')
  285. return JsonResponse({'result': 1, 'description': '', 'payload': {'total': 0, 'dataList': []}})
  286. ### 广告数据报表
  287. @error_tolerate(nil = JsonErrorResponse(u'获取趋势列表失败'))
  288. @permission_required(ROLE.manager, ROLE.advertisement, ROLE.advertiser)
  289. def getAdFansTrend(request):
  290. # type:(WSGIRequest)->JsonResponse
  291. """
  292. 获取广告效果趋势
  293. :param request:
  294. :return:
  295. """
  296. currentUser = request.user # type: cast(Union[Advertisement, Advertiser, Manager])
  297. query = prepare_query(request.GET)
  298. queryDict = query.raw
  299. delta = query.attrs['dateTimeAdded__lte'] - query.attrs['dateTimeAdded__gte'] # type: datetime.timedelta
  300. if delta.days > 90:
  301. return JsonErrorResponse(u'只支持查询最远至90天前的数据')
  302. #: TODO to refactor 引入校验schema
  303. if 'adId' in queryDict:
  304. queryDict['adId'] = int(queryDict['adId'])
  305. if is_manager(currentUser):
  306. queryDict['managerId'] = str(currentUser.id)
  307. elif is_advertiser(currentUser):
  308. queryDict['advertiserId'] = str(currentUser.id)
  309. elif is_advertisement(currentUser):
  310. queryDict['adId'] = int(currentUser.adId)
  311. matchStage = {
  312. 'createdDate':
  313. {
  314. '$gte': queryDict.pop('startTime'),
  315. '$lte': queryDict.pop('endTime')
  316. },
  317. }
  318. matchStage.update(queryDict)
  319. projectStage = {
  320. '_id': 0,
  321. 'clicks': 1,
  322. 'createdDate': 1,
  323. 'fans': {'$cond': ['$converted', 1, 0]},
  324. 'maleFans': '$features.sex.is_male',
  325. 'femaleFans': '$features.sex.is_female'
  326. }
  327. projectStage.update(dict.fromkeys(AdRecord.filter_fields(), 1))
  328. groupStage = {
  329. '_id': '$createdDate',
  330. 'clicks': {'$sum': '$clicks'},
  331. 'fans': {'$sum': '$fans'},
  332. 'show': {'$sum': '$clicks'},
  333. 'maleFans': {'$sum': '$maleFans'},
  334. 'femaleFans': {'$sum': '$femaleFans'}
  335. }
  336. aggregates = AdRecord.get_collection().aggregate(
  337. [
  338. {'$project': projectStage},
  339. {'$match': matchStage},
  340. {'$group': groupStage},
  341. {'$addFields': {'time': '$_id'}},
  342. {'$sort': {'time': 1}},
  343. ]
  344. )
  345. return JsonResponse({'result': 1, 'description': '', 'payload': list(aggregates)})
  346. @permission_required(ROLE.manager)
  347. def getAdMoneyTrend(request):
  348. # type:(WSGIRequest)->JsonResponse
  349. """
  350. 收益趋势
  351. :param request:
  352. :return:
  353. """
  354. # TODO 待讨论
  355. return JsonResponse({'result': 1, 'description': '', 'payload': []})
  356. @error_tolerate(nil = DefaultJsonErrorResponse)
  357. @permission_required(ROLE.manager, ROLE.advertisement, ROLE.advertiser)
  358. def getAdFansDetail(request):
  359. # type:(WSGIRequest)->JsonResponse
  360. """
  361. 获取广告效果详情列表
  362. :param request:
  363. :return:
  364. """
  365. query = prepare_query(request.GET)
  366. currentUser = request.user # type: cast(Union[Advertisement, Advertiser, Manager])
  367. if is_manager(currentUser):
  368. query.attrs['managerId'] = str(currentUser.id)
  369. elif is_advertiser(currentUser):
  370. query.attrs['advertiserId'] = str(currentUser.id)
  371. elif is_advertisement(currentUser):
  372. query.attrs['adId'] = int(currentUser.adId)
  373. cursor = AdRecord.objects(converted=True, **query.attrs)
  374. total = cursor.count()
  375. pagedRecords = cursor.order_by('-dateTimeAdded').paginate(pageIndex=query.pageIndex, pageSize=query.pageSize) # type: List[AdRecord]
  376. records = [ r.to_dict() for r in pagedRecords ]
  377. return JsonResponse({'result': 1, 'description': None, 'data': {'total': total, 'dataList': records}})
  378. @error_tolerate(nil=DefaultJsonErrorResponse)
  379. def getPayAfterAd(request):
  380. # type:(WSGIRequest)->JsonResponse
  381. """
  382. 获取支付后广告链接(蓝牙payAfter页面)
  383. :param request:
  384. :return:
  385. """
  386. logicalCode = request.GET.get('logicalCode', None)
  387. if not logicalCode:
  388. logger.debug('no logical code.')
  389. return JsonOkResponse(description='no ads available')
  390. device = Device.get_dev_by_logicalCode(logicalCode)
  391. if not device:
  392. logger.debug('no device find.')
  393. return JsonOkResponse(description='no ads available')
  394. ua = request.META.get('HTTP_USER_AGENT', '')
  395. user = request.user # type: MyUser
  396. ad_dict = Advertisement.fetch_payafter_ad(ua, user, device.owner, device)
  397. logger.debug('ad dict is {}'.format(ad_dict))
  398. if not ad_dict:
  399. return JsonOkResponse(description='no ads available')
  400. else:
  401. return JsonResponse({'result': 1, 'description': u'', 'payload': ad_dict})
  402. def taskList(request):
  403. # type:(WSGIRequest)->JsonResponse
  404. """
  405. 广告适配系统
  406. :param request:
  407. :return:
  408. """
  409. try:
  410. user = request.user # type: cast(MyUser)
  411. devNo = request.GET.get('devNo')
  412. if not devNo:
  413. return JsonResponse({'result': 0, 'description': u'请首先扫码', 'payload': {}})
  414. device = Device.get_dev(devNo)
  415. if not device:
  416. return JsonResponse({'result': 0, 'description': u'设备不存在', 'payload': {}})
  417. ads = get_available_ads_by_user(device, user)
  418. logger.debug('%s got ads(%s) on device(logicalCode=%s)' % (repr(user), ads, device['logicalCode']))
  419. allocated_ads = [{'id': ad.adId, 'name': ad.name, 'word': ad.word, 'qrcode': ad.img} for ad in ads]
  420. random.shuffle(allocated_ads)
  421. return JsonResponse({'result': 1, 'description': None, 'payload': {'taskList': allocated_ads}})
  422. except Exception as e:
  423. logger.exception(e)
  424. return JsonResponse({'result': 1, 'description': None, 'payload': {'taskList': []}})
  425. ### 广告主
  426. @error_tolerate(nil = JsonErrorResponse(u'注册失败'))
  427. def registerAdvertiser(request):
  428. # type:(WSGIRequest)->JsonResponse
  429. """
  430. 注册成为广告主
  431. :param request:
  432. :return:
  433. """
  434. payload = json.loads(request.body)
  435. try:
  436. Advertiser.create_user(**payload)
  437. except NotUniqueError:
  438. return JsonErrorResponse(description = u'该手机号已注册')
  439. return JsonResponse({'result': 1, 'description': None, 'payload': {}})
  440. @error_tolerate(nil = JsonErrorResponse(u'登录失败'))
  441. def advertiserLogin(request):
  442. # type:(WSGIRequest)->JsonResponse
  443. """
  444. 广告主登录
  445. :param request:
  446. :return:
  447. """
  448. payload = json.loads(request.body)
  449. username, password = payload['username'], payload['password']
  450. return advertiser_login(request, logger, username, password)
  451. @error_tolerate(nil = JsonErrorResponse(u'修改密码失败'))
  452. @permission_required(ROLE.advertiser)
  453. def changeAdvertiserPassword(request):
  454. # type: (WSGIRequest)->JsonResponse
  455. currentAdvertiser = request.user # type: cast(Advertiser)
  456. payload = json.loads(request.body)
  457. password = payload['password']
  458. if PASSWORD_RE.match(password) is None:
  459. return JsonErrorResponse(description=u'密码必须大于8位,只能是数字或字母或常见特殊符号')
  460. currentAdvertiser.set_password(payload['password'])
  461. return JsonResponse({'result': 1})
  462. @error_tolerate(nil = JsonErrorResponse(u'获取广告主信息失败'))
  463. @permission_required(ROLE.advertiser)
  464. def getAdvertiserInfo(request):
  465. # type:(WSGIRequest)->JsonResponse
  466. """
  467. 获取广告主信息
  468. :param request:
  469. :return:
  470. """
  471. user = request.user # type: cast(Advertiser)
  472. return JsonResponse(
  473. {
  474. 'result': 1,
  475. 'description': '',
  476. 'payload': {'nickname': user.nickname, 'brandName': '', 'quota': user.quota, 'role': ROLE.advertiser }
  477. }
  478. )
  479. ### others
  480. @error_tolerate(nil = JsonErrorResponse(u'生成报表失败'))
  481. @permission_required(ROLE.manager)
  482. def exportExcel(request):
  483. # type:(WSGIRequest)->JsonResponse
  484. """
  485. 广告效果报表生成excel
  486. :param request:
  487. :return:
  488. """
  489. manager = request.user # type: cast(Manager)
  490. def get_offline_task_name(task_type, user, **kwargs):
  491. # type: (basestring, UserSearchable, Dict)->basestring
  492. tmp_list = [task_type, user.username]
  493. if 'adId' in kwargs and kwargs['adId']:
  494. tmp_list.append(u'广告_%s' % (kwargs['adId'],))
  495. elif 'devNo' in kwargs:
  496. logicalCode = Device.get_logicalCode_by_devNo(kwargs['devNo'])
  497. tmp_list.append(u'设备号_%s' % (logicalCode,))
  498. elif 'groupId' in kwargs:
  499. address = Group.get_group(kwargs['groupId']).get('address', '')
  500. tmp_list.append(u'地址_%s' % (address,))
  501. elif 'dealerId' in kwargs:
  502. dealerName = Dealer.objects(id = str(kwargs['dealerId'])).get().nickname
  503. tmp_list.append(u'经销商_%s' % (dealerName,))
  504. elif 'agentId' in kwargs:
  505. agentName = Agent.objects(id = str(kwargs['agentId'])).get().nickname
  506. tmp_list.append(u'代理商_%s' % (agentName,))
  507. tmp_list.append(u'%s至%s' % (kwargs['startTime'], kwargs['endTime']))
  508. tmp_list.append(str(utils_datetime.generate_timestamp_ex()))
  509. return '-'.join(tmp_list).replace("/", "_")
  510. query = prepare_query(request.GET)
  511. offline_task_name = get_offline_task_name(task_type = OfflineTaskType.AD_REPORT,
  512. user = manager,
  513. **(query.raw))
  514. #: make queryAttrs serializable
  515. queryAttrs = query.attrs
  516. queryAttrs['dateTimeAdded__lte'] = dt_to_timestamp(queryAttrs['dateTimeAdded__lte'])
  517. queryAttrs['dateTimeAdded__gte'] = dt_to_timestamp(queryAttrs['dateTimeAdded__gte'])
  518. file_path, offline_task = OfflineTask.issue_export_report(offline_task_name = offline_task_name,
  519. process_func_name = 'generate_ad_excel_report',
  520. task_type = OfflineTaskType.AD_REPORT,
  521. userid = str(manager.id),
  522. role = ROLE.manager,
  523. paras = queryAttrs)
  524. task_caller(func_name = offline_task.process_func_name,
  525. offline_task_id = str(offline_task.id),
  526. filepath = file_path,
  527. queryAttrs = queryAttrs)
  528. return JsonResponse({
  529. 'result': 1,
  530. 'description': u"请前往离线任务查看任务处理情况",
  531. 'payload': str(offline_task.id)})
  532. def getDialogAd(request):
  533. # type:(WSGIRequest)->JsonResponse
  534. # TODO 支付前广告,待补充
  535. return JsonResponse({
  536. 'result': 1,
  537. 'description': '',
  538. 'payload': {'dataList': []}})
  539. def getBannerAd(request):
  540. # type:(WSGIRequest)->JsonResponse
  541. logicalCode = request.GET.get('logicalCode')
  542. dev = Device.get_dev_by_logicalCode(logicalCode)
  543. dealer = Dealer.objects(id=str(dev['ownerId'])).only('agentId').get() # type: Dealer
  544. agent = Agent.objects(id=str(dealer.agentId)).only('managerId').get() # type: Agent
  545. ad = Advertisement.filter_by_top_show(managerId=agent.managerId).first() # type: Optional[Advertisement]
  546. if not ad:
  547. return JsonOkResponse(payload={})
  548. else:
  549. return JsonOkResponse(payload={'link': ad.link, 'img': ad.img})
  550. @permission_required(ROLE.supermanager)
  551. @error_tolerate(nil = DefaultJsonErrorResponse)
  552. def getAdWords(request):
  553. # type:(WSGIRequest)->JsonResponse
  554. pageIndex = int(request.GET.get('pageIndex', 1))
  555. pageSize = int(request.GET.get('pageSize', 10))
  556. searchKey = request.GET.get('searchKey')
  557. startTime = request.GET.get('startTime')
  558. endTime = request.GET.get('endTime')
  559. query = {}
  560. if startTime is not None and endTime is not None:
  561. query['dateTimeAdded__lte'] = date_to_datetime_ceiling(endTime)
  562. query['dateTimeAdded__gte'] = date_to_datetime_floor(startTime)
  563. total = AdWord.objects(**query).search(searchKey).count()
  564. qs = AdWord.objects(**query).search(searchKey).paginate(pageIndex=pageIndex, pageSize=pageSize) # type: Iterable[AdWord]
  565. return JsonOkResponse(payload={'dataList': [ _.to_dict() for _ in qs ], 'total': total})
  566. @permission_required(ROLE.supermanager)
  567. @error_tolerate(nil = DefaultJsonErrorResponse)
  568. def addAdWord(request):
  569. # type:(WSGIRequest)->JsonResponse
  570. payload = parse_json_payload(request.body)
  571. AdWord(**payload).save()
  572. return JsonOkResponse()
  573. @permission_required(ROLE.supermanager)
  574. @error_tolerate(nil = DefaultJsonErrorResponse)
  575. def editAdword(request):
  576. # type:(WSGIRequest)->JsonResponse
  577. payload = parse_json_payload(request.body)
  578. ad_word = AdWord.objects(id=payload['id']).get()
  579. updated = ad_word.update(**payload)
  580. if updated:
  581. return JsonOkResponse(payload={'description': u'更新成功'})
  582. else:
  583. return JsonErrorResponse(payload={'description': u'更新失败'})
  584. @permission_required(ROLE.supermanager)
  585. @error_tolerate(nil = DefaultJsonErrorResponse)
  586. def deleteAdword(request):
  587. # type:(WSGIRequest)->JsonResponse
  588. payload = json.loads(request.body)
  589. ids = payload.get('ids')
  590. if not ids:
  591. return JsonErrorResponse(description=u'')
  592. AdWord.objects(id__in=ids).delete() # type: AdWord
  593. return JsonOkResponse()
  594. @error_tolerate(nil = DefaultJsonErrorResponse)
  595. def userGetAdword(request):
  596. # type:(WSGIRequest)->JsonResponse
  597. try:
  598. user = request.user # type: cast(MyUser)
  599. ad_word = AdWord.get_random_one() # type: AdWord
  600. if ad_word is not None:
  601. ad_word.incr_count(1)
  602. title = ad_word.title
  603. if not ad_word.autoPass:
  604. return MyJsonOkResponse({'result': 1, 'description': '', 'payload': {'content': ad_word.promotionUrl}})
  605. tbkCode = serviceCache.get('tbk_{}'.format(str(ad_word.id)))
  606. if not tbkCode:
  607. from library.aliopen.tbkapi import CreatTaokoulingReq
  608. request = CreatTaokoulingReq(appKey = '33175347', appSecret = 'c4e2d8426d1d08ec49ef00bc40ac94d5')
  609. request.setParams(url = ad_word.promotionUrl)
  610. rv = xmltodict.parse(request.getResponse().content)
  611. logger.debug('tbk code rv for {} is: {}'.format(str(ad_word.id), str(rv)))
  612. tokens = rv['tbk_tpwd_create_response']['data']['model'].split(' ')
  613. tbkCode = '{} {} {}'.format(tokens[0], tokens[1], title)
  614. logger.debug('new tbk code is: {}'.format(tbkCode))
  615. serviceCache.set('tbk_{}'.format(str(ad_word.id)), tbkCode, 60 * 60 * 2)
  616. return MyJsonOkResponse({'result': 1, 'description': '', 'payload': {'content': tbkCode}})
  617. else:
  618. logger.debug('cache tbk code is: {}'.format(tbkCode))
  619. return MyJsonOkResponse({'result': 1, 'description': '', 'payload': {'content': tbkCode}})
  620. except Exception as e:
  621. logger.error('exception = {}; id = {}'.format(traceback.format_exc(), str(ad_word.id)))
  622. return MyJsonOkResponse({'result': 1, 'description': '', 'payload': {'content': ''}})
  623. @error_tolerate(nil = DefaultJsonErrorResponse)
  624. def alipayCallback(request, uidType):
  625. # type:(WSGIRequest, str)->JsonResponse
  626. try:
  627. uidType = uidType.upper()
  628. payload = json.loads(request.body)
  629. if uidType == 'CPM':
  630. # uid = payload.get('uid')
  631. # bid = payload.get('bid')
  632. # try:
  633. # AdLog.objects.get(openId=uid, bidid=bid, status__ne='finished').update(status='finished', finishedData=request.body, finishedTime=datetime.datetime.now())
  634. # except:
  635. # logger.info(traceback.format_exc())
  636. pass
  637. elif uidType == 'RH':
  638. uid = payload.get('uid')
  639. urlId = payload.get('ext', {}).get('id')
  640. redpack = Redpack.take_effect(openId = uid, urlId = urlId, **{'RHCallback': request.body})
  641. if redpack:
  642. RedpackBuilder._set_alipay_key(uid, redpack.factoryCode, urlId, redpack.money.mongo_amount,
  643. showType = redpack.showType)
  644. return JsonResponse({"status": 1, "alipayOrderId": urlId})
  645. elif uidType == 'LX':
  646. pass
  647. try:
  648. AliAdLog.objects(campaignId = payload.get('campaignId'), bid = payload.get('bid')).upsert_one(
  649. uid = payload.get('uid'), uidType = payload.get('uidtype'), campaignId = payload.get('campaignId'),
  650. bid = payload.get('bid'), tradeTS = payload.get('tradetimestamp'), commission = payload.get('commission'))
  651. except Exception as e:
  652. logger.error(e)
  653. finally:
  654. return JsonResponse({'success': True, 'errorMsg': '', 'result': True})