promote: Stop using VDate for advance date checking.

The logic was getting too complicated so has been moved into
promote.get_date_limits().
This commit is contained in:
Brian Simpson
2015-02-20 17:09:37 -05:00
parent 2dac08e8df
commit f6891b5339
4 changed files with 109 additions and 83 deletions

View File

@@ -269,8 +269,8 @@ class SponsorController(PromoteController):
@validate(
VSponsorAdmin(),
start=VDate('startdate', reference_date=promote.promo_datetime_now),
end=VDate('enddate', reference_date=promote.promo_datetime_now),
start=VDate('startdate'),
end=VDate('enddate'),
sr_name=nop('sr_name'),
collection_name=nop('collection_name'),
)
@@ -917,8 +917,8 @@ class PromoteApiController(ApiController):
@validatedForm(
VSponsorAdmin(),
VModhash(),
start=VDate('startdate', reference_date=promote.promo_datetime_now),
end=VDate('enddate', reference_date=promote.promo_datetime_now),
start=VDate('startdate'),
end=VDate('enddate'),
sr=VSubmitSR('sr', promotion=True),
)
def POST_add_roadblock(self, form, jquery, start, end, sr):
@@ -942,8 +942,8 @@ class PromoteApiController(ApiController):
@validatedForm(
VSponsorAdmin(),
VModhash(),
start=VDate('startdate', reference_date=promote.promo_datetime_now),
end=VDate('enddate', reference_date=promote.promo_datetime_now),
start=VDate('startdate'),
end=VDate('enddate'),
sr=VSubmitSR('sr', promotion=True),
)
def POST_rm_roadblock(self, form, jquery, start, end, sr):
@@ -959,21 +959,8 @@ class PromoteApiController(ApiController):
@validatedForm(
VSponsor('link_id36'),
VModhash(),
start=VDate(
'startdate',
earliest=timedelta(days=g.min_promote_future),
latest=timedelta(days=g.max_promote_future),
reference_date=promote.promo_datetime_now,
business_days=True,
sponsor_override=True,
),
end=VDate(
'enddate',
earliest=timedelta(days=g.min_promote_future),
latest=timedelta(days=g.max_promote_future),
reference_date=promote.promo_datetime_now,
sponsor_override=True,
),
start=VDate('startdate'),
end=VDate('enddate'),
link=VLink('link_id36'),
bid=VFloat('bid', coerce=False),
target=VPromoTarget(),
@@ -1000,10 +987,31 @@ class PromoteApiController(ApiController):
cpm = PromotionPrices.get_price(c.user, target, location)
if (form.has_errors('startdate', errors.BAD_DATE,
errors.DATE_TOO_EARLY, errors.DATE_TOO_LATE) or
form.has_errors('enddate', errors.BAD_DATE, errors.DATE_TOO_EARLY,
errors.DATE_TOO_LATE, errors.BAD_DATE_RANGE)):
if (form.has_errors('startdate', errors.BAD_DATE) or
form.has_errors('enddate', errors.BAD_DATE)):
return
min_start, max_start, max_end = promote.get_date_limits(
link, c.user_is_sponsor)
if start.date() < min_start:
c.errors.add(errors.DATE_TOO_EARLY,
msg_params={'day': min_start.strftime("%m/%d/%Y")},
field='startdate')
form.has_errors('startdate', errors.DATE_TOO_EARLY)
return
if start.date() > max_start:
c.errors.add(errors.DATE_TOO_LATE,
msg_params={'day': max_start.strftime("%m/%d/%Y")},
field='startdate')
form.has_errors('startdate', errors.DATE_TOO_LATE)
return
if end.date() > max_end:
c.errors.add(errors.DATE_TOO_LATE,
msg_params={'day': max_end.strftime("%m/%d/%Y")},
field='enddate')
form.has_errors('enddate', errors.DATE_TOO_LATE)
return
if end < start:
@@ -1011,16 +1019,6 @@ class PromoteApiController(ApiController):
form.has_errors('enddate', errors.BAD_DATE_RANGE)
return
# check that start is not so late that authorization hold will expire
if not c.user_is_sponsor:
max_start = promote.get_max_startdate()
if start > max_start:
c.errors.add(errors.DATE_TOO_LATE,
msg_params={'day': max_start.strftime("%m/%d/%Y")},
field='startdate')
form.has_errors('startdate', errors.DATE_TOO_LATE)
return
# Limit the number of PromoCampaigns a Link can have
# Note that the front end should prevent the user from getting
# this far
@@ -1148,20 +1146,19 @@ class PromoteApiController(ApiController):
if campaign_has_oversold_error(form, campaign):
return
# check that start is not so late that authorization hold will expire
max_start = promote.get_max_startdate()
if campaign.start_date > max_start:
# check the campaign dates are still valid (user may have created
# the campaign a few days ago)
min_start, max_start, max_end = promote.get_date_limits(
link, c.user_is_sponsor)
if campaign.start_date.date() > max_start:
msg = _("please change campaign start date to %(date)s or earlier")
date = format_date(max_start, format="short", locale=c.locale)
msg %= {'date': date}
form.set_text(".status", msg)
return
# check the campaign start date is still valid (user may have created
# the campaign a few days ago)
now = promote.promo_datetime_now()
min_start = now + timedelta(days=g.min_promote_future)
if campaign.start_date.date() < min_start.date():
if campaign.start_date.date() < min_start:
msg = _("please change campaign start date to %(date)s or later")
date = format_date(min_start, format="short", locale=c.locale)
msg %= {'date': date}

View File

@@ -117,7 +117,7 @@ from r2.lib.menus import OffsiteButton, menu, JsNavMenu
from r2.lib.normalized_hot import normalized_hot
from r2.lib.strings import plurals, rand_strings, strings, Score
from r2.lib.utils import is_subdomain, title_to_url, query_string, UrlParser
from r2.lib.utils import url_links_builder, make_offset_date, median, to36
from r2.lib.utils import url_links_builder, median, to36
from r2.lib.utils import trunc_time, timesince, timeuntil, weighted_lottery
from r2.lib.template_helpers import (
add_sr,
@@ -144,7 +144,6 @@ import csv
import hmac
import hashlib
import cStringIO
import pytz
import sys, random, datetime, calendar, simplejson, re, time
import time
from itertools import chain, product
@@ -3868,26 +3867,8 @@ class PromoteLinkEdit(PromoteLinkBase):
)
self.bids.append(row)
# determine date range
now = promote.promo_datetime_now()
if c.user_is_sponsor:
min_start = now
elif promote.is_accepted(link):
min_start = make_offset_date(now, 1, business_days=True)
else:
min_start = make_offset_date(now, g.min_promote_future,
business_days=True)
if c.user_is_sponsor:
max_end = now + datetime.timedelta(days=366)
else:
max_end = now + datetime.timedelta(days=g.max_promote_future)
if c.user_is_sponsor:
max_start = max_end - datetime.timedelta(days=1)
else:
max_start = promote.get_max_startdate()
min_start, max_start, max_end = promote.get_date_limits(
link, c.user_is_sponsor)
default_end = min_start + datetime.timedelta(days=2)
default_start = min_start

View File

@@ -20,8 +20,9 @@
# Inc. All Rights Reserved.
###############################################################################
import calendar
from collections import namedtuple
from datetime import datetime, timedelta
import datetime
from decimal import Decimal, ROUND_DOWN, ROUND_UP
import hashlib
import hmac
@@ -34,6 +35,7 @@ import urlparse
from pylons import g, c
from pylons.i18n import ungettext
from pytz import timezone
from r2.lib import (
authorize,
@@ -445,19 +447,65 @@ def auth_campaign(link, campaign, user, pay_id):
# midnight eastern-US.
# TODO: make this a config parameter
timezone_offset = -5 # hours
timezone_offset = timedelta(0, timezone_offset * 3600)
timezone_offset = datetime.timedelta(0, timezone_offset * 3600)
def promo_datetime_now(offset=None):
now = datetime.now(g.tz) + timezone_offset
now = datetime.datetime.now(g.tz) + timezone_offset
if offset is not None:
now += timedelta(offset)
now += datetime.timedelta(offset)
return now
def get_max_startdate():
# authorization hold happens now but expires after 30 days. charge
# happens 1 day before the campaign launches. the latest a campaign
# can start is 30 days from now (it will get charged in 29 days).
return promo_datetime_now() + timedelta(days=30)
# campaigns can launch the following day if they're created before 17:00 PDT
DAILY_CUTOFF = datetime.time(17, tzinfo=timezone("US/Pacific"))
def get_date_limits(link, is_sponsor=False):
promo_today = promo_datetime_now().date()
if is_sponsor:
min_start = promo_today
elif is_accepted(link):
# link is already accepted--let user create a campaign starting
# tomorrow because it doesn't need to be re-reviewed
min_start = promo_today + datetime.timedelta(days=1)
else:
# campaign and link will need to be reviewed before they can launch.
# review can happen until DAILY_CUTOFF PDT Monday through Friday and
# Sunday. Any campaign created after DAILY_CUTOFF is treated as if it
# were created the following day.
now = datetime.datetime.now(tz=timezone("US/Pacific"))
now_today = now.date()
too_late_for_review = now.time() > DAILY_CUTOFF
if too_late_for_review and now_today.weekday() == calendar.FRIDAY:
# no review late on Friday--earliest review is Sunday to launch
# on Monday
min_start = now_today + datetime.timedelta(days=3)
elif now_today.weekday() == calendar.SATURDAY:
# no review any time on Saturday--earliest review is Sunday to
# launch on Monday
min_start = now_today + datetime.timedelta(days=2)
elif too_late_for_review:
# no review late in the day--earliest review is tomorrow to
# launch the following day
min_start = now_today + datetime.timedelta(days=2)
else:
# review will happen today so can launch tomorrow
min_start = now_today + datetime.timedelta(days=1)
if is_sponsor:
max_end = promo_today + datetime.timedelta(days=366)
else:
max_end = promo_today + datetime.timedelta(days=93)
if is_sponsor:
max_start = max_end - datetime.timedelta(days=1)
else:
# authorization hold happens now but expires after 30 days. charge
# happens 1 day before the campaign launches. the latest a campaign
# can start is 30 days from now (it will get charged in 29 days).
max_start = promo_today + datetime.timedelta(days=30)
return min_start, max_start, max_end
def accept_promotion(link):
@@ -693,8 +741,8 @@ def make_daily_promotions():
def finalize_completed_campaigns(daysago=1):
# PromoCampaign.end_date is utc datetime with year, month, day only
now = datetime.now(g.tz)
date = now - timedelta(days=daysago)
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,
@@ -874,8 +922,8 @@ def get_total_run(thing):
# a manually launched promo (e.g., sr discovery) might not have campaigns.
if not earliest or not latest:
latest = datetime.utcnow()
earliest = latest - timedelta(days=30) # last month
latest = datetime.datetime.utcnow()
earliest = latest - datetime.timedelta(days=30) # last month
# ugh this stuff is a mess. they're stored as "UTC" but actually mean UTC-5.
earliest = earliest.replace(tzinfo=g.tz) - timezone_offset
@@ -886,7 +934,7 @@ def get_total_run(thing):
def get_traffic_dates(thing):
"""Retrieve the start and end of a Promoted Link or PromoCampaign."""
now = datetime.now(g.tz).replace(minute=0, second=0, microsecond=0)
now = datetime.datetime.now(g.tz).replace(minute=0, second=0, microsecond=0)
start, end = get_total_run(thing)
end = min(now, end)
return start, end
@@ -894,7 +942,7 @@ def get_traffic_dates(thing):
def get_billable_impressions(campaign):
start, end = get_traffic_dates(campaign)
if start > datetime.now(g.tz):
if start > datetime.datetime.now(g.tz):
return 0
traffic_lookup = traffic.TargetedImpressionsByCodename.promotion_history
@@ -966,9 +1014,9 @@ def Run(verbose=True):
"""
if verbose:
print "%s promote.py:Run() - make_daily_promotions()" % datetime.now(g.tz)
print "%s promote.py:Run() - make_daily_promotions()" % datetime.datetime.now(g.tz)
make_daily_promotions()
if verbose:
print "%s promote.py:Run() - finished" % datetime.now(g.tz)
print "%s promote.py:Run() - finished" % datetime.datetime.now(g.tz)

View File

@@ -41,7 +41,7 @@ and choose "frontpage" or "targeted" and click "save":
Please note that we can't approve your ad until you have authorized your credit
card payment, and that your ad must be approved before it goes live on your
selected dates. We require ${g.min_promote_future} ${ungettext("business day", "business days", g.min_promote_future)} as a grace period for the approval
selected dates. We require one business day as a grace period for the approval
process before the ad can go live. To pay for your campaign, click the "pay"
button within the campaign dashboard on the link above.