terminalone/models/campaign.py
# -*- coding: utf-8 -*-
"""Provides campaign object."""
from __future__ import absolute_import
from terminalone.models import BudgetFlight
from .. import t1types
from ..entity import Entity
from ..errors import ClientError
from ..vendor import six
from functools import partial
class Campaign(Entity):
"""Campaign entity.
When creating a new campaign, "zone_name" must be set to the name, such as
America/New_York, rather than the code. A list of time zone names can be
found on the developer portal."""
collection = 'campaigns'
resource = 'campaign'
_relations = {
'advertiser', 'ad_server', 'currency', 'merit_pixel', 'time_zone',
}
_conv = t1types.enum({'every', 'one', 'variable'}, 'variable')
_cap_ints = t1types.enum({'hour', 'day', 'week', 'month',
'not-applicable'}, 'not-applicable')
_cap_types = t1types.enum({'even', 'asap', 'no-limit'}, 'no-limit')
_goal_cats = t1types.enum({'audience', 'engagement', 'response'}, None)
_goal_types = t1types.enum({'spend', 'reach', 'cpc', 'cpe', 'cpa', 'roi', 'viewability_rate', 'vcr', 'ctr', 'vcpm'},
None)
_serv_types = t1types.enum({'SELF', 'MANAGED'}, 'SELF')
_bid_cross_types = t1types.enum({'DETERMINISTIC_ONLY', 'DETERMINISTIC_FIRST'}, None)
_pull = {
'ad_server_fee': float,
'ad_server_id': int,
'ad_server_password': None,
'ad_server_username': None,
'advertiser_id': int,
'agency_fee_pct': float,
'conversion_type': None,
'conversion_variable_minutes': int,
'created_on': t1types.strpt,
'currency_code': None,
'dcs_data_is_campaign_level': t1types.int_to_bool,
'end_date': t1types.strpt,
'frequency_amount': int,
'frequency_interval': None,
'frequency_type': None,
'frequency_optimization': t1types.int_to_bool,
'goal_alert': float,
'goal_category': None,
'goal_type': None,
'goal_value': float,
'has_custom_attribution': t1types.int_to_bool,
'id': int,
'impression_cap_amount': int,
'impression_cap_automatic': t1types.int_to_bool,
'impression_cap_type': None,
'io_name': None,
'io_reference_num': None,
'initial_start_date': t1types.strpt,
'margin_pct': float,
'minimize_multi_ads': t1types.int_to_bool,
'merit_pixel_id': int,
'name': None,
'override_suspicious_traffic_filter': t1types.int_to_bool,
'pacing_alert': float,
'pc_window_minutes': int,
'pv_pct': float,
'pv_window_minutes': int,
'service_type': None,
'source_campaign_id': int,
'spend_cap_amount': float,
'spend_cap_automatic': t1types.int_to_bool,
'spend_cap_enabled': t1types.int_to_bool,
'spend_cap_type': None,
'start_date': t1types.strpt,
'status': t1types.int_to_bool,
'suspicious_traffic_filter_level': int,
'total_budget': float,
'total_impression_budget': int,
'updated_on': t1types.strpt,
'use_default_ad_server': t1types.int_to_bool,
'use_mm_freq': t1types.int_to_bool,
'version': int,
'zone_name': None,
'viewability_type': None,
'viewability_vendor_id': int,
'viewability_sample_rate': float,
'is_programmatic_guaranteed': t1types.int_to_bool,
'restrict_targeting_to_same_device_id': t1types.int_to_bool,
'connected_id_type': None,
'bid_min_devices': int,
'political': t1types.int_to_bool
}
_push = _pull.copy()
_push.update({
'conversion_type': _conv,
'dcs_data_is_campaign_level': int,
'end_date': t1types.strft,
'frequency_interval': _cap_ints,
'frequency_type': _cap_types,
'frequency_optimization': int,
'goal_category': _goal_cats,
'goal_type': _goal_types,
'has_custom_attribution': int,
'impression_cap_automatic': int,
'impression_cap_type': _cap_types,
'initial_start_date': t1types.strft,
'minimize_multi_ads': int,
'override_suspicious_traffic_filter': int,
'service_type': _serv_types,
'spend_cap_automatic': int,
'spend_cap_enabled': int,
'spend_cap_type': _cap_types,
'start_date': t1types.strft,
'status': int,
'suspicious_traffic_filter_level': int,
'use_default_ad_server': int,
'use_mm_freq': int,
'is_programmatic_guaranteed': int,
'restrict_targeting_to_same_device_id': int,
'connected_id_type': _bid_cross_types,
'political': int,
'merit_pixel_id': partial(t1types.int_or_none, null_on_none=True),
'pc_window_minutes': partial(t1types.int_or_none, null_on_none=True),
'pv_window_minutes': partial(t1types.int_or_none, null_on_none=True),
'pv_pct': partial(t1types.float_or_none, null_on_none=True),
'conversion_variable_minutes': partial(t1types.int_or_none, null_on_none=True)
})
def __init__(self, session, properties=None, **kwargs):
super(Campaign, self).__init__(session, properties, **kwargs)
def save(self, data=None, url=None):
"""Save object to T1 while accounting for old fields"""
if data is None:
data = self._properties.copy()
if 'merit_pixel_id' in data and data['merit_pixel_id'] is None:
self._properties.pop('merit_pixel_id', None)
data['merit_pixel_id'] = None
if 'pc_window_minutes' in data and data['pc_window_minutes'] is None:
self._properties.pop('pc_window_minutes', None)
data['pc_window_minutes'] = None
if 'pv_window_minutes' in data and data['pv_window_minutes'] is None:
self._properties.pop('pv_window_minutes', None)
data['pv_window_minutes'] = None
if 'pv_pct' in data and data['pv_pct'] is None:
self._properties.pop('pv_pct', None)
data['pv_pct'] = None
if 'conversion_variable_minutes' in data and data['conversion_variable_minutes'] is None:
self._properties.pop('conversion_variable_minutes', None)
data['conversion_variable_minutes'] = None
super(Campaign, self).save(data=data, url=url)
def save_budget_flights(self, data=None):
if data is None and self.budget_flights is None:
raise ClientError('No budget flights to save')
if data is None:
data = self.budget_flights
postdata = {}
for i, flight in enumerate(data):
d = flight.get_formdata(includeunchanged=True)
for key, value in six.iteritems(d):
postdata['budget_flights.{}.{}'.format(i + 1, key)] = value
try:
postdata['budget_flights.{}.id'.format(i + 1)] = flight.id
except AttributeError:
pass
url = self._construct_url(addl=['budget_flights', 'bulk', ]) + '?full=*'
flights, _ = self._post(self._get_service_path(), rest=url, data=postdata, )
self._init_properties['budget_flights'] = [BudgetFlight(self.session, f) for f in flights]
if self._properties.get('budget_flights'):
del (self._properties['budget_flights'])