示例#1
0
文件: promote.py 项目: j2p2/reddit
def refund_campaign(link, camp, billable_amount, billable_impressions):
    refund_amount = get_refund_amount(camp, billable_amount)
    if refund_amount <= 0:
        return

    owner = Account._byID(camp.owner_id, data=True)
    try:
        success = authorize.refund_transaction(owner, camp.trans_id, camp._id, refund_amount)
    except authorize.AuthorizeNetException as e:
        text = "%s $%s refund failed" % (camp, refund_amount)
        PromotionLog.add(link, text)
        g.log.debug(text + " (response: %s)" % e)
        return

    text = "%s completed with $%s billable (%s impressions @ $%s)." " %s refunded." % (
        camp,
        billable_amount,
        billable_impressions,
        camp.cpm,
        refund_amount,
    )
    PromotionLog.add(link, text)
    camp.refund_amount = refund_amount
    camp._commit()
    unset_underdelivered_campaigns(camp)
    emailer.refunded_promo(link)
示例#2
0
文件: promote.py 项目: Hmaal/reddit
def accept_promotion(link):
    """
    Accepting is campaign agnostic.  Accepting the ad just means that
    it is allowed to run if payment has been processed.

    If a campagn is able to run, this also requeues it.
    """
    PromotionLog.add(link, 'status update: accepted')
    # update the query queue

    set_promote_status(link, PROMOTE_STATUS.accepted)

    # campaigns that should be live now must be updated
    now = promo_datetime_now(0)
    if link._fullname in set(l.thing_name for l in
                             PromotionWeights.get_campaigns(now)):
        PromotionLog.add(link, 'Marked promotion for acceptance')
        charge_pending(0) # campaign must be charged before it will go live
        queue_changed_promo(link, "accepted")

    # campaigns that were charged and will go live in the future must be updated
    future_campaigns = [camp for camp in PromoCampaign._by_link(link._id)
                        if camp.start_date > now]
    transactions = get_transactions(link, future_campaigns)
    charged_campaigns = [camp for camp in future_campaigns
                         if (transactions.get(camp._id) and
                             transactions.get(camp._id).is_charged())]
    for campaign in charged_campaigns:
        hooks.get_hook('campaign.edit').call(link=link, campaign=campaign)

    if link._spam:
        link._spam = False
        link._commit()
    emailer.accept_promo(link)
示例#3
0
def edit_campaign(link, campaign, dates, bid, sr):
    sr_name = sr.name if sr else '' # empty string means target to all
    try:
        # if the bid amount changed, cancel any pending transactions
        if campaign.bid != bid:
            void_campaign(link, campaign)

        # update the schedule
        PromotionWeights.reschedule(link, campaign._id, sr_name,
                                    dates[0], dates[1], bid)

        # update values in the db
        campaign.update(dates[0], dates[1], bid, sr_name, campaign.trans_id, commit=True)

        # record the transaction
        text = 'updated campaign %s. (bid: %0.2f)' % (campaign._id, bid)
        PromotionLog.add(link, text)

        # make it a freebie, if applicable
        author = Account._byID(link.author_id, True)
        if getattr(author, "complimentary_promos", False):
            free_campaign(link, campaign, c.user)

    except Exception, e: # record error and rethrow 
        g.log.error("Failed to update PromoCampaign %s on link %d. Error was: %r" %
                    (campaign._id, link._id, e))
        try: # wrapped in try/except so orig error won't be lost if commit fails
            text = 'update FAILED. (campaign: %s, bid: %.2f)' % (campaign._id,
                                                                 bid)
            PromotionLog.add(link, text)
        except:
            pass
        raise e
示例#4
0
def refund_campaign(link, camp, refund_amount, billable_amount,
        billable_impressions):
    owner = Account._byID(camp.owner_id, data=True)
    success, reason = authorize.refund_transaction(
        owner, camp.trans_id, camp._id, refund_amount)
    if not success:
        text = ('%s $%s refund failed' % (camp, refund_amount))
        PromotionLog.add(link, text)
        g.log.debug(text + ' (reason: %s)' % reason)

        return False

    if billable_impressions:
        text = ('%s completed with $%s billable (%s impressions @ $%s).'
                ' %s refunded.' % (camp, billable_amount,
                                   billable_impressions,
                                   camp.bid_pennies / 100.,
                                   refund_amount))
    else:
        text = ('%s completed with $%s billable. %s refunded' % (camp,
            billable_amount, refund_amount))

    PromotionLog.add(link, text)
    camp.refund_amount = refund_amount
    camp._commit()
    queries.unset_underdelivered_campaigns(camp)
    emailer.refunded_promo(link)

    return True
示例#5
0
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)
示例#6
0
文件: promote.py 项目: AD42/reddit
def new_promotion(title, url, selftext, user, ip):
    """
    Creates a new promotion with the provided title, etc, and sets it
    status to be 'unpaid'.
    """
    sr = Subreddit._byID(get_promote_srid())
    l = Link._submit(title, url, user, sr, ip)
    l.promoted = True
    l.disable_comments = False
    PromotionLog.add(l, 'promotion created')

    if url == 'self':
        l.url = l.make_permalink_slow()
        l.is_self = True
        l.selftext = selftext

    l._commit()

    # set the status of the link, populating the query queue
    if c.user_is_sponsor or user.trusted_sponsor:
        set_promote_status(l, PROMOTE_STATUS.accepted)
    else:
        set_promote_status(l, PROMOTE_STATUS.unpaid)

    # the user has posted a promotion, so enable the promote menu unless
    # they have already opted out
    if user.pref_show_promote is not False:
        user.pref_show_promote = True
        user._commit()

    # notify of new promo
    emailer.new_promo(l)
    return l
示例#7
0
def charge_campaign(link, campaign):
    if charged_or_not_needed(campaign):
        return

    user = Account._byID(link.author_id)
    success, reason = authorize.charge_transaction(user, campaign.trans_id,
                                                   campaign._id)

    if not success:
        if reason == authorize.TRANSACTION_NOT_FOUND:
            # authorization hold has expired
            original_trans_id = campaign.trans_id
            campaign.trans_id = NO_TRANSACTION
            campaign._commit()
            text = ('voided expired transaction for %s: (trans_id: %d)'
                    % (campaign, original_trans_id))
            PromotionLog.add(link, text)
        return

    hooks.get_hook('promote.edit_campaign').call(link=link, campaign=campaign)

    if not is_promoted(link):
        update_promote_status(link, PROMOTE_STATUS.pending)

    emailer.queue_promo(link, campaign.bid, campaign.trans_id)
    text = ('auth charge for campaign %s, trans_id: %d' %
            (campaign._id, campaign.trans_id))
    PromotionLog.add(link, text)
