Add priorities to PromoCampaign.

This commit is contained in:
Brian Simpson
2013-10-11 08:42:05 -04:00
parent 261bc5825c
commit ff1eb45b6a
8 changed files with 356 additions and 109 deletions

View File

@@ -78,6 +78,7 @@ from r2.lib.validator import (
VLink,
VModhash,
VOneOf,
VPriority,
VPromoCampaign,
VRatelimit,
VSelfText,
@@ -129,6 +130,9 @@ def _check_dates(dates):
def campaign_has_oversold_error(form, campaign):
if campaign.priority.inventory_override:
return
target = Subreddit._by_name(campaign.sr_name) if campaign.sr_name else None
return has_oversold_error(form, campaign._id, campaign.start_date,
campaign.end_date, campaign.bid, campaign.cpm,
@@ -184,6 +188,17 @@ class PromoteController(ListingController):
sr_names = sorted([sr.name for sr in srs], key=lambda s: s.lower())
return sr_names
@classmethod
@memoize('house_campaigns', time=60)
def get_house_campaigns(cls):
now = promote.promo_datetime_now()
pws = PromotionWeights.get_campaigns(now)
campaign_ids = {pw.promo_idx for pw in pws}
campaigns = PromoCampaign._byID(campaign_ids, data=True,
return_dict=False)
campaigns = [camp for camp in campaigns if not camp.priority.cpm]
return campaigns
@property
def menus(self):
filters = [
@@ -240,6 +255,10 @@ class PromoteController(ListingController):
return [Link._fullname_from_id36(to36(id)) for id in link_ids]
elif self.sort == 'reported':
return queries.get_reported_links(get_promote_srid())
elif self.sort == 'house':
campaigns = self.get_house_campaigns()
link_ids = {camp.link_id for camp in campaigns}
return [Link._fullname_from_id36(to36(id)) for id in link_ids]
return queries.get_all_promoted_links()
else:
if self.sort == "future_promos":
@@ -558,9 +577,10 @@ class PromoteController(ListingController):
coerce=False, error=errors.BAD_BID),
sr=VSubmitSR('sr', promotion=True),
campaign_id36=nop("campaign_id36"),
targeting=VLength("targeting", 10))
targeting=VLength("targeting", 10),
priority=VPriority("priority"))
def POST_edit_campaign(self, form, jquery, link, campaign_id36,
dates, bid, sr, targeting):
dates, bid, sr, targeting, priority):
if not link:
return
@@ -601,29 +621,34 @@ class PromoteController(ListingController):
form.has_errors('title', errors.TOO_MANY_CAMPAIGNS)
return
if form.has_errors('bid', errors.BAD_BID):
return
campaign = None
if campaign_id36:
# you cannot edit the bid of a live ad unless it's a freebie
try:
campaign = PromoCampaign._byID36(campaign_id36)
if (bid != campaign.bid and
campaign.start_date < datetime.now(g.tz) and
not campaign.is_freebie()):
c.errors.add(errors.BID_LIVE, field='bid')
form.has_errors('bid', errors.BID_LIVE)
return
except NotFound:
pass
min_bid = 0 if c.user_is_sponsor else g.min_promote_bid
if bid is None or bid < min_bid:
c.errors.add(errors.BAD_BID, field='bid',
msg_params={'min': min_bid,
'max': g.max_promote_bid})
form.has_errors('bid', errors.BAD_BID)
return
if priority.cpm:
if form.has_errors('bid', errors.BAD_BID):
return
# you cannot edit the bid of a live ad unless it's a freebie
if (campaign and bid != campaign.bid and
campaign.start_date < datetime.now(g.tz) and
not campaign.is_freebie()):
c.errors.add(errors.BID_LIVE, field='bid')
form.has_errors('bid', errors.BID_LIVE)
return
min_bid = 0 if c.user_is_sponsor else g.min_promote_bid
if bid is None or bid < min_bid:
c.errors.add(errors.BAD_BID, field='bid',
msg_params={'min': min_bid,
'max': g.max_promote_bid})
form.has_errors('bid', errors.BAD_BID)
return
else:
bid = 0. # Set bid to 0 as dummy value
if targeting == 'one':
if form.has_errors('sr', errors.SUBREDDIT_NOEXIST,
@@ -646,23 +671,24 @@ class PromoteController(ListingController):
sr = None
# Check inventory
campaign_id = campaign._id if campaign_id36 else None
if has_oversold_error(form, campaign_id, start, end, bid, cpm, sr):
campaign_id = campaign._id if campaign else None
if (not priority.inventory_override and
has_oversold_error(form, campaign_id, start, end, bid, cpm, sr)):
return
if campaign_id36 is not None:
campaign = PromoCampaign._byID36(campaign_id36)
promote.edit_campaign(link, campaign, dates, bid, cpm, sr)
if campaign:
promote.edit_campaign(link, campaign, dates, bid, cpm, sr, priority)
r = promote.get_renderable_campaigns(link, campaign)
jquery.update_campaign(r.campaign_id36, r.start_date, r.end_date,
r.duration, r.bid, r.spent, r.cpm,
r.sr, r.status)
r.duration, r.bid, r.spent, r.cpm, r.sr,
r.priority_name, r.inventory_override,
r.status)
else:
campaign = promote.new_campaign(link, dates, bid, cpm, sr)
campaign = promote.new_campaign(link, dates, bid, cpm, sr, priority)
r = promote.get_renderable_campaigns(link, campaign)
jquery.new_campaign(r.campaign_id36, r.start_date, r.end_date,
r.duration, r.bid, r.spent, r.cpm,
r.sr, r.status)
r.duration, r.bid, r.spent, r.cpm, r.sr,
r.priority_name, r.inventory_override, r.status)
@validatedForm(VSponsor('link_id'),
VModhash(),

View File

@@ -37,7 +37,12 @@ from r2.models.gold import (
days_to_pennies,
gold_revenue_on,
)
from r2.models.promo import NO_TRANSACTION, PromotionLog, PromotedLinkRoadblock
from r2.models.promo import (
NO_TRANSACTION,
PROMOTE_PRIORITIES,
PromotedLinkRoadblock,
PromotionLog,
)
from r2.models.token import OAuth2Client, OAuth2AccessToken
from r2.models import traffic
from r2.models import ModAction
@@ -3425,6 +3430,7 @@ class PromotePage(Reddit):
dest='/admin/graph'))
buttons.append(NavButton('report', 'report'))
buttons.append(NavButton('underdelivered', 'underdelivered'))
buttons.append(NavButton('house ads', 'house'))
buttons.append(NavButton('reported links', 'reported'))
menu = NavMenu(buttons, base_path = '/promoted',
@@ -3510,6 +3516,9 @@ class PromoteLinkForm(Templated):
self.min_bid = 0 if c.user_is_sponsor else g.min_promote_bid
self.priorities = [(p.name, p.text, p.description, p.default, p.inventory_override, p.cpm)
for p in sorted(PROMOTE_PRIORITIES.values(), key=lambda p: p.value)]
# preload some inventory
srnames = set()
for title, names in self.subreddit_selector.subreddit_names:

View File

@@ -186,15 +186,23 @@ def campaign_is_live(link, campaign_index):
class RenderableCampaign():
def __init__(self, campaign_id36, start_date, end_date, duration, bid,
spent, cpm, sr, status):
spent, cpm, sr, priority, status):
self.campaign_id36 = campaign_id36
self.start_date = start_date
self.end_date = end_date
self.duration = duration
self.bid = "%.2f" % bid
self.spent = "%.2f" % spent
if priority.cpm:
self.bid = "%.2f" % bid
self.spent = "%.2f" % spent
else:
self.bid = "N/A"
self.spent = "N/A"
self.cpm = cpm
self.sr = sr
self.priority_name = priority.name
self.inventory_override = priority.inventory_override
self.status = status
@classmethod
@@ -228,7 +236,8 @@ class RenderableCampaign():
'pay_url': pay_url(link, camp),
'view_live_url': view_live_url(link, sr),
'sponsor': user_is_sponsor,
'live': live}
'live': live,
'non_cpm': not camp.priority.cpm}
if transaction and transaction.is_void():
status['paid'] = False
@@ -240,7 +249,7 @@ class RenderableCampaign():
status['refund_url'] = refund_url(link, camp)
rc = cls(campaign_id36, start_date, end_date, duration, bid, spent,
cpm, sr, status)
cpm, sr, camp.priority, status)
r.append(rc)
return r
@@ -353,22 +362,28 @@ def get_transactions(link, campaigns):
bids_by_campaign = {c._id: bid_dict[(c._id, c.trans_id)] for c in campaigns}
return bids_by_campaign
def new_campaign(link, dates, bid, cpm, sr):
def new_campaign(link, dates, bid, cpm, sr, priority):
# 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])
campaign = PromoCampaign._new(link, sr_name, bid, cpm, dates[0], dates[1],
priority)
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, data=True)
if getattr(author, "complimentary_promos", False):
free_campaign(link, campaign, c.user)
if campaign.priority.cpm:
author = Account._byID(link.author_id, data=True)
if getattr(author, "complimentary_promos", False):
free_campaign(link, campaign, c.user)
else:
# non-cpm campaigns are never charged, so we need to fire the hook now
hooks.get_hook('promote.new_charge').call(link=link, campaign=campaign)
return campaign
def free_campaign(link, campaign, user):
auth_campaign(link, campaign, user, -1)
def edit_campaign(link, campaign, dates, bid, cpm, sr):
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
@@ -381,16 +396,17 @@ def edit_campaign(link, campaign, dates, bid, cpm, sr):
# update values in the db
campaign.update(dates[0], dates[1], bid, cpm, sr_name,
campaign.trans_id, commit=True)
campaign.trans_id, priority, commit=True)
# record the transaction
text = 'updated campaign %s. (bid: %0.2f)' % (campaign._id, bid)
PromotionLog.add(link, text)
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)
# 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)
@@ -572,7 +588,7 @@ def accepted_campaigns(offset=0):
campaigns = dict((camp._id, camp) for camp in campaign_query)
for pw in promo_weights:
campaign = campaigns.get(pw.promo_idx)
if not campaign or not campaign.trans_id:
if not campaign or (not campaign.trans_id and campaign.priority.cpm):
continue
link = accepted_links.get(campaign.link_id)
if not link:
@@ -580,6 +596,14 @@ def accepted_campaigns(offset=0):
yield (link, campaign, pw.weight)
def charged_or_not_needed(campaign):
# True if a campaign has a charged transaction or doesn't need one
charged = authorize.is_charged_transaction(campaign.trans_id, campaign._id)
needs_charge = campaign.priority.cpm
return charged or not needs_charge
def get_scheduled(offset=0):
"""
Arguments:
@@ -596,7 +620,7 @@ def get_scheduled(offset=0):
error_campaigns = []
for l, campaign, weight in accepted_campaigns(offset=offset):
try:
if authorize.is_charged_transaction(campaign.trans_id, campaign._id):
if charged_or_not_needed(campaign):
adweight = AdWeight(l._fullname, weight, campaign._fullname)
adweights.append(adweight)
except Exception, e: # could happen if campaign things have corrupt data
@@ -614,8 +638,7 @@ 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):
# already charged
if charged_or_not_needed(camp):
continue
charge_succeeded = authorize.charge_transaction(user, camp.trans_id,
@@ -641,7 +664,7 @@ def charge_pending(offset=1):
def scheduled_campaigns_by_link(l, date=None):
# A promotion/campaign is scheduled/live if it's in
# PromotionWeights.get_campaigns(now) and
# authorize.is_charged_transaction()
# charged_or_not_needed
date = date or promo_datetime_now()
@@ -656,7 +679,7 @@ def scheduled_campaigns_by_link(l, date=None):
for campaign_id in campaigns:
try:
campaign = PromoCampaign._byID(campaign_id, data=True)
if authorize.is_charged_transaction(campaign.trans_id, campaign_id):
if charged_or_not_needed(campaign):
accepted.append(campaign_id)
except NotFound:
g.log.error("PromoCampaign %d scheduled to run on %s not found." %

View File

@@ -1683,6 +1683,15 @@ class VOneOf(Validator):
for s in self.options),
}
class VPriority(Validator):
def run(self, val):
if c.user_is_sponsor:
return PROMOTE_PRIORITIES.get(val, PROMOTE_DEFAULT_PRIORITY)
else:
return PROMOTE_DEFAULT_PRIORITY
class VImageType(Validator):
def run(self, img_type):
if not img_type in ('png', 'jpg'):

View File

@@ -27,6 +27,7 @@ import json
from pycassa.types import CompositeType
from pylons import g, c
from pylons.i18n import _, N_
from r2.lib import filters
from r2.lib.cache import sgm
@@ -40,6 +41,60 @@ from r2.models.subreddit import Subreddit
PROMOTE_STATUS = Enum("unpaid", "unseen", "accepted", "rejected",
"pending", "promoted", "finished")
class PriorityLevel(object):
name = ''
_text = N_('')
_description = N_('')
value = 1 # Values are from 1 (highest) to 100 (lowest)
default = False
inventory_override = False
cpm = True # Non-cpm is percentage, will fill unsold impressions
def __repr__(self):
return "<PriorityLevel %s: %s>" % (self.name, self.value)
@property
def text(self):
return _(self._text) if self._text else ''
@property
def description(self):
return _(self._description) if self._description else ''
class HighPriority(PriorityLevel):
name = 'high'
_text = N_('highest')
value = 5
class MediumPriority(PriorityLevel):
name = 'standard'
_text = N_('standard')
value = 10
default = True
class RemnantPriority(PriorityLevel):
name = 'remnant'
_text = N_('remnant')
_description = N_('lower priority, impressions are not guaranteed')
value = 20
inventory_override = True
class HousePriority(PriorityLevel):
name = 'house'
_text = N_('house')
_description = N_('non-CPM, displays in all unsold impressions')
value = 30
inventory_override = True
cpm = False
HIGH, MEDIUM, REMNANT, HOUSE = HighPriority(), MediumPriority(), RemnantPriority(), HousePriority()
PROMOTE_PRIORITIES = {p.name: p for p in (HIGH, MEDIUM, REMNANT, HOUSE)}
PROMOTE_DEFAULT_PRIORITY = MEDIUM
@memoize("get_promote_srid")
def get_promote_srid(name = 'promos'):
@@ -64,6 +119,10 @@ def calc_impressions(bid, cpm_pennies):
NO_TRANSACTION = 0
class PromoCampaign(Thing):
_defaults = dict(
priority_name=PROMOTE_DEFAULT_PRIORITY.name,
)
def __getattr__(self, attr):
val = Thing.__getattr__(self, attr)
if attr in ('start_date', 'end_date'):
@@ -72,8 +131,14 @@ class PromoCampaign(Thing):
val = val.replace(tzinfo=g.tz)
return val
@classmethod
def get_priority_name(cls, priority):
if not priority in PROMOTE_PRIORITIES.values():
raise ValueError("%s is not a valid priority" % val)
return priority.name
@classmethod
def _new(cls, link, sr_name, bid, cpm, start_date, end_date):
def _new(cls, link, sr_name, bid, cpm, start_date, end_date, priority):
pc = PromoCampaign(link_id=link._id,
sr_name=sr_name,
bid=bid,
@@ -81,7 +146,8 @@ class PromoCampaign(Thing):
start_date=start_date,
end_date=end_date,
trans_id=NO_TRANSACTION,
owner_id=link.author_id)
owner_id=link.author_id,
priority_name=cls.get_priority_name(priority))
pc._commit()
return pc
@@ -111,8 +177,14 @@ class PromoCampaign(Thing):
# deal with pre-CPM PromoCampaigns
if not hasattr(self, 'cpm'):
return -1
elif not self.priority.cpm:
return -1
return calc_impressions(self.bid, self.cpm)
@property
def priority(self):
return PROMOTE_PRIORITIES[self.priority_name]
def is_freebie(self):
return self.trans_id < 0
@@ -121,13 +193,14 @@ class PromoCampaign(Thing):
return self.start_date < now and self.end_date > now
def update(self, start_date, end_date, bid, cpm, sr_name, trans_id,
commit=True):
priority, commit=True):
self.start_date = start_date
self.end_date = end_date
self.bid = bid
self.cpm = cpm
self.sr_name = sr_name
self.trans_id = trans_id
self.priority_name = self.get_priority_name(priority)
if commit:
self._commit()

View File

@@ -4521,6 +4521,7 @@ ul.tabmenu.formtab {
font-size: small;
padding: 4px;
padding-top: 8px;
width: 90px;
}
.linefield .campaign input[type=text] {
font-size: x-small;

View File

@@ -1,9 +1,9 @@
r.sponsored = {
init: function() {
init: function(isSponsor) {
$("#sr-autocomplete").on("sr-changed blur", function() {
r.sponsored.fill_campaign_editor()
})
this.isSponsor = isSponsor
this.inventory = {}
},
@@ -64,7 +64,7 @@ r.sponsored = {
}
},
get_booked_inventory: function($form, srname) {
get_booked_inventory: function($form, srname, isOverride) {
var campaign_id36 = $form.find('input[name="campaign_id36"]').val(),
campaign_row = $('.existing-campaigns .campaign-row input[name="campaign_id36"]')
.filter('*[value="' + campaign_id36 + '"]')
@@ -83,6 +83,11 @@ r.sponsored = {
return {}
}
var existingOverride = campaign_row.find('*[name="override"]').val()
if (isOverride != existingOverride) {
return {}
}
var startdate = campaign_row.find('*[name="startdate"]').val(),
enddate = campaign_row.find('*[name="enddate"]').val(),
dates = this.get_dates(startdate, enddate),
@@ -101,7 +106,7 @@ r.sponsored = {
},
check_inventory: function($form) {
check_inventory: function($form, isOverride) {
var bid = this.get_bid($form),
cpm = this.get_cpm($form),
requested = this.calc_impressions(bid, cpm),
@@ -113,7 +118,7 @@ r.sponsored = {
target = $form.find('*[name="sr"]').val(),
srname = targeted ? target : '',
dates = r.sponsored.get_dates(startdate, enddate),
booked = this.get_booked_inventory($form, srname)
booked = this.get_booked_inventory($form, srname, isOverride)
// bail out in state where targeting is selected but srname
// has not been entered yet
@@ -124,32 +129,56 @@ r.sponsored = {
$.when(r.sponsored.get_check_inventory(srname, dates)).done(
function() {
var minDaily = _.min(_.map(dates, function(date) {
var datestr = $.datepicker.formatDate('mm/dd/yy', date),
daily_booked = booked[datestr] || 0
return r.sponsored.inventory[srname][datestr] + daily_booked
}))
if (isOverride) {
// do a simple sum of available inventory for override
var available = _.reduce(_.map(dates, function(date){
var datestr = $.datepicker.formatDate('mm/dd/yy', date),
daily_booked = booked[datestr] || 0
return r.sponsored.inventory[srname][datestr] + daily_booked
}), function(memo, num){ return memo + num; }, 0)
} else {
// calculate conservative inventory estimate
var minDaily = _.min(_.map(dates, function(date) {
var datestr = $.datepicker.formatDate('mm/dd/yy', date),
daily_booked = booked[datestr] || 0
return r.sponsored.inventory[srname][datestr] + daily_booked
}))
var available = minDaily * ndays
}
var available = minDaily * ndays,
maxbid = r.sponsored.calc_bid(available, cpm)
var maxbid = r.sponsored.calc_bid(available, cpm)
if (available < requested) {
var message = r._("We have insufficient inventory to fulfill" +
" your requested budget, target, and dates." +
" Only %(available)s impressions available" +
" on %(target)s from %(start)s to %(end)s. " +
"Maximum budget is $%(max)s."
).format({
available: r.utils.prettyNumber(available),
target: targeted ? srname : 'the frontpage',
start: startdate,
end: enddate,
max: maxbid
})
if (isOverride) {
var message = r._("We expect to only have %(available)s " +
"impressions on %(target)s from %(start)s " +
"to %(end)s. We may not fully deliver."
).format({
available: r.utils.prettyNumber(available),
target: targeted ? srname : 'the frontpage',
start: startdate,
end: enddate
})
$(".available-info").text('')
$(".OVERSOLD_DETAIL").text(message).show()
} else {
var message = r._("We have insufficient inventory to fulfill" +
" your requested budget, target, and dates." +
" Only %(available)s impressions available" +
" on %(target)s from %(start)s to %(end)s. " +
"Maximum budget is $%(max)s."
).format({
available: r.utils.prettyNumber(available),
target: targeted ? srname : 'the frontpage',
start: startdate,
end: enddate,
max: maxbid
})
$(".available-info").text('')
$(".OVERSOLD_DETAIL").text(message).show()
r.sponsored.disable_form($form)
$(".available-info").text('')
$(".OVERSOLD_DETAIL").text(message).show()
r.sponsored.disable_form($form)
}
} else {
$(".available-info").text(r._("%(num)s available (maximum budget is $%(max)s)").format({num: r.utils.prettyNumber(available), max: maxbid}))
$(".OVERSOLD_DETAIL").hide()
@@ -200,7 +229,10 @@ r.sponsored = {
bid = this.get_bid($form),
cpm = this.get_cpm($form),
ndays = this.get_duration($form),
impressions = this.calc_impressions(bid, cpm);
impressions = this.calc_impressions(bid, cpm),
selected = $form.find('*[name="priority"]:checked'),
isOverride = selected.attr("override") == "override",
isCpm = selected.attr("cpm") == "cpm"
$(".duration").text(ndays + " " + ((ndays > 1) ? r._("days") : r._("day")))
$(".price-info").text(r._("$%(cpm)s per 1,000 impressions").format({cpm: (cpm/100).toFixed(2)}))
@@ -208,8 +240,14 @@ r.sponsored = {
$(".OVERSOLD").hide()
this.enable_form($form)
this.check_bid($form)
this.check_inventory($form)
if (isCpm) {
this.show_cpm()
this.check_bid($form)
this.check_inventory($form, isOverride)
} else {
this.hide_cpm()
}
},
disable_form: function($form) {
@@ -224,6 +262,24 @@ r.sponsored = {
.removeClass("disabled");
},
hide_cpm: function() {
var priceRow = $('#cpm').parent('td').parent('tr'),
budgetRow = $('#bid').parent('td').parent('tr'),
impressionsRow = $('#impressions').parent('td').parent('tr')
priceRow.hide("slow")
budgetRow.hide("slow")
impressionsRow.hide("slow")
},
show_cpm: function() {
var priceRow = $('#cpm').parent('td').parent('tr'),
budgetRow = $('#bid').parent('td').parent('tr'),
impressionsRow = $('#impressions').parent('td').parent('tr')
priceRow.show("slow")
budgetRow.show("slow")
impressionsRow.show("slow")
},
targeting_on: function() {
$('.targeting').find('*[name="sr"]').prop("disabled", "").end().slideDown();
this.fill_campaign_editor()
@@ -234,6 +290,10 @@ r.sponsored = {
this.fill_campaign_editor()
},
priority_changed: function() {
this.fill_campaign_editor()
},
check_bid: function($form) {
var bid = this.get_bid($form),
minimum_bid = $("#bid").data("min_bid");
@@ -338,11 +398,14 @@ function get_flag_class(flags) {
if (flags.refund) {
css_class += " refund";
}
if (flags.non_cpm) {
css_class += " non_cpm";
}
return css_class
}
$.new_campaign = function(campaign_id36, start_date, end_date, duration,
bid, spent, cpm, targeting, flags) {
bid, spent, cpm, targeting, priority, override, flags) {
cancel_edit(function() {
var data =('<input type="hidden" name="startdate" value="' +
start_date +'"/>' +
@@ -352,6 +415,8 @@ $.new_campaign = function(campaign_id36, start_date, end_date, duration,
'<input type="hidden" name="cpm" value="' + cpm + '"/>' +
'<input type="hidden" name="targeting" value="' +
(targeting || '') + '"/>' +
'<input type="hidden" name="priority" value="' + priority + '"/>' +
'<input type="hidden" name="override" value="' + override + '"/>' +
'<input type="hidden" name="campaign_id36" value="' + campaign_id36 + '"/>');
if (flags && flags.pay_url) {
data += ("<input type='hidden' name='pay_url' value='" +
@@ -365,7 +430,7 @@ $.new_campaign = function(campaign_id36, start_date, end_date, duration,
data += ("<input type='hidden' name='refund_url' value='" +
flags.refund_url + "'/>");
}
var row = [start_date, end_date, duration, "$" + bid, "$" + spent, targeting, data];
var row = [start_date, end_date, duration, priority, "$" + bid, "$" + spent, targeting, data];
$(".existing-campaigns .error").hide();
var css_class = get_flag_class(flags);
$(".existing-campaigns table").show()
@@ -378,7 +443,8 @@ $.new_campaign = function(campaign_id36, start_date, end_date, duration,
};
$.update_campaign = function(campaign_id36, start_date, end_date,
duration, bid, spent, cpm, targeting, flags) {
duration, bid, spent, cpm, targeting, priority,
override, flags) {
cancel_edit(function() {
$('.existing-campaigns input[name="campaign_id36"]')
.filter('*[value="' + (campaign_id36 || '0') + '"]')
@@ -387,6 +453,7 @@ $.update_campaign = function(campaign_id36, start_date, end_date,
.children(":first").html(start_date)
.next().html(end_date)
.next().html(duration)
.next().html(priority)
.next().html("$" + bid).removeClass()
.next().html("$" + spent)
.next().html(targeting)
@@ -394,6 +461,8 @@ $.update_campaign = function(campaign_id36, start_date, end_date,
.find('*[name="startdate"]').val(start_date).end()
.find('*[name="enddate"]').val(end_date).end()
.find('*[name="targeting"]').val(targeting).end()
.find('*[name="priority"]').val(priority).end()
.find('*[name="override"]').val(override).end()
.find('*[name="bid"]').val(bid).end()
.find('*[name="cpm"]').val(cpm).end()
.find("button, span").remove();
@@ -412,7 +481,7 @@ $.set_up_campaigns = function() {
$(".existing-campaigns tr").each(function() {
var tr = $(this);
var td = $(this).find("td:last");
var bid_td = $(this).find("td:first").next().next().next()
var bid_td = $(this).find("td:first").next().next().next().next()
.addClass("bid");
if(td.length && ! td.children("button, span").length ) {
if(tr.hasClass("live")) {
@@ -427,20 +496,22 @@ $.set_up_campaigns = function() {
/* once paid, we shouldn't muck around with the campaign */
if(!tr.hasClass("complete") && !tr.hasClass("live")) {
if (tr.hasClass("sponsor") && !tr.hasClass("free")) {
$(bid_td).append($(free).addClass("free")
.click(function() { free_campaign(tr) }))
}
else if (!tr.hasClass("paid")) {
$(bid_td).prepend($(pay).addClass("pay fancybutton")
.click(function() { pay_campaign(tr) }));
} else if (tr.hasClass("free")) {
$(bid_td).addClass("free paid")
.prepend("<span class='info'>freebie</span>");
} else {
(bid_td).addClass("paid")
.prepend($(repay).addClass("pay fancybutton")
.click(function() { pay_campaign(tr) }));
if (!tr.hasClass("non_cpm")) {
if (tr.hasClass("sponsor") && !tr.hasClass("free")) {
$(bid_td).append($(free).addClass("free")
.click(function() { free_campaign(tr) }))
}
else if (!tr.hasClass("paid")) {
$(bid_td).prepend($(pay).addClass("pay fancybutton")
.click(function() { pay_campaign(tr) }));
} else if (tr.hasClass("free")) {
$(bid_td).addClass("free paid")
.prepend("<span class='info'>freebie</span>");
} else {
(bid_td).addClass("paid")
.prepend($(repay).addClass("pay fancybutton")
.click(function() { pay_campaign(tr) }));
}
}
var e = $(edit).addClass("edit fancybutton")
.click(function() { edit_campaign(tr); });
@@ -451,7 +522,9 @@ $.set_up_campaigns = function() {
if (tr.hasClass("complete")) {
$(td).append("<span class='info'>complete</span>");
}
$(bid_td).addClass("paid")
if (!tr.hasClass("non_cpm")) {
$(bid_td).addClass("paid")
}
/* sponsors can always edit */
if (tr.hasClass("sponsor")) {
var e = $(edit).addClass("edit fancybutton")
@@ -461,6 +534,9 @@ $.set_up_campaigns = function() {
}
}
});
if (!r.sponsored.isSponsor) {
$('.existing-campaigns tr td:nth-child(4), .existing-campaigns tr th:nth-child(4)').hide()
}
return $;
}
@@ -543,6 +619,10 @@ function edit_campaign(elem) {
i = '*[name="' + i + '"]';
c.find(i).val(data_tr.find(i).val());
});
var priorities = c.find('*[name="priority"]'),
campPriority = data_tr.find('*[name="priority"]').val()
priorities.filter('*[value="' + campPriority + '"]')
.prop("checked", "checked")
/* check if targeting is turned on */
var targeting = data_tr
.find('*[name="targeting"]').val();
@@ -598,6 +678,7 @@ function create_campaign() {
.find('input[name="campaign_id36"]').val('').end()
.find('input[name="sr"]').val('').prop("disabled", "disabled").end()
.find('input[name="targeting"][value="none"]').prop("checked", "checked").end()
.find('input[name="priority"][default="default"]').prop("checked", "checked").end()
.find(".targeting").hide().end()
.find('input[name="cpm"]').val(base_cpm).end()
.fadeIn();

View File

@@ -42,7 +42,7 @@ ${unsafe(js.use('sponsored'))}
<%def name="javascript_setup()">
<script type="text/javascript">
r.sponsored.init();
r.sponsored.init(${unsafe(simplejson.dumps(c.user_is_sponsor))});
r.sponsored.setup(${unsafe(simplejson.dumps(thing.inventory))})
</script>
</%def>
@@ -344,6 +344,29 @@ ${self.javascript_setup()}
</td>
</tr>
<tr style="${'display:none' if not c.user_is_sponsor else ''}">
<th>${_("priority")}</th>
<td class="prefright">
%for value, text, description, default, inventory_override, cpm in thing.priorities:
<label>
<input id="${value}" class="nomargin"
type="radio" value="${value}" name="priority"
onclick="r.sponsored.priority_changed()"
${"checked='checked'" if default else ""}
${"default='default'" if default else ""}
${"override='override'" if inventory_override else ""}
${"cpm='cpm'" if cpm else ""}>
%if description:
<span>${"%s (%s)" % (text, description)}</span>
%else:
<span>${text}</span>
%endif
</label>
<br />
%endfor
</td>
</tr>
<tr>
<th>${_("budget")}</th>
<td class="prefright">
@@ -469,6 +492,7 @@ ${self.javascript_setup()}
<th title="${start_title}">${_("start")}</th>
<th title="${end_title}">${_("end")}</th>
<th>${_("duration")}</th>
<th>${_("priority")}</th>
<th>${_("total budget")}</th>
<th>${_("spent")}</th>
<th title="${targeting_title}">${_("targeting")}</th>
@@ -486,7 +510,8 @@ ${self.javascript_setup()}
%for rc in sorted(thing.campaigns, key=lambda rc: rc.start_date):
$.new_campaign(${unsafe(','.join(simplejson.dumps(attr) for attr in
[rc.campaign_id36, rc.start_date, rc.end_date, rc.duration,
rc.bid, rc.spent, rc.cpm, rc.sr, rc.status]))});
rc.bid, rc.spent, rc.cpm, rc.sr, rc.priority_name,
rc.inventory_override, rc.status]))});
%endfor
$.set_up_campaigns();
});