def accept_promotion(link): was_edited_live = is_edited_live(link) update_promote_status(link, PROMOTE_STATUS.accepted) if link._spam: link._spam = False link._commit() if not was_edited_live: emailer.accept_promo(link) # if the link has campaigns running now charge them and promote the link now = promo_datetime_now() campaigns = list(PromoCampaign._by_link(link._id)) is_live = False for camp in campaigns: if is_accepted_promo(now, link, camp): # if link was edited live, do not check against Authorize.net if not was_edited_live: charge_campaign(link, camp) if charged_or_not_needed(camp): promote_link(link, camp) is_live = True if is_live: all_live_promo_srnames(_update=True)
def terminate_campaign(link, campaign): if not is_live_promo(link, campaign): return now = promo_datetime_now() original_end = campaign.end_date dates = [campaign.start_date, now] # NOTE: this will delete PromotionWeights after and including now.date() edit_campaign( link=link, campaign=campaign, dates=dates, target=campaign.target, frequency_cap=campaign.frequency_cap, priority=campaign.priority, location=campaign.location, total_budget_pennies=campaign.total_budget_pennies, cost_basis=campaign.cost_basis, bid_pennies=campaign.bid_pennies, ) campaigns = list(PromoCampaign._by_link(link._id)) is_live = any( is_live_promo(link, camp) for camp in campaigns if camp._id != campaign._id) if not is_live: update_promote_status(link, PROMOTE_STATUS.finished) all_live_promo_srnames(_update=True) msg = 'terminated campaign %s (original end %s)' % (campaign._id, original_end.date()) PromotionLog.add(link, msg)
def get_promos(date, sr_names=None, link=None): campaign_ids = PromotionWeights.get_campaign_ids(date, sr_names=sr_names, link=link) campaigns = PromoCampaign._byID(campaign_ids, data=True, return_dict=False) link_ids = {camp.link_id for camp in campaigns} links = Link._byID(link_ids, data=True) for camp in campaigns: yield camp, links[camp.link_id]
def update_served(items): for item in items: if not item.promoted: continue campaign = PromoCampaign._by_fullname(item.campaign) if not campaign.has_served: campaign.has_served = True campaign._commit()
def finalize_completed_campaigns(daysago=1): # PromoCampaign.end_date is utc datetime with year, month, day only now = datetime.datetime.now(g.tz) date = now - datetime.timedelta(days=daysago) date = date.replace(hour=0, minute=0, second=0, microsecond=0) q = PromoCampaign._query( PromoCampaign.c.end_date == date, # exclude no transaction PromoCampaign.c.trans_id != NO_TRANSACTION, data=True) # filter out freebies campaigns = filter(lambda camp: camp.trans_id > NO_TRANSACTION, q) if not campaigns: return # check that traffic is up to date earliest_campaign = min(campaigns, key=lambda camp: camp.start_date) start, end = get_total_run(earliest_campaign) missing_traffic = traffic.get_missing_traffic(start.replace(tzinfo=None), date.replace(tzinfo=None)) if missing_traffic: raise ValueError("Can't finalize campaigns finished on %s." "Missing traffic from %s" % (date, missing_traffic)) links = Link._byID([camp.link_id for camp in campaigns], data=True) underdelivered_campaigns = [] for camp in campaigns: if hasattr(camp, 'refund_amount'): continue link = links[camp.link_id] billable_impressions = get_billable_impressions(camp) billable_amount = get_billable_amount(camp, billable_impressions) if billable_amount >= camp.total_budget_pennies: if hasattr(camp, 'cpm'): text = '%s completed with $%s billable (%s impressions @ $%s).' text %= (camp, billable_amount, billable_impressions, camp.bid_dollars) else: text = '%s completed with $%s billable (pre-CPM).' text %= (camp, billable_amount) PromotionLog.add(link, text) camp.refund_amount = 0. camp._commit() elif charged_or_not_needed(camp): underdelivered_campaigns.append(camp) if underdelivered_campaigns: queries.set_underdelivered_campaigns(underdelivered_campaigns)
def new_campaign(link, dates, target, frequency_cap, priority, location, platform, mobile_os, ios_devices, ios_version_range, android_devices, android_version_range, total_budget_pennies, cost_basis, bid_pennies): campaign = PromoCampaign.create( link, target, dates[0], dates[1], frequency_cap, priority, location, platform, mobile_os, ios_devices, ios_version_range, android_devices, android_version_range, total_budget_pennies, cost_basis, bid_pennies) PromotionWeights.add(link, campaign) PromotionLog.add(link, 'campaign %s created' % campaign._id) if not campaign.is_house: author = Account._byID(link.author_id, data=True) if getattr(author, "complimentary_promos", False): free_campaign(link, campaign, c.user) hooks.get_hook('promote.new_campaign').call(link=link, campaign=campaign) return campaign
def get_campaigns_by_date(srs, start, end, ignore=None): srs = tup(srs) sr_names = [sr.name for sr in srs] campaign_ids = PromotionWeights.get_campaign_ids( start, end=end, sr_names=sr_names) if ignore: campaign_ids.discard(ignore._id) campaigns = PromoCampaign._byID(campaign_ids, data=True, return_dict=False) # filter out deleted campaigns that didn't have their PromotionWeights # deleted campaigns = filter(lambda camp: not camp._deleted, campaigns) transaction_ids = {camp.trans_id for camp in campaigns if camp.trans_id != NO_TRANSACTION} if transaction_ids: transactions = Bid.query().filter(Bid.transaction.in_(transaction_ids)) # index transactions by transaction and campaign id because freebies # reuse the same transaction id (they always use -link id) transaction_by_id = { (bid.transaction, bid.campaign): bid for bid in transactions} else: transaction_by_id = {} dates = set(get_date_range(start, end)) ret = {date: set() for date in dates} for camp in campaigns: if camp.trans_id == NO_TRANSACTION: continue if camp.impressions <= 0: # pre-CPM campaign continue transaction = transaction_by_id[(camp.trans_id, camp._id)] if not (transaction.is_auth() or transaction.is_charged()): continue camp_dates = set(get_date_range(camp.start_date, camp.end_date)) for date in camp_dates.intersection(dates): ret[date].add(camp) return ret
def get_scheduled(date, sr_name=''): campaign_ids = PromotionWeights.get_campaign_ids(date, sr_names=[sr_name]) campaigns = PromoCampaign._byID(campaign_ids, return_dict=False, data=True) links = Link._by_fullname({camp.link_id for camp in campaigns}, return_dict=False, data=True) links = {l._id: l for l in links} kept = [] for camp in campaigns: if camp.trans_id == 0: continue link = links[camp.link_id] if link._spam or not promote.is_accepted(link): continue kept.append(camp._id) return [(camp._fullname, camp.link_id, camp.total_budget_dollars) for camp in kept]
def filter_campaigns(date, fullnames): campaigns = PromoCampaign._by_fullname(fullnames, data=True, return_dict=False) # filter out campaigns that shouldn't be live pc_date = datetime.datetime(date.year, date.month, date.day, 0, 0, tzinfo=g.tz) campaigns = [ camp for camp in campaigns if camp.start_date <= pc_date <= camp.end_date ] # check for links with targeted campaigns - we can't handle them now has_targeted = [camp.link_id for camp in campaigns if camp.sr_name != ''] return [camp for camp in campaigns if camp.link_id not in has_targeted]
def get_total_run(thing): """Return the total time span this link or campaign will run. Starts at the start date of the earliest campaign and goes to the end date of the latest campaign. """ campaigns = [] if isinstance(thing, Link): campaigns = PromoCampaign._by_link(thing._id) elif isinstance(thing, PromoCampaign): campaigns = [thing] else: campaigns = [] earliest = None latest = None for campaign in campaigns: if not charged_or_not_needed(campaign): continue if not earliest or campaign.start_date < earliest: earliest = campaign.start_date if not latest or campaign.end_date > latest: latest = campaign.end_date # a manually launched promo (e.g., sr discovery) might not have campaigns. if not earliest or not latest: latest = datetime.datetime.utcnow() earliest = latest - datetime.timedelta(days=30) # last month # ugh this stuff is a mess. they're stored as "UTC" but actually mean UTC-5. earliest = earliest.replace(tzinfo=g.tz) - timezone_offset latest = latest.replace(tzinfo=g.tz) - timezone_offset return earliest, latest
def make_campaign_table(self): campaigns = PromoCampaign._by_link(self.thing._id) total_budget_dollars = 0. total_spent = 0 total_paid_impressions = 0 total_impressions = 0 total_clicks = 0 self.campaign_table = [] for camp in campaigns: if not is_launched_campaign(camp): continue is_live = camp.is_live_now() self.has_early_campaign |= is_early_campaign(camp) self.has_live_campaign |= is_live history = get_billable_traffic(camp) impressions, clicks = 0, 0 for date, (imp, click) in history: impressions += imp clicks += click start = to_date(camp.start_date).strftime('%Y-%m-%d') end = to_date(camp.end_date).strftime('%Y-%m-%d') target = camp.target.pretty_name location = camp.location_str spent = promote.get_spent_amount(camp) is_active = self.campaign and self.campaign._id36 == camp._id36 url = '/traffic/%s/%s' % (self.thing._id36, camp._id36) is_total = False campaign_budget_dollars = camp.total_budget_dollars row = self.make_campaign_table_row(camp._id36, start=start, end=end, target=target, location=location, budget_dollars=campaign_budget_dollars, spent=spent, paid_impressions=camp.impressions, impressions=impressions, clicks=clicks, is_live=is_live, is_active=is_active, url=url, is_total=is_total) self.campaign_table.append(row) total_budget_dollars += campaign_budget_dollars total_spent += spent total_paid_impressions += camp.impressions total_impressions += impressions total_clicks += clicks # total row start = '---' end = '---' target = '---' location = '---' is_live = False is_active = not self.campaign url = '/traffic/%s' % self.thing._id36 is_total = True row = self.make_campaign_table_row(_('total'), start=start, end=end, target=target, location=location, budget_dollars=total_budget_dollars, spent=total_spent, paid_impressions=total_paid_impressions, impressions=total_impressions, clicks=total_clicks, is_live=is_live, is_active=is_active, url=url, is_total=is_total) self.campaign_table.append(row)