示例#8
0
文件: promote.py 项目: pra85/reddit
def new_promotion(is_self, title, content, author, ip):
    """
    Creates a new promotion with the provided title, etc, and sets it
    status to be 'unpaid'.
    """
    sr = Subreddit._byID(Subreddit.get_promote_srid())
    l = Link._submit(
        is_self=is_self,
        title=title,
        content=content,
        author=author,
        sr=sr,
        ip=ip,
    )

    l.promoted = True
    l.disable_comments = False
    l.sendreplies = True
    PromotionLog.add(l, 'promotion created')

    update_promote_status(l, PROMOTE_STATUS.unpaid)

    # the user has posted a promotion, so enable the promote menu unless
    # they have already opted out
    if author.pref_show_promote is not False:
        author.pref_show_promote = True
        author._commit()

    # notify of new promo
    emailer.new_promo(l)
    return l
示例#9
0
def accept_promotion(link):
    """
    Accepting is campaign agnostic.  Accepting the ad just means that
    it is allowed to run if payment has been processed.

    If a campagn is able to run, this also requeues it.
    """
    # update the query queue
    set_promote_status(link, PROMOTE_STATUS.accepted)

    # campaigns that should be live now must be updated
    now = promo_datetime_now(0)
    promotion_weights = PromotionWeights.get_campaigns(now)
    live_campaigns = {pw.promo_idx for pw in promotion_weights if pw.thing_name == link._fullname}
    if live_campaigns:
        campaigns = PromoCampaign._byID(live_campaigns, data=True, return_dict=False)
        PromotionLog.add(link, "has live campaigns, forcing live")
        charge_pending(0)  # campaign must be charged before it will go live
        for campaign in campaigns:
            hooks.get_hook("campaign.edit").call(link=link, campaign=campaign)
        queue_changed_promo(link, "accepted")

    # campaigns that were charged and will go live in the future must be updated
    future_campaigns = [camp for camp in PromoCampaign._by_link(link._id) if camp.start_date > now]
    transactions = get_transactions(link, future_campaigns)
    charged_campaigns = [
        camp for camp in future_campaigns if (transactions.get(camp._id) and transactions.get(camp._id).is_charged())
    ]
    for campaign in charged_campaigns:
        hooks.get_hook("campaign.edit").call(link=link, campaign=campaign)

    if link._spam:
        link._spam = False
        link._commit()
    emailer.accept_promo(link)
示例#10
0
文件: promote.py 项目: j2p2/reddit
def new_promotion(title, url, selftext, user, ip):
    """
    Creates a new promotion with the provided title, etc, and sets it
    status to be 'unpaid'.
    """
    sr = Subreddit._byID(get_promote_srid())
    l = Link._submit(title, url, user, sr, ip)
    l.promoted = True
    l.disable_comments = False
    PromotionLog.add(l, "promotion created")

    if url == "self":
        l.url = l.make_permalink_slow()
        l.is_self = True
        l.selftext = selftext

    l._commit()

    update_promote_status(l, PROMOTE_STATUS.unpaid)

    # the user has posted a promotion, so enable the promote menu unless
    # they have already opted out
    if user.pref_show_promote is not False:
        user.pref_show_promote = True
        user._commit()

    # notify of new promo
    emailer.new_promo(l)
    return l
def _deactivate_campaign(link, campaign):
    with g.make_lock('adzerk_update', 'adzerk-' + link._fullname):
        g.log.debug('running deactivate_campaign %s' % link)
        az_flight = update_flight(link, campaign)
        az_flight.IsActive = False
        az_flight._send()
        PromotionLog.add(link, 'deactivated %s' % az_flight)
def update_campaign(link):
    """Add/update a reddit link as an Adzerk Campaign"""
    if hasattr(link, 'adzerk_campaign_id'):
        az_campaign = adzerk_api.Campaign.get(link.adzerk_campaign_id)
    else:
        az_campaign = None

    d = {
        'AdvertiserId': g.az_selfserve_advertiser_id,
        'IsDeleted': False,
        'IsActive': not link._deleted,
        'Price': 0,
    }

    log_text = None
    if az_campaign:
        changed = update_changed(az_campaign, **d)
    else:
        d.update({
            'Name': link._fullname,
            'Flights': [],
            'StartDate': date_to_adzerk(datetime.datetime.now(g.tz)),
        })
        az_campaign = adzerk_api.Campaign.create(**d)
        link.adzerk_campaign_id = az_campaign.Id
        link._commit()
        log_text = 'created %s' % az_campaign

    if log_text:
        PromotionLog.add(link, log_text)
        g.log.info(log_text)

    return az_campaign
def create_cfmap(link, campaign, az_campaign, az_creative, az_flight):
    """Create a CreativeFlightMap.

    Map the the reddit link (adzerk Creative) and reddit campaign (adzerk
    Flight).

    """

    if getattr(campaign, 'adzerk_cfmap_id', None) is not None:
        raise AttributeError('%s has existing adzerk_cfmap_id' % campaign)

    d = {
        'SizeOverride': False,
        'CampaignId': az_campaign.Id,
        'PublisherAccountId': g.az_selfserve_advertiser_id,
        'Percentage': 100,  # Each flight only has one creative (what about autobalanced)
        'DistributionType': 2, # 2: Percentage, 1: Auto-Balanced, 0: ???
        'Iframe': False,
        'Creative': {'Id': az_creative.Id},
        'FlightId': az_flight.Id,
        'Impressions': 100, # Percentage
        'IsDeleted': False,
        'IsActive': True,
    }

    az_cfmap = adzerk_api.CreativeFlightMap.create(az_flight.Id, **d)
    campaign.adzerk_cfmap_id = az_cfmap.Id
    campaign._commit()

    log_text = 'created %s' % az_cfmap
    PromotionLog.add(link, log_text)
    g.log.info(log_text)

    return az_cfmap
def _deactivate_overdelivered(link, campaign):
    with g.make_lock('adzerk_update', 'adzerk-' + link._fullname):
        msg = '%s deactivating adzerk flight for %s - %s'
        g.log.info(msg % (datetime.datetime.now(g.tz), link, campaign))

        az_flight = update_flight(link, campaign)
        PromotionLog.add(link, 'deactivated %s' % az_flight)
示例#15
0
文件: promote.py 项目: Lax100/reddit
def edit_campaign(link, campaign, dates, bid, cpm, sr, priority):
    sr_name = sr.name if sr else '' # empty string means target to all

    # if the bid amount changed, cancel any pending transactions
    if campaign.bid != bid:
        void_campaign(link, campaign)

    # update the schedule
    PromotionWeights.reschedule(link, campaign._id, sr_name,
                                dates[0], dates[1], bid)

    # update values in the db
    campaign.update(dates[0], dates[1], bid, cpm, sr_name,
                    campaign.trans_id, priority, commit=True)

    if campaign.priority.cpm:
        # record the transaction
        text = 'updated campaign %s. (bid: %0.2f)' % (campaign._id, bid)
        PromotionLog.add(link, text)

        # make it a freebie, if applicable
        author = Account._byID(link.author_id, True)
        if getattr(author, "complimentary_promos", False):
            free_campaign(link, campaign, c.user)

    hooks.get_hook('promote.edit_campaign').call(link=link, campaign=campaign)
示例#16
0
文件: promote.py 项目: AD42/reddit
def charge_pending(offset=1):
    for l, camp, weight in accepted_campaigns(offset=offset):
        user = Account._byID(l.author_id)
        try:
            if charged_or_not_needed(camp):
                continue

            charge_succeeded = authorize.charge_transaction(user, camp.trans_id,
                                                            camp._id)

            if not charge_succeeded:
                continue

            hooks.get_hook('promote.new_charge').call(link=l, campaign=camp)

            if is_promoted(l):
                emailer.queue_promo(l, camp.bid, camp.trans_id)
            else:
                set_promote_status(l, PROMOTE_STATUS.pending)
                emailer.queue_promo(l, camp.bid, camp.trans_id)
            text = ('auth charge for campaign %s, trans_id: %d' %
                    (camp._id, camp.trans_id))
            PromotionLog.add(l, text)
        except:
            print "Error on %s, campaign %s" % (l, camp._id)
def update_creative(link, az_advertiser):
    """Add/update a reddit link as an Adzerk Creative"""
    if getattr(link, 'external_creative_id', None) is not None:
        az_creative = adzerk_api.Creative.get(link.external_creative_id)
    else:
        az_creative = None

    title = link._fullname
    url = add_sr(link.url, sr_path=False) if link.is_self else link.url

    # protocols are case sensitive (lower) in adzerk.
    # can cause double protocols:
    # http://Http://www.example.com
    url = re.sub(r"^(https?)", lambda m: m.group(0).lower(), url, flags=re.I)

    # as long as there are no 3rd party trackers for the link
    # it's DNT compliant.
    DNT_compliant = (not (hasattr(link, 'third_party_tracking_url') or
        hasattr(link, 'third_party_tracking_url_2')))

    d = {
        'Body': title,
        'ScriptBody': render_link(link),
        'AdTypeId': LEADERBOARD_AD_TYPE,
        'Alt': '',
        'Url': url,
        'IsHTMLJS': True,
        'IsSync': False,
        'IsDeleted': False,
        'IsActive': not link._deleted,
        'IsNoTrack': DNT_compliant,
    }

    if az_creative:
        changed = update_changed(az_creative, **d)
        change_strs = make_change_strings(changed)
        if change_strs:
            log_text = 'updated %s: ' % az_creative + ', '.join(change_strs)
        else:
            log_text = None
    else:
        d.update({
            'AdvertiserId': az_advertiser.Id,
            'Title': title,
        })
        try:
            az_creative = adzerk_api.Creative.create(**d)
        except:
            raise ValueError(d)

        link.external_creative_id = az_creative.Id
        link._commit()
        log_text = 'created %s' % az_creative

    if log_text:
        PromotionLog.add(link, log_text)
        g.log.info(log_text)

    return az_creative
示例#18
0
文件: promote.py 项目: travmi/reddit
def toggle_pause_campaign(link, campaign, should_pause):
    campaign.paused = should_pause
    campaign._commit()

    action = "paused" if should_pause else "resumed"
    PromotionLog.add(link, "%s campaign %s" % (action, campaign._id))

    hooks.get_hook("promote.edit_campaign").call(link=link, campaign=campaign)
示例#19
0
def review_fraud(link, is_fraud):
    link.fraud = is_fraud
    link._commit()
    PromotionLog.add(link, "marked as fraud" if is_fraud else "resolved as not fraud")
    queries.unset_payment_flagged_link(link)

    if is_fraud:
        reject_promotion(link, "fraud")
        hooks.get_hook("promote.fraud_identified").call(link=link, sponsor=c.user)
示例#20
0
def flag_payment(link, reason):
    # already determined to be fraud.
    if link.payment_flagged_reason and link.fraud:
        return

    link.payment_flagged_reason = reason
    link._commit()
    PromotionLog.add(link, "payment flagged: %s" % reason)
    queries.set_payment_flagged_link(link)
def update_adzerk(link, campaign):
    az_campaign = update_campaign(link)
    az_creative = update_creative(link, campaign)
    az_flight = update_flight(link, campaign)
    az_cfmap = update_cfmap(link, campaign)
    text = ('%s/%s updated to %s, %s, %s, %s' % (link, campaign,
                                                 az_campaign, az_creative,
                                                 az_flight, az_cfmap))
    PromotionLog.add(link, text)
示例#22
0
文件: promote.py 项目: pra85/reddit
def toggle_pause_campaign(link, campaign, should_pause):
    campaign.paused = should_pause
    campaign._commit()

    action = 'paused' if should_pause else 'resumed'
    PromotionLog.add(link, '%s campaign %s' % (action, campaign._id))

    hooks.get_hook('promote.edit_campaign').call(link=link,
        campaign=campaign)
示例#23
0
def new_campaign(link, dates, bid, sr):
    # empty string for sr_name means target to all
    sr_name = sr.name if sr else ""
    campaign = PromoCampaign._new(link, sr_name, bid, dates[0], dates[1])
    PromotionWeights.add(link, campaign._id, sr_name, dates[0], dates[1], bid)
    PromotionLog.add(link, 'campaign %s created' % campaign._id)
    author = Account._byID(link.author_id, True)
    if getattr(author, "complimentary_promos", False):
        free_campaign(link, campaign, c.user)
    return campaign
示例#24
0
文件: promote.py 项目: j2p2/reddit
def void_campaign(link, campaign):
    transactions = get_transactions(link, [campaign])
    bid_record = transactions.get(campaign._id)
    if bid_record:
        a = Account._byID(link.author_id)
        authorize.void_transaction(a, bid_record.transaction, campaign._id)
        campaign.trans_id = NO_TRANSACTION
        campaign._commit()
        text = "voided transaction for %s: (trans_id: %d)" % (campaign, bid_record.transaction)
        PromotionLog.add(link, text)
def update_cfmap(link, campaign, triggered_by):
    """Create a CreativeFlightMap.

    Map the the reddit link (adzerk Creative) and reddit campaign (adzerk
    Flight).

    """

    if getattr(campaign, 'external_cfmap_id', None) is not None:
        return

    d = {
        'SizeOverride': False,
        'CampaignId': link.external_campaign_id,
        'Percentage': 100,  # Each flight only has one creative (what about autobalanced)
        'DistributionType': 2, # 2: Percentage, 1: Auto-Balanced, 0: ???
        'Iframe': False,
        'Creative': {'Id': link.external_creative_id},
        'FlightId': campaign.external_flight_id,
        'Impressions': 100, # Percentage
        'IsDeleted': False,
        'IsActive': True,
    }

    request_error = None
    try:
        az_cfmap = adzerk_api.CreativeFlightMap.create(campaign.external_flight_id, **d)
    except adzerk_api.AdzerkError as e:
        request_error = e
    finally:
        g.ad_events.adzerk_api_request(
            request_type="create_cfmap",
            thing=campaign,
            request_body=d,
            triggered_by=triggered_by,
            request_error=request_error,
            additional_data=dict(
                link_fullname=link._fullname,
                link_id=link._id,
            )
        )

        if request_error:
            raise request_error


    campaign.external_cfmap_id = az_cfmap.Id
    campaign._commit()

    log_text = 'created %s' % az_cfmap
    PromotionLog.add(link, log_text)
    g.log.info(log_text)

    return az_cfmap
示例#26
0
文件: promote.py 项目: rizha/reddit
def edit_campaign(link, campaign, dates, bid, cpm, target, priority, location,
                  platform='desktop', mobile_os=None):
    changed = {}
    if bid != campaign.bid:
         # if the bid amount changed, cancel any pending transactions
        void_campaign(link, campaign, reason='changed_bid')
        changed['bid'] = ("$%0.2f" % campaign.bid, "$%0.2f" % bid)
        hooks.get_hook('promote.edit_bid').call(
            link=link,campaign=campaign, previous=campaign.bid, current=bid)
        campaign.bid = bid
    if dates[0] != campaign.start_date or dates[1] != campaign.end_date:
        original = '%s to %s' % (campaign.start_date, campaign.end_date)
        edited = '%s to %s' % (dates[0], dates[1])
        changed['dates'] = (original, edited)
        campaign.start_date = dates[0]
        campaign.end_date = dates[1]
    if cpm != campaign.cpm:
        changed['cpm'] = (campaign.cpm, cpm)
        campaign.cpm = cpm
    if target != campaign.target:
        changed['target'] = (campaign.target, target)
        campaign.target = target
    if priority != campaign.priority:
        changed['priority'] = (campaign.priority.name, priority.name)
        campaign.priority = priority
    if location != campaign.location:
        changed['location'] = (campaign.location, location)
        campaign.location = location
    if platform != campaign.platform:
        changed["platform"] = (campaign.platform, platform)
        campaign.platform = platform
    if mobile_os != campaign.mobile_os:
        changed["mobile_os"] = (campaign.mobile_os, mobile_os)
        campaign.mobile_os = mobile_os

    change_strs = map(lambda t: '%s: %s -> %s' % (t[0], t[1][0], t[1][1]),
                      changed.iteritems())
    change_text = ', '.join(change_strs)
    campaign._commit()

    # update the index
    PromotionWeights.reschedule(link, campaign)

    if campaign.priority.cpm:
        # make it a freebie, if applicable
        author = Account._byID(link.author_id, True)
        if getattr(author, "complimentary_promos", False):
            free_campaign(link, campaign, c.user)

    # record the changes
    if change_text:
        PromotionLog.add(link, 'edited %s: %s' % (campaign, change_text))

    hooks.get_hook('promote.edit_campaign').call(link=link, campaign=campaign)
def update_flight(link, campaign):
    """Add/update a reddit campaign as an Adzerk Flight"""
    if hasattr(campaign, 'adzerk_flight_id'):
        az_flight = adzerk_api.Flight.get(campaign.adzerk_flight_id)
    else:
        az_flight = None

    az_campaign = adzerk_api.Campaign.get(link.adzerk_campaign_id)

    d = {
        'StartDate': date_to_adzerk(campaign.start_date),
        'EndDate': date_to_adzerk(campaign.end_date),
        'OptionType': 1, # 1: CPM, 2: Remainder
        'IsUnlimited': False,
        'IsFullSpeed': False,
        'Keywords': srname_to_keyword(campaign.sr_name),
        'CampaignId': az_campaign.Id,
        'PriorityId': g.az_selfserve_priority_id, # TODO: property of PromoCampaign
        'IsDeleted': False,
        'IsActive': not campaign._deleted,
        'IsFreqCap': None,
    }

    is_cpm = hasattr(campaign, 'cpm')
    if is_cpm:
        d.update({
            'Price': campaign.cpm / 100.,   # convert from cents to dollars
            'Impressions': campaign.impressions + ADZERK_IMPRESSION_BUMP,
            'GoalType': 1, # 1: Impressions
            'RateType': 2, # 2: CPM
        })
    else:
        d.update({
            'Price': campaign.bid,
            'Impressions': int(campaign.bid / campaign.ndays),
            'GoalType': 2, # 2: Percentage
            'RateType': 1, # 1: Flat
        })

    log_text = None
    if az_flight:
        changed = update_changed(az_flight, **d)
    else:
        d.update({'Name': campaign._fullname})
        az_flight = adzerk_api.Flight.create(**d)
        campaign.adzerk_flight_id = az_flight.Id
        campaign._commit()
        log_text = 'created %s' % az_flight

    if log_text:
        PromotionLog.add(link, log_text)
        g.log.info(log_text)

    return az_flight
示例#28
0
def new_campaign(link, dates, bid, cpm, target, priority, location, platform, mobile_os):
    campaign = PromoCampaign.create(link, target, bid, cpm, dates[0], dates[1], priority, location, platform, mobile_os)
    PromotionWeights.add(link, campaign)
    PromotionLog.add(link, "campaign %s created" % campaign._id)

    if campaign.priority.cpm:
        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
示例#29
0
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)
示例#30
0
def reject_promotion(link, reason=None):
    was_live = is_promoted(link)
    update_promote_status(link, PROMOTE_STATUS.rejected)
    if reason:
        PromotionLog.add(link, "rejected: %s" % reason)

    # Send a rejection email (unless the advertiser requested the reject)
    if not c.user or c.user._id != link.author_id:
        emailer.reject_promo(link, reason=reason)

    if was_live:
        all_live_promo_srnames(_update=True)
示例#31
0
def reject_promotion(link, reason=None):
    was_live = is_promoted(link)
    update_promote_status(link, PROMOTE_STATUS.rejected)
    if reason:
        PromotionLog.add(link, "rejected: %s" % reason)

    # Send a rejection email (unless the advertiser requested the reject)
    if not c.user or c.user._id != link.author_id:
        emailer.reject_promo(link, reason=reason)

    if was_live:
        all_live_promo_srnames(_update=True)
示例#32
0
def edit_campaign(link, campaign, dates, bid, cpm, sr, priority, location):
    sr_name = sr.name if sr else ''  # empty string means target to all

    changed = {}
    if bid != campaign.bid:
        changed['bid'] = ("$%0.2f" % campaign.bid, "$%0.2f" % bid)
    if dates[0] != campaign.start_date or dates[1] != campaign.end_date:
        original = '%s to %s' % (campaign.start_date, campaign.end_date)
        edited = '%s to %s' % (dates[0], dates[1])
        changed['dates'] = (original, edited)
    if cpm != campaign.cpm:
        changed['cpm'] = (campaign.cpm, cpm)
    if sr_name != campaign.sr_name:
        format_sr_name = (lambda sr_name: '/r/%s' % sr_name
                          if sr_name else '<frontpage>')
        changed['sr_name'] = map(format_sr_name, (campaign.sr_name, sr_name))
    if priority != campaign.priority:
        changed['priority'] = (campaign.priority.name, priority.name)

    change_strs = map(lambda t: '%s: %s -> %s' % (t[0], t[1][0], t[1][1]),
                      changed.iteritems())
    change_text = ', '.join(change_strs)

    # if the bid amount changed, cancel any pending transactions
    if campaign.bid != bid:
        void_campaign(link, campaign, reason='changed_bid')

    # update the schedule
    PromotionWeights.reschedule(link, campaign._id, sr_name, dates[0],
                                dates[1], bid)

    # update values in the db
    campaign.update(dates[0],
                    dates[1],
                    bid,
                    cpm,
                    sr_name,
                    campaign.trans_id,
                    priority,
                    location,
                    commit=True)

    if campaign.priority.cpm:
        # make it a freebie, if applicable
        author = Account._byID(link.author_id, True)
        if getattr(author, "complimentary_promos", False):
            free_campaign(link, campaign, c.user)

    # record the changes
    if change_text:
        PromotionLog.add(link, 'edited %s: %s' % (campaign, change_text))

    hooks.get_hook('promote.edit_campaign').call(link=link, campaign=campaign)
示例#33
0
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)
示例#34
0
def new_campaign(link, dates, bid, cpm, target, priority, location):
    campaign = PromoCampaign.create(link, target, bid, cpm, dates[0], dates[1],
                                    priority, location)
    PromotionWeights.add(link, campaign)
    PromotionLog.add(link, 'campaign %s created' % campaign._id)

    if campaign.priority.cpm:
        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
示例#35
0
def flag_payment(link, reason):
    # already determined to be fraud or already flagged for that reason.
    if link.fraud or reason in link.payment_flagged_reason:
        return

    if link.payment_flagged_reason:
        link.payment_flagged_reason += (", %s" % reason)
    else:
        link.payment_flagged_reason = reason

    link._commit()
    PromotionLog.add(link, "payment flagged: %s" % reason)
    queries.set_payment_flagged_link(link)
示例#36
0
 def _run(msgs, chan):
     items = [json.loads(msg.body) for msg in msgs]
     if QUEUE_ALL in items:
         # QUEUE_ALL is just an indicator to run make_daily_promotions.
         # There's no promotion log to update in this case.
         print "Received %s QUEUE_ALL message(s)" % items.count(QUEUE_ALL)
         items = [i for i in items if i != QUEUE_ALL]
     make_daily_promotions()
     links = Link._by_fullname([i["link"] for i in items])
     for item in items:
         PromotionLog.add(links[item['link']],
                          "Finished remaking current promotions (this link "
                          "was: %(message)s" % item)
示例#37
0
def edit_campaign(link, campaign, dates, bid, cpm, target, priority, location):
    changed = {}
    if bid != campaign.bid:
        # if the bid amount changed, cancel any pending transactions
        void_campaign(link, campaign, reason='changed_bid')
        changed['bid'] = ("$%0.2f" % campaign.bid, "$%0.2f" % bid)
        hooks.get_hook('promote.edit_bid').call(link=link,
                                                campaign=campaign,
                                                previous=campaign.bid,
                                                current=bid)
        campaign.bid = bid
    if dates[0] != campaign.start_date or dates[1] != campaign.end_date:
        original = '%s to %s' % (campaign.start_date, campaign.end_date)
        edited = '%s to %s' % (dates[0], dates[1])
        changed['dates'] = (original, edited)
        campaign.start_date = dates[0]
        campaign.end_date = dates[1]
    if cpm != campaign.cpm:
        changed['cpm'] = (campaign.cpm, cpm)
        campaign.cpm = cpm
    if target != campaign.target:
        changed['target'] = (campaign.target, target)
        campaign.target = target
    if priority != campaign.priority:
        changed['priority'] = (campaign.priority.name, priority.name)
        campaign.priority = priority
    if location != campaign.location:
        changed['location'] = (campaign.location, location)
        campaign.location = location

    change_strs = map(lambda t: '%s: %s -> %s' % (t[0], t[1][0], t[1][1]),
                      changed.iteritems())
    change_text = ', '.join(change_strs)
    campaign._commit()

    # update the index
    PromotionWeights.reschedule(link, campaign)

    if campaign.priority.cpm:
        # make it a freebie, if applicable
        author = Account._byID(link.author_id, True)
        if getattr(author, "complimentary_promos", False):
            free_campaign(link, campaign, c.user)

    # record the changes
    if change_text:
        PromotionLog.add(link, 'edited %s: %s' % (campaign, change_text))

    hooks.get_hook('promote.edit_campaign').call(link=link, campaign=campaign)
示例#38
0
def new_campaign(link, dates, bid, cpm, sr, priority, location):
    # empty string for sr_name means target to all
    sr_name = sr.name if sr else ""
    campaign = PromoCampaign._new(link, sr_name, bid, cpm, dates[0], dates[1],
                                  priority, location)
    PromotionWeights.add(link, campaign._id, sr_name, dates[0], dates[1], bid)
    PromotionLog.add(link, 'campaign %s created' % campaign._id)

    if campaign.priority.cpm:
        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
示例#39
0
def void_campaign(link, campaign, reason):
    transactions = get_transactions(link, [campaign])
    bid_record = transactions.get(campaign._id)
    if bid_record:
        a = Account._byID(link.author_id)
        authorize.void_transaction(a, bid_record.transaction, campaign._id)
        campaign.trans_id = NO_TRANSACTION
        campaign._commit()
        text = ('voided transaction for %s: (trans_id: %d)' %
                (campaign, bid_record.transaction))
        PromotionLog.add(link, text)

        if bid_record.transaction > 0:
            # notify the user that the transaction was voided if it was not
            # a freebie
            emailer.void_payment(link, campaign, reason)
def update_cfmap(link, campaign):
    """Add/update a CreativeFlightMap.
    
    Map the the reddit link (adzerk Creative) and reddit campaign (adzerk
    Flight).

    """

    az_campaign = adzerk_api.Campaign.get(link.adzerk_campaign_id)
    az_creative = adzerk_api.Creative.get(campaign.adzerk_creative_id)
    az_flight = adzerk_api.Flight.get(campaign.adzerk_flight_id)

    if hasattr(campaign, 'adzerk_cfmap_id'):
        az_cfmap = adzerk_api.CreativeFlightMap.get(az_flight.Id,
                                                    campaign.adzerk_cfmap_id)
    else:
        az_cfmap = None

    d = {
        'SizeOverride': False,
        'CampaignId': az_campaign.Id,
        'PublisherAccountId': g.az_selfserve_advertiser_id,
        'Percentage': 100,  # Each flight only has one creative (what about autobalanced)
        'DistributionType': 2, # 2: Percentage, 1: Auto-Balanced, 0: ???
        'Iframe': False,
        'Creative': {'Id': az_creative.Id},
        'FlightId': az_flight.Id,
        'Impressions': 100, # Percentage
        'IsDeleted': False,
        'IsActive': not campaign._deleted,
    }


    log_text = None
    if az_cfmap:
        changed = update_changed(az_cfmap, **d)
    else:
        az_cfmap = adzerk_api.CreativeFlightMap.create(az_flight.Id, **d)
        campaign.adzerk_cfmap_id = az_cfmap.Id
        campaign._commit()
        log_text = 'created %s' % az_cfmap

    if log_text:
        PromotionLog.add(link, log_text)
        g.log.info(log_text)

    return az_cfmap
示例#41
0
def reject_promotion(link, reason=None):
    PromotionLog.add(link, 'status update: rejected')
    # update the query queue
    # Since status is updated first,
    # if make_daily_promotions happens to run
    # while we're doing work here, it will correctly exclude it
    set_promote_status(link, PROMOTE_STATUS.rejected)

    all_ads = get_live_promotions([LiveAdWeights.ALL_ADS])
    links = set(x.link for x in all_ads[LiveAdWeights.ALL_ADS])
    if link._fullname in links:
        PromotionLog.add(link, 'Marked promotion for rejection')
        queue_changed_promo(link, "rejected")

    # Send a rejection email (unless the advertiser requested the reject)
    if not c.user or c.user._id != link.author_id:
        emailer.reject_promo(link, reason=reason)
示例#42
0
def accept_promotion(link):
    """
    Accepting is campaign agnostic.  Accepting the ad just means that
    it is allowed to run if payment has been processed.

    If a campagn is able to run, this also requeues it.
    """
    # update the query queue
    set_promote_status(link, PROMOTE_STATUS.accepted)

    # campaigns that should be live now must be updated
    now = promo_datetime_now(0)
    promotion_weights = PromotionWeights.get_campaigns(now)
    live_campaigns = {
        pw.promo_idx
        for pw in promotion_weights if pw.thing_name == link._fullname
    }
    if live_campaigns:
        campaigns = PromoCampaign._byID(live_campaigns,
                                        data=True,
                                        return_dict=False)
        PromotionLog.add(link, 'has live campaigns, forcing live')
        charge_pending(0)  # campaign must be charged before it will go live
        for campaign in campaigns:
            hooks.get_hook('campaign.edit').call(link=link, campaign=campaign)
        queue_changed_promo(link, "accepted")

    # campaigns that were charged and will go live in the future must be updated
    future_campaigns = [
        camp for camp in PromoCampaign._by_link(link._id)
        if camp.start_date > now
    ]
    transactions = get_transactions(link, future_campaigns)
    charged_campaigns = [
        camp for camp in future_campaigns
        if (transactions.get(camp._id)
            and transactions.get(camp._id).is_charged())
    ]
    for campaign in charged_campaigns:
        hooks.get_hook('campaign.edit').call(link=link, campaign=campaign)

    if link._spam:
        link._spam = False
        link._commit()
    emailer.accept_promo(link)
示例#43
0
def charge_pending(offset=1):
    for l, camp, weight in accepted_campaigns(offset=offset):
        user = Account._byID(l.author_id)
        try:
            if (authorize.is_charged_transaction(camp.trans_id, camp._id) or not
                authorize.charge_transaction(user, camp.trans_id, camp._id)):
                continue

            if is_promoted(l):
                emailer.queue_promo(l, camp.bid, camp.trans_id)
            else:
                set_promote_status(l, PROMOTE_STATUS.pending)
                emailer.queue_promo(l, camp.bid, camp.trans_id)
            text = ('auth charge for campaign %s, trans_id: %d' %
                    (camp._id, camp.trans_id))
            PromotionLog.add(l, text)
        except:
            print "Error on %s, campaign %s" % (l, camp._id)
示例#44
0
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
示例#45
0
def _update_adzerk(link, campaign):
    with g.make_lock('adzerk_update', 'adzerk-' + link._fullname):
        msg = '%s updating/creating adzerk objects for %s - %s'
        g.log.info(msg % (datetime.datetime.now(g.tz), link, campaign))
        az_campaign = update_campaign(link)

        if campaign:
            az_creative = update_creative(link, campaign)
            az_flight = update_flight(link, campaign, az_campaign)
            if getattr(campaign, 'adzerk_cfmap_id', None) is not None:
                az_cfmap = adzerk_api.CreativeFlightMap.get(
                    az_flight.Id, campaign.adzerk_cfmap_id)
            else:
                az_cfmap = create_cfmap(link, campaign, az_campaign,
                                        az_creative, az_flight)
            PromotionLog.add(link, 'updated %s' % az_flight)
        else:
            PromotionLog.add(link, 'updated %s' % az_campaign)
示例#46
0
def update_creative(link, campaign):
    """Add/update a reddit link/campaign as an Adzerk Creative"""
    if getattr(campaign, 'adzerk_creative_id', None) is not None:
        az_creative = adzerk_api.Creative.get(campaign.adzerk_creative_id)
    else:
        az_creative = None

    title = '-'.join((link._fullname, campaign._fullname))
    d = {
        'Body': title,
        'ScriptBody': render_link(link, campaign),
        'AdvertiserId': g.az_selfserve_advertiser_id,
        'AdTypeId': g.az_selfserve_ad_type,
        'Alt': '',
        'Url': '',
        'IsHTMLJS': True,
        'IsSync': False,
        'IsDeleted': False,
        'IsActive': not campaign._deleted,
    }

    if az_creative:
        changed = update_changed(az_creative, **d)
        change_strs = make_change_strings(changed)
        if change_strs:
            log_text = 'updated %s: ' % az_creative + ', '.join(change_strs)
        else:
            log_text = None
    else:
        d.update({'Title': title})
        try:
            az_creative = adzerk_api.Creative.create(**d)
        except:
            raise ValueError(d)

        campaign.adzerk_creative_id = az_creative.Id
        campaign._commit()
        log_text = 'created %s' % az_creative

    if log_text:
        PromotionLog.add(link, log_text)
        g.log.info(log_text)

    return az_creative
示例#47
0
def edit_campaign(link, campaign, dates, bid, cpm, sr, priority):
    sr_name = sr.name if sr else ''  # empty string means target to all
    try:
        # if the bid amount changed, cancel any pending transactions
        if campaign.bid != bid:
            void_campaign(link, campaign)

        # update the schedule
        PromotionWeights.reschedule(link, campaign._id, sr_name, dates[0],
                                    dates[1], bid)

        # update values in the db
        campaign.update(dates[0],
                        dates[1],
                        bid,
                        cpm,
                        sr_name,
                        campaign.trans_id,
                        priority,
                        commit=True)

        if campaign.priority.cpm:
            # record the transaction
            text = 'updated campaign %s. (bid: %0.2f)' % (campaign._id, bid)
            PromotionLog.add(link, text)

            # make it a freebie, if applicable
            author = Account._byID(link.author_id, True)
            if getattr(author, "complimentary_promos", False):
                free_campaign(link, campaign, c.user)

        hooks.get_hook('campaign.edit').call(link=link, campaign=campaign)

    except Exception, e:  # record error and rethrow
        g.log.error(
            "Failed to update PromoCampaign %s on link %d. Error was: %r" %
            (campaign._id, link._id, e))
        try:  # wrapped in try/except so orig error won't be lost if commit fails
            text = 'update FAILED. (campaign: %s, bid: %.2f)' % (campaign._id,
                                                                 bid)
            PromotionLog.add(link, text)
        except:
            pass
        raise e
示例#48
0
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]
    sr = Subreddit._by_name(campaign.sr_name) if campaign.sr_name else None

    # NOTE: this will delete PromotionWeights after and including now.date()
    edit_campaign(link, campaign, dates, campaign.bid, campaign.cpm, sr,
                  campaign.priority, campaign.location)

    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)
示例#49
0
def charge_campaign(link, campaign):
    if charged_or_not_needed(campaign):
        return

    user = Account._byID(link.author_id)
    charge_succeeded = authorize.charge_transaction(user, campaign.trans_id,
                                                    campaign._id)

    if not charge_succeeded:
        return

    hooks.get_hook('promote.edit_campaign').call(link=link, campaign=campaign)

    if not is_promoted(link):
        update_promote_status(link, PROMOTE_STATUS.pending)

    emailer.queue_promo(link, campaign.bid, campaign.trans_id)
    text = ('auth charge for campaign %s, trans_id: %d' %
            (campaign._id, campaign.trans_id))
    PromotionLog.add(link, text)
示例#50
0
def update_campaign(link, az_advertiser=None):
    """Add/update a reddit link as an Adzerk Campaign"""
    if getattr(link, 'external_campaign_id', None) is not None:
        az_campaign = adzerk_api.Campaign.get(link.external_campaign_id)
    else:
        az_campaign = None

    d = {
        'SalespersonId': g.az_selfserve_salesperson_id,
        'IsDeleted': False,  # deleting an adzerk object will make it
        # unretrievable, so just set it inactive
        'IsActive': promote.is_accepted(link) and not link._deleted,
        'Price': 0,
    }

    if az_advertiser:
        d["AdvertiserId"] = az_advertiser.Id

    if az_campaign:
        changed = update_changed(az_campaign, **d)
        change_strs = make_change_strings(changed)
        if change_strs:
            log_text = 'updated %s: ' % az_campaign + ', '.join(change_strs)
        else:
            log_text = None
    else:
        d.update({
            'Name': link._fullname,
            'Flights': [],
            'StartDate': date_to_adzerk(datetime.datetime.now(g.tz)),
        })
        az_campaign = adzerk_api.Campaign.create(**d)
        link.external_campaign_id = az_campaign.Id
        link._commit()
        log_text = 'created %s' % az_campaign

    if log_text:
        PromotionLog.add(link, log_text)
        g.log.info(log_text)

    return az_campaign
示例#51
0
def accept_promotion(link):
    """
    Accepting is campaign agnostic.  Accepting the ad just means that
    it is allowed to run if payment has been processed.

    If a campagn is able to run, this also requeues it.
    """
    PromotionLog.add(link, 'status update: accepted')
    # update the query queue

    set_promote_status(link, PROMOTE_STATUS.accepted)
    now = promo_datetime_now(0)
    if link._fullname in set(l.thing_name
                             for l in PromotionWeights.get_campaigns(now)):
        PromotionLog.add(link, 'Marked promotion for acceptance')
        charge_pending(0)  # campaign must be charged before it will go live
        queue_changed_promo(link, "accepted")
    if link._spam:
        link._spam = False
        link._commit()
    emailer.accept_promo(link)
示例#52
0
def refund_campaign(link, camp, billable_amount):
    refund_amount = camp.bid - billable_amount
    owner = Account._byID(camp.owner_id, data=True)
    try:
        success = authorize.refund_transaction(user, camp.trans_id, camp._id,
                                               refund_amount)
    except authorize.AuthorizeNetException as e:
        text = ('%s $%s refund failed' % (camp, refund_amount))
        PromotionLog.add(link, text)
        g.log.debug(text + ' (response: %s)' % e)
        return

    text = (
        '%s completed with $%s billable (%s impressions @ $%s).'
        ' %s refunded.' %
        (camp, billable_amount, billable_impressions, camp.cpm, refund_amount))
    PromotionLog.add(link, text)
    camp.refund_amount = refund_amount
    camp._commit()
    unset_underdelivered_campaigns(camp)
    emailer.refunded_promo(link)
示例#53
0
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, campaign, dates, campaign.bid, campaign.cpm,
                  campaign.target, campaign.priority, campaign.location)

    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)
示例#54
0
def refund_campaign(link, camp, billable_amount, billable_impressions):
    refund_amount = get_refund_amount(camp, billable_amount)
    if refund_amount <= 0:
        return

    owner = Account._byID(camp.owner_id, data=True)
    success, reason = authorize.refund_transaction(owner, camp.trans_id,
                                                   camp._id, refund_amount)
    if not success:
        text = ('%s $%s refund failed' % (camp, refund_amount))
        PromotionLog.add(link, text)
        g.log.debug(text + ' (reason: %s)' % reason)
        return

    text = (
        '%s completed with $%s billable (%s impressions @ $%s).'
        ' %s refunded.' %
        (camp, billable_amount, billable_impressions, camp.cpm, refund_amount))
    PromotionLog.add(link, text)
    camp.refund_amount = refund_amount
    camp._commit()
    queries.unset_underdelivered_campaigns(camp)
    emailer.refunded_promo(link)
示例#55
0
def create_cfmap(link, campaign, az_campaign, az_creative, az_flight):
    """Create a CreativeFlightMap.

    Map the the reddit link (adzerk Creative) and reddit campaign (adzerk
    Flight).

    """

    if getattr(campaign, 'adzerk_cfmap_id', None) is not None:
        raise AttributeError('%s has existing adzerk_cfmap_id' % campaign)

    d = {
        'SizeOverride': False,
        'CampaignId': az_campaign.Id,
        'PublisherAccountId': g.az_selfserve_advertiser_id,
        'Percentage':
        100,  # Each flight only has one creative (what about autobalanced)
        'DistributionType': 2,  # 2: Percentage, 1: Auto-Balanced, 0: ???
        'Iframe': False,
        'Creative': {
            'Id': az_creative.Id
        },
        'FlightId': az_flight.Id,
        'Impressions': 100,  # Percentage
        'IsDeleted': False,
        'IsActive': True,
    }

    az_cfmap = adzerk_api.CreativeFlightMap.create(az_flight.Id, **d)
    campaign.adzerk_cfmap_id = az_cfmap.Id
    campaign._commit()

    log_text = 'created %s' % az_cfmap
    PromotionLog.add(link, log_text)
    g.log.info(log_text)

    return az_cfmap
示例#56
0
def auth_campaign(link, campaign, user, pay_id=None, freebie=False):
    """
    Authorizes (but doesn't charge) a budget with authorize.net.
    Args:
    - link: promoted link
    - campaign: campaign to be authorized
    - user: Account obj of the user doing the auth (usually the currently
        logged in user)
    - pay_id: customer payment profile id to use for this transaction. (One
        user can have more than one payment profile if, for instance, they have
        more than one credit card on file.) Set pay_id to -1 for freebies.

    Returns: (True, "") if successful or (False, error_msg) if not. 
    """
    void_campaign(link, campaign, reason='changed_payment')

    if freebie:
        trans_id, reason = authorize.auth_freebie_transaction(
            campaign.total_budget_dollars, user, link, campaign._id)
    else:
        trans_id, reason = authorize.auth_transaction(
            campaign.total_budget_dollars, user, pay_id, link, campaign._id)

    if trans_id and not reason:
        text = ('updated payment and/or budget for campaign %s: '
                'SUCCESS (trans_id: %d, amt: %0.2f)' %
                (campaign._id, trans_id, campaign.total_budget_dollars))
        PromotionLog.add(link, text)
        if trans_id < 0:
            PromotionLog.add(link, 'FREEBIE (campaign: %s)' % campaign._id)

        if trans_id:
            if is_finished(link):
                # When a finished promo gets a new paid campaign it doesn't
                # need to go through approval again and is marked accepted
                new_status = PROMOTE_STATUS.accepted
            else:
                new_status = max(PROMOTE_STATUS.unseen, link.promote_status)
        else:
            new_status = max(PROMOTE_STATUS.unpaid, link.promote_status)
        update_promote_status(link, new_status)

        if user and (user._id == link.author_id) and trans_id > 0:
            emailer.promo_total_budget(link, campaign.total_budget_dollars,
                                       campaign.start_date)

    else:
        text = (
            "updated payment and/or budget for campaign %s: FAILED ('%s')" %
            (campaign._id, reason))
        PromotionLog.add(link, text)
        trans_id = 0

    campaign.trans_id = trans_id
    campaign._commit()

    return bool(trans_id), reason
示例#57
0
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 and freebies
        PromoCampaign.c.trans_id > 0,
        data=True)
    campaigns = list(q)

    # check that traffic is up to date
    earliest_campaign = min(campaigns, key=lambda camp: camp.start_date)
    start, end = promote.get_total_run(earliest_campaign)
    missing_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 link in links], data=True)

    for camp in campaigns:
        if hasattr(camp, 'refund_amount'):
            continue

        link = links[camp.link_id]
        billable_impressions = promote.get_billable_impressions(camp)
        billable_amount = promote.get_billable_amount(camp,
                                                      billable_impressions)

        if billable_amount >= camp.bid:
            text = ('%s completed with $%s billable (%s impressions @ $%s).' %
                    (camp, billable_amount, billable_impressions, camp.cpm))
            PromotionLog.add(link, text)
            refund_amount = 0.
        else:
            refund_amount = camp.bid - billable_amount
            user = Account._byID(link.author_id, data=True)
            try:
                success = authorize.refund_transaction(user, camp.trans_id,
                                                       camp._id, refund_amount)
            except authorize.AuthorizeNetException as e:
                text = ('%s $%s refund failed' % (camp, refund_amount))
                PromotionLog.add(link, text)
                g.log.debug(text + ' (response: %s)' % e)
                continue
            text = ('%s completed with $%s billable (%s impressions @ $%s).'
                    ' %s refunded.' %
                    (camp, billable_amount, billable_impressions, camp.cpm,
                     refund_amount))
            PromotionLog.add(link, text)

        camp.refund_amount = refund_amount
        camp._commit()
示例#58
0
def auth_campaign(link, campaign, user, pay_id):
    """
    Authorizes (but doesn't charge) a bid with authorize.net.
    Args:
    - link: promoted link
    - campaign: campaign to be authorized
    - user: Account obj of the user doing the auth (usually the currently
        logged in user)
    - pay_id: customer payment profile id to use for this transaction. (One
        user can have more than one payment profile if, for instance, they have
        more than one credit card on file.) Set pay_id to -1 for freebies.

    Returns: (True, "") if successful or (False, error_msg) if not. 
    """
    void_campaign(link, campaign)
    test = 1 if g.debug else None
    trans_id, reason = authorize.auth_transaction(campaign.bid,
                                                  user,
                                                  pay_id,
                                                  link,
                                                  campaign._id,
                                                  test=test)

    if trans_id and not reason:
        text = ('updated payment and/or bid for campaign %s: '
                'SUCCESS (trans_id: %d, amt: %0.2f)' %
                (campaign._id, trans_id, campaign.bid))
        PromotionLog.add(link, text)
        if trans_id < 0:
            PromotionLog.add(link, 'FREEBIE (campaign: %s)' % campaign._id)

        if trans_id:
            new_status = max(PROMOTE_STATUS.unseen, link.promote_status)
        else:
            new_status = max(PROMOTE_STATUS.unpaid, link.promote_status)
        set_promote_status(link, new_status)
        # notify of campaign creation
        # update the query queue
        if user and (user._id == link.author_id) and trans_id > 0:
            emailer.promo_bid(link, campaign.bid, campaign.start_date)

    else:
        # something bad happend.
        text = ("updated payment and/or bid for campaign %s: FAILED ('%s')" %
                (campaign._id, reason))
        PromotionLog.add(link, text)
        trans_id = 0

    campaign.trans_id = trans_id
    campaign._commit()

    return bool(trans_id), reason
示例#59
0
def unapprove_promotion(link):
    PromotionLog.add(link, 'status update: unapproved')
    # update the query queue
    set_promote_status(link, PROMOTE_STATUS.unseen)
示例#60
0
def delete_campaign(link, campaign):
    PromotionWeights.delete_unfinished(link, campaign._id)
    void_campaign(link, campaign)
    campaign.delete()
    PromotionLog.add(link, 'deleted campaign %s' % campaign._id)