class App(BaseReadonlyModelMixin, BaseModel): app_id = models.AutoField(primary_key=True) publisher_id = models.ForeignKey(Publisher, db_column='publisher_id') app = models.CharField(max_length=255) os = models.CharField(max_length=7, choices=OS_CHOICES) # pylint: disable=invalid-name app_url = models.CharField(max_length=255) category1 = models.CharField(max_length=255) category2 = models.CharField(max_length=255) bcat = models.TextField() badv = models.TextField() battr = models.CharField(max_length=255) deleted = models.BooleanField(default=False) date_added = DateTimeField() date_modified = DateTimeField() cost_method = models.CharField(max_length=8, default=COST_METHOD_REVSHARE, choices=COST_METHOD_CHOICES) cpm = models.FloatField(default=0.0) revshare = models.FloatField(default=0.6) cost_map = models.TextField() bid_floor_map = models.TextField() bg_frames = models.TextField() video_enabled = models.BooleanField(default=True) tier1_approved = models.BooleanField(default=False) objects = AppManager() objects_raw = models.Manager() class Meta: managed = True db_table = 'app' app_label = 'restapi' def __unicode__(self): return self.os + " - " + self.app
class Zone(BaseModel): zone_id = models.AutoField(primary_key=True) app_id = models.ForeignKey(App, db_column='app_id') zone = models.CharField(max_length=255, default='') orientation = models.CharField(max_length=9, choices=ZONE_ORIENTATION_CHOICES, default=ZONE_ORIENTATION_BOTH) status = models.CharField(max_length=7, choices=ZONE_STATUS_CHOICES, default=ZONE_STATUS_LIVE) deleted = models.BooleanField(default=False) date_added = DateTimeField(auto_now_add=True) date_modified = DateTimeField(auto_now=True) cost_method = models.CharField(max_length=8, choices=COST_METHOD_CHOICES, default=COST_METHOD_BLANK) cpm = models.FloatField(default=0.0) revshare = models.FloatField(default=0.0) cost_map = models.TextField(default="") bid_floor_map = models.TextField(default="") custom_priority = models.TextField(default="") size = models.CharField(max_length=8, choices=ZONE_SIZE_CHOICES, default=ZONE_SIZE_BLANK) callback_url = models.CharField(max_length=255, default='') ad_type = models.IntegerField(choices=ZONE_AD_TYPE_CHOICES, default=ZONE_AD_TYPE_DISPLAY_VAST_VOXEL) security_key = models.CharField(max_length=100, null=True, default='') video_type = models.IntegerField(default=0) locked = models.BooleanField(default=False) objects = ZoneManager() objects_raw = models.Manager() class Meta: managed = True db_table = 'zone' app_label = 'restapi' def __unicode__(self): title = u'{0} - {1} ({2})'.format(self.zone, self.orientation, self.app) return title.encode('ascii', 'ignore') @property def encrypted_key(self): return encrypt(self.zone_id) @property # pylint: disable=invalid-name def os(self): return self.app_id.os @property def app(self): return self.app_id.app @property def ad_type_title(self): return dict(ZONE_AD_TYPE_CHOICES)[self.ad_type]
class TradingDesk(BaseModel): trading_desk_id = models.AutoField(primary_key=True) trading_desk = models.CharField(max_length=255) trading_desk_key = models.CharField(max_length=40, default=ApiKeyGenerator.key_generator) account_manager = models.IntegerField(max_length=10, default=0) currency = models.CharField(max_length=3, default=CURRENCY_USD) contact = models.CharField(max_length=100) address1 = models.CharField(max_length=255, default='', blank=True) address2 = models.CharField(max_length=255, default='', blank=True) city = models.CharField(max_length=100, default='', blank=True) state_prov = models.CharField(max_length=100, default='', blank=True) zip = models.CharField(max_length=10, default='', blank=True) country = models.CharField(max_length=3, choices=COUNTRIES, default=COUNTRIES[0][0]) phone = models.CharField(max_length=20, default='', blank=True) email = models.CharField(max_length=255, default='', blank=True) notes = models.TextField(blank=True, default='') status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TradingDeskManager() objects_raw = models.Manager() permission_check = True def is_own(self): # have no sence for new trading desk if self.pk is None: return True else: return bool( self.trading_desk_userprofiles.filter(user=REGISTRY['user'])) @property def is_manage_trading_desk(self): return self.pk == MANAGE_TRADING_DESK_ID def __unicode__(self): return self.trading_desk class Meta: # managed=False db_table = 'trading_desk' app_label = 'restapi' ordering = ['trading_desk']
class BidderWhitelistIds(BaseModel): id = models.AutoField(primary_key=True, db_column='id') placement_type = models.CharField(max_length=4, choices=PLACEMENT_TYPE_CHOICES) placement_id = models.CharField(max_length=128) tag = models.CharField(max_length=128, default='', blank=True, null=True) last_update = DateTimeField(default=None, auto_now=True) campaign_id = models.IntegerField(default=0) ad_group_id = models.IntegerField(default=0) source_id = models.IntegerField(default=0) size = SizeCharField(max_length=8, choices=SIZE_CHOICES, null=True, default=None, blank=True) objects = BidderWhitelistManager() objects_raw = models.Manager() permission_check = True def is_own(self): return PlacementHelper.is_own(self.campaign_id, self.ad_group_id) @classmethod def get_perm_model_name(self): return BidderWhitelist.get_perm_model_name() def __unicode__(self): return self.placement_id class Meta: managed = True app_label = 'restapi' db_table = 'bidder_whitelist' ordering = ['-last_update'] unique_together = ('campaign_id', 'ad_group_id', 'source_id', 'placement_type', 'placement_id')
class BidderWhitelist(BaseModel, BaseReadonlyModelMixin): id = models.AutoField(primary_key=True, db_column='id') placement_type = models.CharField(max_length=4, choices=PLACEMENT_TYPE_CHOICES) placement_id = models.CharField(max_length=128) tag = models.CharField(max_length=128, default='', blank=True, null=True) last_update = DateTimeField(default=None, auto_now=True) advertiser_id = models.IntegerField() campaign_id = models.IntegerField() campaign = models.CharField(max_length=255) ad_group_id = models.IntegerField() ad_group = models.CharField(max_length=255) source_id = models.IntegerField() source = models.CharField(max_length=255) size = SizeCharField(max_length=8, choices=SIZE_CHOICES, null=True, default=None, blank=True) objects = BidderWhitelistManager() objects_raw = models.Manager() permission_check = True def is_own(self): return PlacementHelper.is_own(self.campaign_id, self.ad_group_id) def autopopulate_by_ownership(self): pass def __unicode__(self): return self.placement_id class Meta: managed = False app_label = 'restapi' db_table = 'bidder_whitelist_view' ordering = ['-last_update'] unique_together = ('campaign_id', 'ad_group_id', 'source_id', 'placement_type', 'placement_id')
class TwitterRevmap(BaseModel): revmap_id = models.AutoField(primary_key=True, db_column='id') campaign_id = models.OneToOneField(Campaign, db_column='campaign_id', related_name='revmap') tw_campaign_id = models.ForeignKey(TwitterCampaign.TwitterCampaign, default=0, db_constraint=False, db_column='tw_campaign_id') tw_line_item_id = models.ForeignKey(TwitterLineItem.TwitterLineItem, default=0, db_constraint=False, db_column='tw_line_item_id') opt_type = models.CharField(max_length=10, blank=True, choices=OPT_TYPE_CHOICES, default=OPT_TYPE_CHOICES[3][0]) opt_value = models.DecimalField(max_digits=9, decimal_places=4, default=0) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterRevmapManager() objects_raw = models.Manager() def __unicode__(self): return self.campaign_id class Meta: managed = True db_table = 'tw_revmap' app_label = 'restapi' unique_together = ('campaign_id', 'tw_campaign_id', 'tw_line_item_id')
class TwitterUser(BaseModel): tw_twitter_user_id = models.BigIntegerField(primary_key=True) name = models.CharField(max_length=255) timezone = models.CharField(max_length=255) oauth_token = models.CharField(max_length=255) oauth_secret = models.CharField(max_length=255) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterUserManager() objects_raw = models.Manager() # permission_check = True def __unicode__(self): return self.name class Meta: db_table = 'tw_twitter_user' app_label = 'restapi'
class DiscretePricing(BaseModel): id = models.AutoField(primary_key=True) placement_type = models.CharField(max_length=4, choices=PLACEMENT_TYPE_CHOICES, default=PLACEMENT_TYPE_ALL) placement_id = models.CharField(max_length=128, default='all') tag = models.CharField(max_length=128, blank=True, null=True, default='') size = models.CharField(max_length=8, choices=DISCRETE_PRICING_SIZE_CHOICES, default=SIZE_ALL) rev_value = models.FloatField() created_at = DateTimeField(default=None, auto_now_add=True) last_update = DateTimeField(default=None, auto_now=True) advertiser_id = models.IntegerField() campaign_id = models.IntegerField() campaign = models.CharField(max_length=255) ad_group_id = models.IntegerField() ad_group = models.CharField(max_length=255) source_id = models.IntegerField() source = models.CharField(max_length=255) objects = DiscretePricingManager() objects_raw = models.Manager() permission_check = False def __unicode__(self): return self.placement_id def is_own(self): return PlacementHelper.is_own(self.campaign_id, self.ad_group_id) def autopopulate_by_ownership(self): pass class Meta: managed = False db_table = 'discrete_pricing_view' app_label = 'restapi' ordering = ['-last_update'] unique_together = ('campaign_id', 'ad_group_id', 'source_id', 'placement_type', 'placement_id')
class CustomHintIds(BaseModel): id = models.AutoField(primary_key=True) size = models.CharField(max_length=8, choices=CUSTOM_HINT_SIZE_CHOICES, default=SIZE_ALL) placement_type = models.CharField(max_length=4, choices=PLACEMENT_TYPE_CHOICES, default=PLACEMENT_TYPE_ALL) placement_id = models.CharField(max_length=128, default='all') inflator_type = models.CharField(max_length=8, choices=INFLATOR_TYPE_CHOICES, null=True, default=None) inflator = models.DecimalField(max_digits=9, decimal_places=6, null=True, default=None) priority = models.IntegerField(default=0) max_frequency = models.IntegerField(default=0) frequency_interval = models.IntegerField(default=0) start_date = WideRangeDateTimeField(default=timezone( settings.TIME_ZONE).localize(datetime(datetime.now().year, 1, 1))) end_date = WideRangeDateTimeField(default=timezone( settings.TIME_ZONE).localize(datetime(datetime.now().year, 12, 31))) tag = models.CharField(max_length=128, blank=True, null=True, default=None) last_update = DateTimeField(default=None, auto_now=True) campaign_id = models.IntegerField(default=0) ad_group_id = models.IntegerField(default=0) ad_id = models.IntegerField(default=0) source_id = models.IntegerField(default=0) objects = CustomHintManager() objects_raw = models.Manager() permission_check = True def __unicode__(self): return self.placement_id @classmethod def get_perm_model_name(self): return CustomHint.get_perm_model_name() def is_own(self): return PlacementHelper.is_own(self.campaign_id, self.ad_group_id) class Meta: managed = True db_table = 'custom_hint' app_label = 'restapi' ordering = ['-last_update'] unique_together = ('campaign_id', 'ad_group_id', 'ad_id', 'source_id', 'size', 'placement_type', 'placement_id')
class Revmap(BaseModel): revmap_id = models.AutoField(primary_key=True, db_column='id') ad_group_id = models.OneToOneField(AdGroup, db_column='ad_group_id', related_name='revmap') ad_group = models.CharField(max_length=255) campaign_id = models.IntegerField(blank=False, null=False, default=None) campaign = models.CharField(max_length=255) opt_type = models.CharField(max_length=10, blank=True, choices=OPT_TYPE_CHOICES, default=OPT_TYPE_CHOICES[3][0]) opt_value = models.DecimalField(max_digits=9, decimal_places=4, default=0) rev_type = models.CharField(max_length=10, blank=True, choices=OPT_TYPE_CHOICES, default=OPT_TYPE_CHOICES[3][0]) rev_value = models.DecimalField(max_digits=9, decimal_places=4, default=0) target_type = models.CharField(max_length=10, blank=True, choices=OPT_TYPE_CHOICES, default=OPT_TYPE_CHOICES[3][0]) target_value = models.DecimalField(max_digits=9, decimal_places=4, default=0) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = RevmapManager() objects_raw = models.Manager() def __unicode__(self): return self.ad_group def make_copy(self): rejected_fields = lambda x: x.__class__ is models.AutoField or x.name == 'pk' values = [ (f.name, getattr(self, f.name)) for f in self._meta.fields if not rejected_fields(f) ] initials = dict([(k, v) for k, v in values if v is not None]) # pylint: disable=invalid-name m = self.__class__(**initials) # pylint: disable=invalid-name m.pk = None return m class Meta: managed = True db_table = 'revmap' app_label = 'restapi'
class ManageUser(BaseModel): user_id = models.AutoField(primary_key=True) role_id = models.ForeignKey(Role, db_column='role_id') full_name = models.CharField(max_length=255, blank=True) email = models.CharField(max_length=255, blank=True) password_salt = models.BinaryField(max_length=16, blank=True) password_hash = models.BinaryField(max_length=20, blank=True) create_time = DateTimeField(null=True, default=None) last_log_in_time = ZeroDateTimeField(default=None, null=True, blank=True) objects = ManageUserManager() objects_raw = models.Manager() def __unicode__(self): return self.email class Meta: managed = True unique_together = ('full_name', 'email') db_table = 'user' app_label = 'restapi'
class Ad(BaseModel): ad_id = models.AutoField(primary_key=True) ad_group_id = models.ForeignKey(AdGroup, db_column='ad_group_id') creative_id = models.IntegerField(blank=True, null=True) ad = models.CharField(max_length=255) # pylint: disable=invalid-name size = models.CharField(max_length=10, choices=AD_SIZE_CHOICES) html = models.TextField(blank=True) preview_html = models.TextField(blank=True) bid = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default=0) targeting = models.TextField(blank=True) categories = models.TextField(blank=True) attrs = models.CharField(max_length=255, blank=True) inflator_text = models.TextField(blank=True) domain = models.CharField(max_length=255, blank=True) redirect_url = models.CharField(max_length=1024, blank=True) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_CHOICES[1][0]) adx_status = models.CharField(max_length=11, choices=AD_STATUS_CHOICES, default=AD_STATUS_CHOICES[0][0]) appnexus_status = models.CharField(max_length=11, choices=AD_STATUS_CHOICES, default=AD_STATUS_CHOICES[0][0]) a9_status = models.CharField(max_length=11, choices=A9_STATUS_CHOICES, default=A9_STATUS_PASSED) external_args = models.TextField(blank=True) ad_type = models.IntegerField(default=1) adx_sensitive_categories = models.TextField(blank=True) adx_product_categories = models.TextField(blank=True) i_url = models.CharField(max_length=1024, blank=True, null=True) last_update = DateTimeField(null=True, default=None, auto_now=True) created_time = DateTimeField(auto_now_add=True) adx_attrs = models.CharField(max_length=255, default='', blank=True) tag = models.CharField(max_length=255, default='', blank=True) objects = AdManager() objects_raw = models.Manager() permission_check = True actions = ('bulk', 'bulk_re_submit_to_adx', 'export') search_args = ('ad_id', ) @property def search_result(self): return { 'level': 'ad', 'ad': self.ad, 'ad_id': self.ad_id, 'ad_type': self.ad_type, 'ad_group_id': self.ad_group_id.pk, 'campaign_id': self.campaign_id, 'advertiser_id': self.ad_group_id.campaign_id.advertiser_id.pk, 'creative_id': self.creative_id, 'bid': self.bid, 'last_update': self.last_update } def is_own(self): """Returns True if ad entity belongs to the current user.""" advertiser_ids = REGISTRY.get('user_advertiser_ids') if advertiser_ids and Ad.objects.filter( ad_group_id__campaign_id__advertiser_id__in=advertiser_ids, ad_id=self.ad_id).exists(): return True agency_ids = REGISTRY.get('user_agency_ids') if agency_ids and Ad.objects.filter( ad_group_id__campaign_id__advertiser_id__agency_id__in= agency_ids, ad_id=self.ad_id).exists(): return True trading_desk_ids = REGISTRY.get('user_trading_desk_ids') if trading_desk_ids and Ad.objects.filter( ad_group_id__campaign_id__advertiser_id__agency_id__trading_desk_id__in =trading_desk_ids, ad_id=self.ad_id).exists(): return True return False def autopopulate_by_ownership(self): pass @property def ad_group(self): return self.ad_group_id.ad_group @property def campaign_id(self): return self.ad_group_id.campaign_id.campaign_id @property def campaign(self): return self.ad_group_id.campaign_id.campaign @property def advertiser_id(self): return self.ad_group_id.campaign_id.advertiser_id.advertiser_id @property def advertiser(self): return self.ad_group_id.campaign_id.advertiser_id.advertiser @property def agency(self): return self.ad_group_id.campaign_id.advertiser_id.agency_id.agency @property def agency_id(self): return self.ad_group_id.campaign_id.advertiser_id.agency_id.agency_id @property def trading_desk(self): return self.ad_group_id.campaign_id.advertiser_id.agency_id.trading_desk_id.trading_desk @property def trading_desk_id(self): return self.ad_group_id.campaign_id.advertiser_id.agency_id.trading_desk_id.trading_desk_id @property def encrypted_ad_id(self): from mng.commons.crypto import encrypt return encrypt(self.ad_id) def __unicode__(self): return self.ad def make_copy(self): # pylint: disable=invalid-name m = deepcopy(self) m.pk = None m.ad_id = None m.creative_id = None m.ad_group_id_id = None m.domain = '' m.targeting = '' m.categories = '' m.status = STATUS_PAUSED # 'paused' m.adx_status = 'new' m.appnexus_status = 'new' m.a9_status = 'passed' m.adx_attrs = '' m.adx_sensitive_categories = '' m.adx_product_categories = '' return m class Meta: managed = True unique_together = ('ad_group_id', 'ad') db_table = 'ad' app_label = 'restapi'
class AuditLog(BaseModel): audit_log_id = models.AutoField(primary_key=True) user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='djangouser', db_constraint=False) audit_type = models.IntegerField() audit_action = models.IntegerField() item_id = models.IntegerField() old_data = models.TextField(blank=True) new_data = models.TextField(blank=True) created = DateTimeField(null=True, default=None, auto_now_add=True) objects = AuditLogManager() objects_raw = models.Manager() permission_check = True def is_own(self): from restapi.models.User import User own_users = list( User.objects_raw.filter( profile__trading_desk__trading_desk_userprofiles__user= REGISTRY['user'])) value = False try: value = self.user in own_users except ObjectDoesNotExist: pass return value def save(self, *args, **kwargs): now = datetime.now() self.created = now - timedelta(hours=PacificTimeShift.get(now)) super(AuditLog, self).save(*args, **kwargs) @property def diff_field(self): diff = {} if self.old_data != 'null' and self.new_data != 'null': try: old = json.loads(self.old_data) except ValueError: old = eval(self.old_data) try: new = json.loads(self.new_data) except ValueError: new = eval(self.new_data) keys = old.viewkeys() | new.viewkeys() for key in keys: if self.audit_action == AUDIT_ACTION_ADD and key in new: diff[key] = unicode(' ') + ' => ' + unicode(new.get(key)) elif self.audit_action == AUDIT_ACTION_UPDATE and key in old and key in new and old[ key] != new[key]: diff[key] = unicode(old.get(key)) + ' => ' + unicode( new.get(key)) elif self.audit_action == AUDIT_ACTION_DELETE: if key in old and key in new and old[key] != new[key]: diff[key] = unicode(old.get(key)) + ' => ' + unicode( new.get(key)) elif key in old and not new: diff[key] = unicode( old.get(key)) + ' => ' + unicode(' ') return diff @property def item(self): from restapi.audit_logger import AuditLogger model = AuditLogger.get_model_by_audit_type(self.audit_type) instance = None if model is not None: qs = model.objects.filter(pk=self.item_id) if qs.exists(): instance = qs.first() else: try: old_data = json.loads(self.old_data) except ValueError: old_data = None if old_data: instance = model() for key in old_data: try: setattr(instance, key, old_data[key]) except (ValueError, TypeError): pass return instance @property def item_name(self): instance = None try: instance = self.item except PermissionDeniedException: pass return unicode(instance) if instance is not None else None class Meta: managed = True db_table = 'audit_log' app_label = 'restapi' ordering = ['-audit_log_id'] def __unicode__(self): return self.audit_log_id
class AdGroup(BasePausedAtModelMixin, BaseModel): ad_group_id = models.AutoField(primary_key=True) campaign_id = models.ForeignKey(Campaign, db_column='campaign_id') ad_group = models.CharField(max_length=255) ad_group_type = models.IntegerField(blank=True, choices=AD_GROUP_TYPE_CHOICES, default=AD_GROUP_TYPE_CHOICES[0][0]) targeting = models.TextField(blank=True, default='', db_column='targeting') categories = models.TextField(blank=True) domain = models.CharField(max_length=255, blank=True) notes = models.TextField(blank=True) redirect_url = models.CharField(max_length=1024, blank=True) destination_url = models.CharField(max_length=511, blank=True, default='') viewthrough_url = models.CharField(max_length=1024, blank=True, default='') inflator_text = models.CharField(max_length=1024, blank=True) frequency_map = OrderedJSONField(blank=True, default='', cmp=cmp_asterisk_last, default_json=str) priority = models.IntegerField(blank=True, default=0) daily_budget_type = models.CharField(max_length=10, blank=True, choices=BUDGET_TYPES_CHOICES, default=BUDGET_TYPES_CHOICES[0][0]) daily_budget_value = models.DecimalField(max_digits=9, decimal_places=2, default=0) daily_spend = models.DecimalField(max_digits=9, decimal_places=2, default=0) capped = models.BooleanField(default=False, blank=True) hourly_capped = models.BooleanField(default=False, blank=True) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_CHOICES[1][0]) tag = models.CharField(max_length=255, blank=True, null=True) flight_start_date = ZeroDateTimeField(default=None, null=True, blank=True) flight_end_date = ZeroDateTimeField(default=None, null=True, blank=True) flight_budget_type = models.CharField(max_length=10, blank=True, choices=BUDGET_TYPES_CHOICES, default=BUDGET_TYPES_CHOICES[0][0]) flight_budget_value = models.DecimalField(max_digits=9, decimal_places=2, default=0) sampling_rate = models.DecimalField(max_digits=9, decimal_places=8, blank=True, null=True) throttling_rate = models.DecimalField(max_digits=9, decimal_places=8, blank=True, null=True) max_frequency = models.IntegerField(blank=True, null=True, default=None) frequency_interval = models.IntegerField(blank=True, null=True, default=None) event_args = models.TextField(blank=True) bidder_args = models.TextField(blank=True) last_update = DateTimeField(null=True, default=None, auto_now=True) paused_at = ZeroDateTimeField(default=None, null=True, blank=True) overridden = models.BooleanField(default=False, blank=True) ignore_fatigue_segment = models.NullBooleanField(default=None, blank=True, null=True) ignore_suppression_segment = models.NullBooleanField(default=None, blank=True, null=True) distribution_app_sha1_android_id = models.NullBooleanField(default=None, blank=True, null=True) distribution_app_ifa = models.NullBooleanField(default=None, blank=True, null=True) distribution_web = models.NullBooleanField(default=None, blank=True, null=True) total_cost_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) daily_cost_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) total_loss_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) daily_loss_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) revmap_rev_type = revmap_field('rev_type') revmap_rev_value = revmap_field('rev_value') revmap_opt_type = revmap_field('opt_type') revmap_opt_value = revmap_field('opt_value') revmap_target_type = revmap_field('target_type') revmap_target_value = revmap_field('target_value') _revmap = None objects = AdGroupManager() objects_raw = models.Manager() permission_check = True actions = ('bulk', 'import', 'export', 'rates', 'limits', 'targeting_json_read', 'targeting_json_update') @property def campaign(self): return self.campaign_id.campaign @property def advertiser_id(self): return self.campaign_id.advertiser_id.advertiser_id @property def advertiser(self): return self.campaign_id.advertiser_id.advertiser @property def agency(self): return self.campaign_id.advertiser_id.agency_id.agency @property def agency_id(self): return self.campaign_id.advertiser_id.agency_id.agency_id @property def trading_desk(self): return self.campaign_id.advertiser_id.agency_id.trading_desk_id.trading_desk @property def trading_desk_id(self): return self.campaign_id.advertiser_id.agency_id.trading_desk_id.trading_desk_id @property def app_install(self): return self.campaign_id.app_install def is_own(self): """Returns True if ad group entity belongs to the current user.""" advertiser_ids = REGISTRY.get('user_advertiser_ids') if advertiser_ids and AdGroup.objects.filter(campaign_id__advertiser_id__in=advertiser_ids, ad_group_id=self.ad_group_id).exists(): return True agency_ids = REGISTRY.get('user_agency_ids') if agency_ids and AdGroup.objects.filter(campaign_id__advertiser_id__agency_id__in=agency_ids, ad_group_id=self.ad_group_id).exists(): return True trading_desk_ids = REGISTRY.get('user_trading_desk_ids') if trading_desk_ids and AdGroup.objects.filter( campaign_id__advertiser_id__agency_id__trading_desk_id__in=trading_desk_ids, ad_group_id=self.ad_group_id).exists(): return True return False def autopopulate_by_ownership(self): pass @property def ads_enabled(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id=self.pk, status=STATUS_ENABLED) @property def ads_disapproved(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id=self.pk, adx_status=AD_STATUS_DISAPPROVED, status=STATUS_ENABLED) @property def a9_pending(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id=self.pk, a9_status=A9_STATUS_PENDING, status=STATUS_ENABLED) @property def a9_failed(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id=self.pk, a9_status=A9_STATUS_FAILED, status=STATUS_ENABLED) @property def ads_total(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id=self.pk) @property def black_lists_total(self): from restapi.models.BidderBlacklist import BidderBlacklistIds as model return model.objects.getCount(ad_group_id=self.pk) @property def white_lists_total(self): from restapi.models.BidderWhitelist import BidderWhitelistIds as model return model.objects.getCount(ad_group_id=self.pk) @property def custom_hints_total(self): from restapi.models.CustomHint import CustomHintIds as model return model.objects.getCount(ad_group_id=self.pk) @property def discrete_pricing_total(self): from restapi.models.DiscretePricing import DiscretePricingIds as model return model.objects.getCount(ad_group_id=self.pk) @property def ads_to_pause(self): """Returns a number of ads recommended to pause.""" from restapi.models.CreativePruning import CreativePruning return CreativePruning.objects.getCount(ad_id__ad_group_id=self.pk, action='pause') @property def ads_to_delete(self): """Returns a number of ads recommended to delete.""" from restapi.models.CreativePruning import CreativePruning return CreativePruning.objects.getCount(ad_id__ad_group_id=self.pk, action='delete') search_args = ('ad_group_id', 'ad_group__icontains') @property def search_result(self): campaign = self.campaign_id advertiser = campaign.advertiser_id agency = advertiser.agency_id result = {'level': 'adgroup', 'ad_group': self.ad_group, 'ad_group_id': self.ad_group_id, 'campaign': campaign.campaign, 'campaign_id': campaign.campaign_id, 'advertiser': advertiser.advertiser, 'advertiser_id': advertiser.advertiser_id, 'agency': agency.agency, 'agency_id': agency.agency_id, 'last_update': self.last_update} return result def __unicode__(self): return self.ad_group def get_fields_for_copy(self): rejected_fields = lambda x: x.__class__ is models.AutoField or x.name == 'pk' extra_fields = ['revmap_rev_type', 'revmap_rev_value', 'revmap_opt_type', 'revmap_opt_value', 'revmap_target_type', 'revmap_target_value'] fields = [f.name for f in self._meta.fields if not rejected_fields(f)] return fields + extra_fields def make_copy(self): values = [(f, getattr(self, f)) for f in self.get_fields_for_copy()] initials = dict([(k, v) for k, v in values if v is not None]) # pylint: disable=invalid-name m = self.__class__(**initials) # pylint: disable=invalid-name m.pk = None m.ad_group_id = None m.status = STATUS_PAUSED # 'paused' return m @property def parent_name(self): return self.campaign_id.campaign def save(self, *args, **kwargs): from restapi.audit_logger import AuditLogger AuditLogger.skip_next_write = True super(AdGroup, self).save(*args, **kwargs) if self._revmap is not None: from restapi.models.Revmap import Revmap revmap_qs = Revmap.objects.filter(ad_group_id=self) is_updated = False if revmap_qs.exists(): revmap = revmap_qs.first() else: revmap = Revmap(ad_group_id=self, ad_group=self.ad_group, campaign_id=self.campaign_id.pk, campaign=self.campaign_id.campaign) is_updated = True for field in self._revmap: old_value = getattr(revmap, field) new_value = self._revmap[field] if isinstance(new_value, Decimal): new_value = float(new_value) setattr(revmap, field, self._revmap[field]) if old_value != new_value: is_updated = True if is_updated: revmap.save() return super(AdGroup, self).save(*args, **kwargs) def as_dict(self): data = super(AdGroup, self).as_dict() for field in ('revmap_rev_type', 'revmap_rev_value', 'revmap_opt_type', 'revmap_opt_value', 'revmap_target_type', 'revmap_target_value'): data[field] = getattr(self, field) return data class Meta: managed = True unique_together = ('campaign_id', 'ad_group') db_table = 'ad_group' app_label = 'restapi' ordering = ('ad_group',)
class Advertiser(BaseModel): advertiser_id = models.AutoField(primary_key=True) advertiser = models.CharField(max_length=255) advertiser_key = models.CharField(max_length=40, default=ApiKeyGenerator.key_generator) agency_id = models.ForeignKey(Agency, db_column='agency_id', related_name='agency_advertisers') account_manager = models.IntegerField(max_length=10, default=0) notes = models.TextField(blank=True) encryption_key = models.BinaryField( max_length=16, blank=True, null=True, default=ApiKeyGenerator.random_string_16) encryption_iv = models.BinaryField( max_length=16, blank=True, null=True, default=ApiKeyGenerator.random_string_16) encryption_suffix = models.BinaryField( max_length=4, blank=True, null=True, default=ApiKeyGenerator.random_string_4) deleted = models.BooleanField(default=False, blank=True) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) zip = models.CharField(max_length=10, default='', blank=True) contact = models.CharField(max_length=100) address1 = models.CharField(max_length=255, default='', blank=True) address2 = models.CharField(max_length=255, default='', blank=True) city = models.CharField(max_length=100, default='', blank=True) state_prov = models.CharField(max_length=100, default='', blank=True) country = models.CharField(max_length=3, choices=COUNTRIES, default=COUNTRIES[0][0]) phone = models.CharField(max_length=20, default='', blank=True) email = models.CharField(max_length=255, default='', blank=True) flight_start_date = ZeroDateField(default=None, null=True, blank=True) flight_budget = models.DecimalField(max_digits=9, decimal_places=2, default=0) sampling_rate = models.DecimalField(max_digits=9, decimal_places=8, blank=True, null=True) throttling_rate = models.DecimalField(max_digits=9, decimal_places=8, blank=True, null=True) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) currency = models.CharField(max_length=3, default=CURRENCY_USD) twitter_margin = models.FloatField(default=0.15) discount = models.DecimalField(max_digits=9, decimal_places=2, default=0) objects = AdvertiserManager() objects_raw = models.Manager() permission_check = True actions = ('filter_by_trading_desk', ) def is_own(self): """Returns True if advertiser entity belongs to the current user.""" user_advertiser_ids = REGISTRY.get('user_advertiser_ids') if user_advertiser_ids and self.advertiser_id in user_advertiser_ids: return True user_agency_ids = REGISTRY.get('user_agency_ids') if user_agency_ids and self.agency_id_id in user_agency_ids: return True user_trading_desk_ids = REGISTRY.get('user_trading_desk_ids') if user_trading_desk_ids and Advertiser.objects.filter( agency_id__trading_desk_id__in=user_trading_desk_ids, advertiser_id=self.advertiser_id).exists(): return True return False def autopopulate_by_ownership(self): pass search_args = ('advertiser_id', 'advertiser__icontains') @property def search_result(self): return { 'level': 'advertiser', 'advertiser': self.advertiser, 'advertiser_id': self.advertiser_id, 'agency': self.agency_id.agency, 'agency_id': self.agency_id.agency_id, 'last_update': self.last_update } @property def agency(self): return self.agency_id.agency @property def trading_desk_id(self): return self.agency_id.trading_desk_id.trading_desk_id @property def trading_desk(self): return self.agency_id.trading_desk_id.trading_desk def __unicode__(self): return self.advertiser class Meta: # managed=False unique_together = ('agency_id', 'advertiser') db_table = 'advertiser' app_label = 'restapi' ordering = ('advertiser', )
class TwitterTailoredAudience(BaseModel): tw_targeting_id = models.BigIntegerField(primary_key=True) tw_account_id = models.ForeignKey(TwitterAccount, db_column='tw_account_id') name = models.CharField(max_length=255) audience_size = models.CharField(max_length=255) targetable_types = models.TextField(blank=True) audience_type = models.CharField(max_length=255) targetable = models.NullBooleanField() list_type = models.CharField(max_length=255) reasons_not_targetable = models.TextField(blank=True) partner_source = models.CharField(max_length=255) is_owner = models.NullBooleanField() permission_level = models.CharField(max_length=255) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None) objects = TwitterTailoredAudienceManager() objects_raw = models.Manager() # permission_check = True search_args = ( 'name', 'tw_account_id', ) def __unicode__(self): return self.name class Meta: db_table = 'tw_tailored_audience' app_label = 'restapi' @classmethod def fetch_tailored_audience(self, data, syncData=False, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): res = {} res['data'] = [] res['success'] = False account_id = data.get('account_id') if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) resource = '/{api_version}/accounts/{account_id}/tailored_audiences?count=1000'.format( api_version=settings.TW_API_VERSION, account_id=account_id) response = Request(client, 'get', resource).perform() tailored_audiences = response.body['data'] next_cursor = None if response.body['next_cursor'] and response.body[ 'next_cursor'] is not 0: next_cursor = response.body['next_cursor'] while next_cursor is not 0: resource = '/{api_version}/accounts/{account_id}/tailored_audiences?cursor={next_cursor}&count=1000'.format( api_version=settings.TW_API_VERSION, account_id=account_id, next_cursor=next_cursor) response = Request(client, 'get', resource).perform() next_cursor = response.body['next_cursor'] or 0 tailored_audiences += response.body['data'] api_domain = 'https://ads.twitter.com' resource = '/accounts/{account_id}/apps.json'.format( account_id=account_id) response = Request(client, 'get', resource, domain=api_domain).perform() apps = response.body.get('apps_info', {}) app_table = [] for (k, v) in apps.iteritems(): app_store_identifier = v.get('app_store_identifier') app_name = v.get('title') if app_store_identifier and app_name: app_table.append({ 'id': app_store_identifier, 'name': app_name }) def replace_app_name(app_name): for app_info in app_table: app_name = app_name.replace(app_info['id'], app_info['name']) return app_name.replace('_', ' ') def human_format(num): magnitude = 0 while abs(num) >= 1000: magnitude += 1 num /= 1000.0 # add more suffixes if you need them return '%.2f%s' % (num, ['', 'K', 'M', 'G', 'T', 'P' ][magnitude]) for audience in tailored_audiences: audience['name'] = replace_app_name(audience['name']) if audience['audience_size']: audience['audience_size'] = human_format( audience['audience_size']) audience['name'] = '%s (%s users)' % ( audience['name'], audience['audience_size']) res['success'] = True res['data'] = tailored_audiences except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} if syncData and res['data'] and res['success']: res['sync'] = {} if isinstance(res['data'], (list, tuple)): sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 for index, audience in enumerate(res['data'], start=0): audience_res = self.sync_tailored_audience( account_id_int, audience) if 'success' in audience_res and audience_res[ 'success'] is True: if audience_res['type'] == 'existing': existing_count += 1 if audience_res['type'] == 'new': new_count += 1 sync_success += 1 elif 'success' in audience_res and audience_res[ 'success'] is False: sync_fail += 1 res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['total'] = sync_success if sync_fail == 0: res['sync']['success'] = True else: res['sync']['success'] = False elif isinstance(res['data'], dict): audience_res = self.sync_tailored_audience( account_id_int, res['data']) if 'success' in audience_res and audience_res[ 'success'] is True: res['data'] = audience_res['data'] res['sync']['success'] = audience_res['success'] res['sync']['type'] = {} res['sync']['type'][audience_res['type']] = 1 res['sync']['total'] = 1 elif 'success' in audience_res and audience_res[ 'success'] is False: res['data'] = audience_res['data'] res['sync']['success'] = audience_res['success'] res['sync']['message'] = audience_res['message'] return res @classmethod def sync_tailored_audience(self, tw_account_id, data, manage_campaign_id=None): res = {} res['data'] = data res['success'] = False res['os_platform'] = 'web' tailored_audience_id_int = base36_to_int(data['id']) if isinstance(tw_account_id, TwitterAccount): m_tw_account = tw_account_id else: m_tw_account = TwitterAccount.objects_raw.get( tw_account_id=tw_account_id) if m_tw_account is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Campaign Cannot find Twitter Account" } return res try: res['type'] = 'existing' m_tw_tailored_audience = TwitterTailoredAudience.objects_raw.get( tw_account_id=m_tw_account, tw_targeting_id=tailored_audience_id_int) except TwitterTailoredAudience.DoesNotExist: res['type'] = 'new' m_tw_tailored_audience = TwitterTailoredAudience( tw_account_id=m_tw_account, tw_targeting_id=tailored_audience_id_int) if m_tw_tailored_audience is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Tailored Audience" } return res m_tw_tailored_audience.name = data['name'] m_tw_tailored_audience.targetable = data['targetable'] m_tw_tailored_audience.audience_size = data['audience_size'] m_tw_tailored_audience.targetable_types = json.dumps( data['targetable_types']) m_tw_tailored_audience.audience_type = data['audience_type'] m_tw_tailored_audience.targetable = data['targetable'] m_tw_tailored_audience.list_type = data['list_type'] m_tw_tailored_audience.reasons_not_targetable = json.dumps( data['reasons_not_targetable']) m_tw_tailored_audience.partner_source = data['partner_source'] m_tw_tailored_audience.is_owner = data['is_owner'] m_tw_tailored_audience.permission_level = data['permission_level'] m_tw_tailored_audience.last_update = data['updated_at'] if data['deleted'] == True: m_tw_tailored_audience.status = STATUS_ARCHIVED m_tw_tailored_audience.save_raw() res['success'] = True return res
class TwitterCampaign(BaseModel): tw_campaign_id = models.BigIntegerField(primary_key=True) tw_account_id = models.ForeignKey(TwitterAccount, db_column='tw_account_id') campaign_id = models.ForeignKey(Campaign, db_column='campaign_id') name = models.CharField(max_length=255) funding_instrument_id = models.CharField(max_length=255) start_time = ZeroDateTimeField(default=None, null=True, blank=True) end_time = ZeroDateTimeField(default=None, null=True, blank=True) standard_delivery = models.NullBooleanField() frequency_cap = models.IntegerField() duration_in_days = models.CharField(max_length=8, choices=[(1, 1), (7, 7), (30, 30)], default=7) total_budget_amount_local_micro = models.BigIntegerField(max_length=20) daily_budget_amount_local_micro = models.BigIntegerField(max_length=20) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterCampaignManager() objects_raw = models.Manager() # permission_check = True search_args = ( 'name__icontains', 'tw_campaign_id', ) @property def search_result(self): advertiser = self.campaign_id.advertiser_id result = { 'level': 'twittercampaign', 'campaign': self.campaign_id.campaign, 'campaign_id': self.campaign_id.campaign_id, 'tw_campaign': "%s / %s" % (self.tw_account_id.name, self.name), 'tw_campaign_id': self.tw_campaign_id, 'advertiser': advertiser.advertiser, 'advertiser_id': advertiser.advertiser_id, 'last_update': self.last_update } return result @property def _advertiser_id(self): return self.campaign_id.advertiser_id.advertiser_id @property def _campaign_id(self): return self.campaign_id.campaign_id def __unicode__(self): return self.name class Meta: db_table = 'tw_campaign' app_label = 'restapi' @classmethod def fetch_campaigns(self, data, syncData=False, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): res = {} res['data'] = [] res['success'] = False account_id = data.get('account_id') campaign_id = data.get('campaign_id') tw_campaign_id = data.get('tw_campaign_id') if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if isinstance(tw_campaign_id, (int, long)): tw_campaign_id_int = tw_campaign_id tw_campaign_id = int_to_base36(tw_campaign_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) # If Manage Campaign ID passed, fetch all Campaigns and find Manage Campaign if campaign_id: resource = '/{api_version}/accounts/{account_id}/campaigns?count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, 'get', resource).perform() if response.headers[ 'x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT: send_twitter_alert_email({ "account_id": account_id, "endpoint": resource }) for campaign in response.body['data']: campaign_id_match = re.findall(r'\s*{(\d+)}\s*$', campaign['name'])[:1] if campaign_id_match and campaign_id_match[0] and int( campaign_id_match[0]) == int(campaign_id): res['data'].append(campaign) res['success'] = True else: if tw_campaign_id: resource = '/{api_version}/accounts/{account_id}/campaigns/{tw_campaign_id}?count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, tw_campaign_id=tw_campaign_id) else: resource = '/{api_version}/accounts/{account_id}/campaigns?count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, 'get', resource).perform() if response.headers[ 'x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT: send_twitter_alert_email({ "account_id": account_id, "endpoint": resource }) res['data'] = response.body['data'] next_cursor = None if response.body['next_cursor'] and response.body[ 'next_cursor'] is not 0: next_cursor = response.body['next_cursor'] while next_cursor is not 0: if tw_campaign_id: resource = '/{api_version}/accounts/{account_id}/campaigns/{tw_campaign_id}?cursor={next_cursor}&count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, tw_campaign_id=tw_campaign_id, next_cursor=next_cursor) else: resource = '/{api_version}/accounts/{account_id}/campaigns?cursor={next_cursor}&count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, next_cursor=next_cursor) response = Request(client, 'get', resource).perform() next_cursor = response.body['next_cursor'] or 0 res['data'] += response.body['data'] res['success'] = True except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} if syncData and res['data'] and res['success']: res['sync'] = {} if isinstance(res['data'], (list, tuple)): sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 for index, api_campaign in enumerate(res['data'], start=0): campaign_res = self.sync_campaign(account_id_int, api_campaign) if 'success' in campaign_res and campaign_res[ 'success'] is True: if campaign_res['type'] == 'existing': existing_count += 1 if campaign_res['type'] == 'new': new_count += 1 sync_success += 1 elif 'success' in campaign_res and campaign_res[ 'success'] is False: sync_fail += 1 res['os_platform'] = campaign_res['os_platform'] res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['total'] = sync_success if sync_fail == 0: res['sync']['success'] = True else: res['sync']['success'] = False elif isinstance(res['data'], dict): campaign_res = self.sync_campaign(account_id_int, res['data']) if 'success' in campaign_res and campaign_res[ 'success'] is True: res['data'] = campaign_res['data'] res['os_platform'] = campaign_res['os_platform'] res['sync']['success'] = campaign_res['success'] res['sync']['type'] = {} res['sync']['type'][campaign_res['type']] = 1 res['sync']['total'] = 1 elif 'success' in campaign_res and campaign_res[ 'success'] is False: res['data'] = campaign_res['data'] res['sync']['success'] = campaign_res['success'] res['sync']['message'] = campaign_res['message'] return res @classmethod def create_campaign(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_update_campaign(data, oauth_token, oauth_token_secret, "post") @classmethod def update_campaign(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_update_campaign(data, oauth_token, oauth_token_secret, "put") @classmethod def create(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_campaign(data, oauth_token, oauth_token_secret) @classmethod def update(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.update_campaign(data, oauth_token, oauth_token_secret) @classmethod def create_update_campaign(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET, request_type="post"): res = {} res['sync'] = {} res['success'] = False account_id = data.get('account_id') campaign_id = data.get('tw_campaign_id') if isinstance(campaign_id, (int, long)): campaign_id_int = campaign_id campaign_id = int_to_base36(campaign_id) if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res if request_type == 'put': if campaign_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Campaign ID" } return res params = {} params['daily_budget_amount_local_micro'] = data.get( 'daily_budget_amount_local_micro', None) params['duration_in_days'] = data.get('duration_in_days', None) params['end_time'] = data.get('end_time', None) params['frequency_cap'] = data.get('frequency_cap', None) params['funding_instrument_id'] = data.get('funding_instrument_id', None) params['name'] = data.get('name', None) if data.get('paused', None) is not None: params['paused'] = 'true' if data.get('paused') else 'false' params['standard_delivery'] = str(data.get('standard_delivery')).lower( ) if data.get('standard_delivery') else None params['start_time'] = data.get('start_time', None) params['total_budget_amount_local_micro'] = data.get( 'total_budget_amount_local_micro', None) # total_budget_amount_local_micro = 0 is not permitted if not params['total_budget_amount_local_micro']: params['total_budget_amount_local_micro'] = None params = dict((k, v) for k, v in params.iteritems() if v is not None and v is not "") client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) if request_type == 'put': resource = '/{api_version}/accounts/{account_id}/campaigns/{campaign_id}'.format( api_version=settings.TW_API_VERSION, account_id=account.id, campaign_id=campaign_id) elif request_type == 'post': resource = '/{api_version}/accounts/{account_id}/campaigns'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, request_type, resource, params=params).perform() if response.code == 200 or response.code == 201: res['success'] = True res['data'] = response.body['data'] if res['data'] and res['success']: campaign_res = self.sync_campaign(account_id_int, res['data']) if 'success' in campaign_res and campaign_res[ 'success'] is True: res['data'] = campaign_res['data'] res['sync']['success'] = campaign_res['success'] res['sync']['type'] = {} res['sync']['type'][campaign_res['type']] = 1 res['sync']['total'] = 1 elif 'success' in campaign_res and campaign_res[ 'success'] is False: res['data'] = campaign_res['data'] res['sync']['success'] = campaign_res['success'] res['sync']['message'] = campaign_res['message'] except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} return res @classmethod def batch_create(self, data, account_id, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.batch_create_update_campaign(data, account_id, oauth_token, oauth_token_secret, "post") @classmethod def batch_update(self, data, account_id, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.batch_create_update_campaign(data, account_id, oauth_token, oauth_token_secret, "put") @classmethod def batch_create_update_campaign( self, data, account_id, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET, request_type="post"): res = {} res['success'] = False campaign_id_int = None if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if account_id is None: res = { 'data': [], 'success': False, 'message': "Missing Twitter Account ID" } return res if isinstance(data, (list, tuple)): post_data = [] for campaign in data: campaign_data = {} params = {} params['daily_budget_amount_local_micro'] = campaign.get( 'daily_budget_amount_local_micro', None) params['duration_in_days'] = campaign.get( 'duration_in_days', None) params['end_time'] = campaign.get('end_time', None) params['frequency_cap'] = campaign.get('frequency_cap', None) params['funding_instrument_id'] = campaign.get( 'funding_instrument_id', None) params['name'] = campaign.get('name', None) if campaign.get('paused', None) is not None: params['paused'] = 'true' if campaign.get( 'paused') else 'false' params['standard_delivery'] = str( campaign.get('standard_delivery')).lower() if campaign.get( 'standard_delivery') else None params['start_time'] = campaign.get('start_time', None) params['total_budget_amount_local_micro'] = campaign.get( 'total_budget_amount_local_micro', None) # total_budget_amount_local_micro = 0 is not permitted if not params['total_budget_amount_local_micro']: params['total_budget_amount_local_micro'] = None params = dict((k, v) for k, v in params.iteritems() if v is not None and v is not "") if request_type == 'post': campaign_data['operation_type'] = 'Create' if request_type == 'put': campaign_data['operation_type'] = 'Update' campaign_data['params'] = params post_data.append(campaign_data) client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX # Split up requests into batches of 20 batch = [] batches = [] for x in range(0, len(post_data), 20): batch = post_data[x:x + 20] batches.append(batch) success_batch = [] error_batch = [] error_details = [] success = False error = False for batch_post in batches: try: account = client.accounts(account_id) headers = {"Content-Type": "application/json"} resource = '/{api_version}/batch/accounts/{account_id}/campaigns'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, 'post', resource, body=json.dumps(batch_post), headers=headers).perform() if response.code == 200 or response.code == 201: success = True success_batch.extend(response.body['data']) except Error as e: error = True if e.response.body.get('operation_errors', None) is not None: for err in e.response.body.get('operation_errors'): if err: if isinstance(err, dict): err = [err] error_details.extend(err) if e.response.body.get('errors', None) is not None: for err in e.response.body.get('errors'): if err: if isinstance(err, dict): err = [err] error_details.extend(err) error_batch.extend(batch_post) except Exception as e: res = {'data': [], 'success': False, 'message': str(e)} error_batch.extend(batch_post) if success_batch and success: res['sync'] = {} if isinstance(success_batch, dict): success_batch = [success_batch] sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 for index, api_campaign in enumerate(success_batch, start=0): campaign_res = self.sync_campaign(account_id_int, api_campaign) if 'success' in campaign_res and campaign_res[ 'success'] is True: if campaign_res['type'] == 'existing': existing_count += 1 if campaign_res['type'] == 'new': new_count += 1 sync_success += 1 elif 'success' in campaign_res and campaign_res[ 'success'] is False: sync_fail += 1 res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['total'] = sync_success if sync_fail == 0: res['sync']['success'] = True else: res['sync']['success'] = False res['success'] = success res['count'] = {} res['count']['success'] = len(success_batch) res['count']['total'] = len(data) res['count']['error'] = len(error_batch) res['data'] = success_batch if error: res['success'] = False res['error'] = {} res['error']['data'] = error_batch res['error']['messages'] = filter(None, error_details) return res elif isinstance(data, dict): if request_type == 'post': return self.create(data, oauth_token, oauth_token_secret) if request_type == 'put': return self.update(data, oauth_token, oauth_token_secret) @classmethod def sync_campaign(self, tw_account_id, data, manage_campaign_id=None): res = {} res['data'] = data res['success'] = False res['os_platform'] = 'web' campaign_id_int = base36_to_int(data['id']) if isinstance(tw_account_id, TwitterAccount): m_tw_account = tw_account_id else: m_tw_account = TwitterAccount.objects_raw.get( tw_account_id=tw_account_id) if m_tw_account is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Campaign Cannot find Twitter Account" } return res try: res['type'] = 'existing' m_tw_campaign = TwitterCampaign.objects_raw.get( tw_account_id=m_tw_account, tw_campaign_id=campaign_id_int) except TwitterCampaign.DoesNotExist: res['type'] = 'new' m_tw_campaign = TwitterCampaign(tw_account_id=m_tw_account, tw_campaign_id=campaign_id_int) if m_tw_campaign is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Campaign" } return res # Get Manage Campaign ID from Twitter Campaign Name if manage_campaign_id is None: campaign_id_match = re.findall(r'\s*{(\d+)}\s*$', data['name'])[:1] if campaign_id_match and campaign_id_match[0]: manage_campaign_id = campaign_id_match[0] if manage_campaign_id: try: m_campaign = Campaign.objects_raw.get( campaign_id=manage_campaign_id) except Campaign.DoesNotExist: m_campaign = None if m_campaign is not None: m_tw_campaign.campaign_id = m_campaign res['data']['manage_campaign_id'] = manage_campaign_id if m_campaign.targeting: targeting = json.loads(m_campaign.targeting) if targeting and 'os' in targeting and targeting['os']: os_platform = targeting['os'] if os_platform == 'iOS': os_platform = 'iphone' res['os_platform'] = os_platform m_tw_campaign.name = data['name'] m_tw_campaign.funding_instrument_id = data[ 'funding_instrument_id'] m_tw_campaign.total_budget_amount_local_micro = data[ 'total_budget_amount_local_micro'] m_tw_campaign.daily_budget_amount_local_micro = data[ 'daily_budget_amount_local_micro'] m_tw_campaign.duration_in_days = data['duration_in_days'] m_tw_campaign.standard_delivery = data['standard_delivery'] if data['start_time'] is not None: m_tw_campaign.start_time = data['start_time'] if data['end_time'] is not None: m_tw_campaign.end_time = data['end_time'] if data['paused'] == True or data['servable'] is False: m_tw_campaign.status = STATUS_PAUSED elif data['paused'] == False or data['servable'] is True: m_tw_campaign.status = STATUS_ENABLED if data['deleted'] == True: m_tw_campaign.status = STATUS_ARCHIVED if data['duration_in_days'] is not None: m_tw_campaign.duration_in_days = data['duration_in_days'] if data['frequency_cap'] is not None: m_tw_campaign.frequency_cap = data['frequency_cap'] m_tw_campaign.save_raw() res['success'] = True else: res['success'] = False res['message'] = 'Missing Manage Campaign ID' return res
class TwitterAppCard(BaseModel): tw_app_card_id = models.BigIntegerField(primary_key=True) tw_account_id = models.ForeignKey(TwitterAccount, db_column='tw_account_id') name = models.CharField(max_length=255) card_type = models.CharField(max_length=20, choices=TW_CARD_TYPE, default='app_card') preview_url = models.CharField(max_length=255) app_country_code = models.CharField(max_length=255) iphone_app_id = models.CharField(max_length=255) ipad_app_id = models.CharField(max_length=255) googleplay_app_id = models.CharField(max_length=255) deep_link = models.CharField(max_length=255) custom_cta = models.CharField(max_length=255, choices=TW_CUSTOM_CTA) custom_icon_media_id = models.CharField(max_length=255) custom_app_description = models.CharField(max_length=255) image_media_id = models.CharField(max_length=255) wide_app_image = models.CharField(max_length=255) wide_app_image_media_id = models.CharField(max_length=255) video_id = models.CharField(max_length=255) video_url = models.CharField(max_length=255) video_poster_url = models.CharField(max_length=255) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterAppCardManager() objects_raw = models.Manager() # permission_check = True def __unicode__(self): return self.name class Meta: db_table = 'tw_app_card' app_label = 'restapi' @classmethod def fetch_app_cards(self, data, syncData=False, oauth_token=settings.TW_ACCESS_TOKEN, oauth_secret=settings.TW_ACCESS_SECRET): res = {} account_id = data.get('account_id') if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) else: account_id_int = base36_to_int(account_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: res['data'] = [] account = client.accounts(account_id) resource = '/{api_version}/accounts/{account_id}/cards/app_download'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, 'get', resource).perform() for card in response.body['data']: res['data'].append(card) resource = '/{api_version}/accounts/{account_id}/cards/image_app_download'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, 'get', resource).perform() for card in response.body['data']: res['data'].append(card) resource = '/{api_version}/accounts/{account_id}/cards/video_app_download'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, 'get', resource).perform() if response.headers[ 'x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT: send_twitter_alert_email({ "account_id": account_id, "endpoint": resource }) for card in response.body['data']: res['data'].append(card) res['success'] = True except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} if syncData and res['data'] and res['success']: res['sync'] = {} if isinstance(res['data'], (list, tuple)): sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 for index, api_app_card in enumerate(res['data'], start=0): app_card_res = self.sync_app_card(account_id_int, api_app_card) if 'success' in app_card_res and app_card_res[ 'success'] is True: if app_card_res['type'] == 'existing': existing_count += 1 if app_card_res['type'] == 'new': new_count += 1 sync_success += 1 elif 'success' in app_card_res and app_card_res[ 'success'] is False: sync_fail += 1 res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['total'] = sync_success if sync_fail == 0: res['sync']['success'] = True else: res['sync']['success'] = False elif isinstance(res['data'], dict): app_card_res = self.sync_app_card(account_id_int, res['data']) if 'success' in app_card_res and app_card_res[ 'success'] is True: res['data'] = app_card_res['data'] res['sync']['success'] = app_card_res['success'] res['sync']['type'] = {} res['sync']['total'] = 1 res['sync']['type'][app_card_res['type']] = 1 # sync_success if 'success' in app_card_res and app_card_res[ 'success'] is False: res['data'] = app_card_res['data'] res['sync']['success'] = app_card_res['success'] res['sync']['message'] = app_card_res['message'] return res @classmethod def sync_app_card(self, tw_account_id, data): res = {} res['data'] = data res['success'] = False appcard_id_int = base36_to_int(data['id']) if isinstance(tw_account_id, TwitterAccount): m_tw_account = tw_account_id else: m_tw_account = TwitterAccount.objects_raw.filter( tw_account_id=tw_account_id).first() if m_tw_account is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter AppCard. Cannot find Twitter Account" } return res try: res['type'] = 'existing' m_tw_appcard = TwitterAppCard.objects_raw.get( tw_account_id=m_tw_account, tw_app_card_id=appcard_id_int) except TwitterAppCard.DoesNotExist: res['type'] = 'new' m_tw_appcard = TwitterAppCard(tw_account_id=m_tw_account, tw_app_card_id=appcard_id_int) if m_tw_appcard is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter AppCard" } return res if data['name']: m_tw_appcard.name = data['name'] if data['card_type'] == 'IMAGE_APP_DOWNLOAD': m_tw_appcard.card_type = 'image' elif data['card_type'] == 'APP_DOWNLOAD': m_tw_appcard.card_type = 'app_card' elif data['card_type'] == 'VIDEO_APP_DOWNLOAD': m_tw_appcard.card_type = 'video' if data['video_url']: m_tw_appcard.video_url = data['video_url'] if data['preview_url']: m_tw_appcard.preview_url = data['preview_url'] if data['app_country_code']: m_tw_appcard.app_country_code = data['app_country_code'] if 'iphone_app_id' in data and data['iphone_app_id']: m_tw_appcard.iphone_app_id = data['iphone_app_id'] if 'ipad_app_id' in data and data['ipad_app_id']: m_tw_appcard.iphone_app_id = data['ipad_app_id'] if 'googleplay_app_id' in data and data['googleplay_app_id']: m_tw_appcard.iphone_app_id = data['googleplay_app_id'] if 'deep_link' in data and data['deep_link']: m_tw_appcard.deep_link = data['deep_link'] if 'app_cta' in data and data['app_cta']: m_tw_appcard.custom_cta = data['app_cta'] if 'custom_icon_media_id' in data and data['custom_icon_media_id']: m_tw_appcard.custom_icon_media_id = data['custom_icon_media_id'] if 'custom_app_description' in data and data['custom_app_description']: m_tw_appcard.custom_app_description = data[ 'custom_app_description'] if 'image_media_id' in data and data['image_media_id']: m_tw_appcard.image_media_id = data['image_media_id'] if 'wide_app_image_media_id' in data and data[ 'wide_app_image_media_id']: m_tw_appcard.wide_app_image_media_id = data[ 'wide_app_image_media_id'] if 'wide_app_image' in data and data['wide_app_image']: m_tw_appcard.wide_app_image = data['wide_app_image'] if 'video_id' in data and data['video_id']: m_tw_appcard.video_id = data['video_id'] if 'video_url' in data and data['video_url']: m_tw_appcard.video_url = data['video_url'] if 'video_poster_url' in data and data['video_poster_url']: m_tw_appcard.video_poster_url = data['video_poster_url'] if data['deleted'] == False: m_tw_appcard.status = STATUS_ENABLED if data['deleted'] == True: m_tw_appcard.status = STATUS_ARCHIVED m_tw_appcard.save_raw() res['success'] = True return res
class Agency(BaseModel): agency_id = models.AutoField(primary_key=True) trading_desk_id = models.ForeignKey(TradingDesk, db_column='trading_desk_id', related_name='trading_desk_agencies') agency = models.CharField(max_length=255) agency_key = models.CharField(max_length=40, default=ApiKeyGenerator.key_generator) account_manager = models.IntegerField(max_length=10, default=0) currency = models.CharField(max_length=3, default=CURRENCY_USD) contact = models.CharField(max_length=100) address1 = models.CharField(max_length=255, default='', blank=True) address2 = models.CharField(max_length=255, default='', blank=True) city = models.CharField(max_length=100, default='', blank=True) state_prov = models.CharField(max_length=100, default='', blank=True) zip = models.CharField(max_length=10, default='', blank=True) country = models.CharField(max_length=3, choices=COUNTRIES, default=COUNTRIES[0][0]) phone = models.CharField(max_length=20, default='', blank=True) email = models.CharField(max_length=255, default='', blank=True) notes = models.TextField(blank=True, default='') status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_CHOICES[1][0]) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = AgencyManager() objects_raw = models.Manager() permission_check = True actions = ('filter_by_trading_desk',) def is_own(self): """Returns True if agency entity belongs to the current user.""" user_advertiser_ids = REGISTRY.get('user_advertiser_ids') if user_advertiser_ids and Agency.objects.filter(agency_advertisers__advertiser_id__in=user_advertiser_ids, agency_id=self.agency_id).exists(): return True user_agency_ids = REGISTRY.get('user_agency_ids') if user_agency_ids and self.agency_id in user_agency_ids: return True user_trading_desk_ids = REGISTRY.get('user_trading_desk_ids') if user_trading_desk_ids and self.trading_desk_id in user_trading_desk_ids: return True return False def autopopulate_by_ownership(self): trading_desk = REGISTRY['user'].profile.trading_desk.first() self.trading_desk_id = trading_desk self.currency = trading_desk.currency def __unicode__(self): return self.agency search_args = ('agency_id', 'agency__icontains') @property def trading_desk(self): return self.trading_desk_id.trading_desk @property def search_result(self): return { 'level': 'agency', 'agency': self.agency, 'agency_id': self.agency_id, 'trading_desk_id': self.trading_desk_id.pk, 'last_update': self.last_update } class Meta: # managed=False unique_together = ('trading_desk_id', 'agency') db_table = 'agency' app_label = 'restapi' ordering = ('agency',)
class CustomHint(BaseModel): id = models.AutoField(primary_key=True) size = models.CharField(max_length=8, choices=CUSTOM_HINT_SIZE_CHOICES, default=SIZE_ALL) placement_type = models.CharField(max_length=4, choices=PLACEMENT_TYPE_CHOICES, default=PLACEMENT_TYPE_ALL) placement_id = models.CharField(max_length=128, default='all') inflator_type = models.CharField(max_length=8, choices=INFLATOR_TYPE_CHOICES, null=True, default=None) inflator = models.DecimalField(max_digits=9, decimal_places=6, null=True, default=None) priority = models.IntegerField(default=0) max_frequency = models.IntegerField(default=0) frequency_interval = models.IntegerField(default=0) start_date = WideRangeDateTimeField(default=timezone( settings.TIME_ZONE).localize(datetime(datetime.now().year, 1, 1))) end_date = WideRangeDateTimeField(default=timezone( settings.TIME_ZONE).localize(datetime(datetime.now().year, 12, 31))) tag = models.CharField(max_length=128, blank=True, null=True, default=None) last_update = DateTimeField(default=None, auto_now=True) advertiser_id = models.IntegerField() campaign_id = models.ForeignKey(Campaign, db_column='campaign_id', null=True, db_constraint=False, on_delete=models.DO_NOTHING) campaign = models.CharField(max_length=255) ad_group_id = models.ForeignKey(AdGroup, db_column='ad_group_id', null=True, db_constraint=False, on_delete=models.DO_NOTHING) ad_group = models.CharField(max_length=255) ad_id = models.IntegerField() # pylint: disable=invalid-name ad = models.CharField(max_length=255) source_id = models.IntegerField() source = models.CharField(max_length=255) objects = CustomHintManager() objects_raw = models.Manager() permission_check = True def __unicode__(self): return self.placement_id def is_own(self): return PlacementHelper.is_own(self.campaign_id, self.ad_group_id) def autopopulate_by_ownership(self): pass class Meta: managed = False db_table = 'custom_hint_view' app_label = 'restapi' ordering = ['-last_update'] unique_together = ('campaign_id', 'ad_group_id', 'ad_id', 'source_id', 'size', 'placement_type', 'placement_id')
class User(BaseModel, UserPermissionsMixin, PermissionsMixin): user_id = models.AutoField(primary_key=True, db_column='id') username = models.CharField(max_length=75, blank=False, unique=True) first_name = models.CharField(max_length=30, blank=True) last_name = models.CharField(max_length=30, blank=True) email = models.CharField(max_length=75, blank=False) is_staff = models.BooleanField(default=1) is_active = models.BooleanField(default=1) date_joined = DateTimeField(auto_now_add=True, null=True, default=None) password = models.CharField(max_length=128) last_login = models.DateTimeField(null=True) objects = UserManager() objects_raw = UserDefaultManager() permission_check = True actions = ['filter_by_trading_desk'] # dummy field for UserSerializer.reset_password_url reset_password_url = None def is_own(self): if self.pk is None: return True user = REGISTRY.get('user') if not user: return False trading_desk = self.profile.trading_desk.first() advertiser = self.profile.advertisers.first() if not advertiser and trading_desk: user_trading_desk = user.trading_desk.first() return user_trading_desk and trading_desk.pk == user_trading_desk.pk return user.pk == self.pk USERNAME_FIELD = 'username' REQUIRED_FIELDS = ('email', ) def __unicode__(self): return self.email @property def user_in_groups(self): return [x.name for x in self.groups.all()] @property def user_in_trading_desks(self): profile = self.profile trading_desks = TradingDesk.objects_raw.filter( trading_desk_userprofiles=profile) return [x.trading_desk for x in trading_desks] @property def full_name(self): return ('%s %s' % (self.first_name, self.last_name)).strip() @property def trading_desk(self): trading_desks = self.profile.trading_desk td = trading_desks.first() if td is not None: return td.trading_desk else: return None @property def trading_desk_id(self): trading_desks = self.profile.trading_desk td = trading_desks.first() if td is not None: return td.trading_desk_id else: return None @property def trading_desk_key(self): trading_desks = self.profile.trading_desk td = trading_desks.first() if td is not None: return td.trading_desk_key else: return None @property def is_manage_user(self): """Returns True if user is Manage.com user.""" try: return self.profile.trading_desk.filter( pk=MANAGE_TRADING_DESK_ID).exists() except base.PermissionDeniedException: return False def get_short_name(self): return self.username def get_username(self): """Returns the identifying username for this User.""" return getattr(self, self.USERNAME_FIELD) def __str__(self): return self.get_username() def natural_key(self): return (self.get_username(), ) def is_anonymous(self): """ Always returns False. This is a way of comparing User objects to anonymous users. """ return False def is_authenticated(self): """ Always return True. This is a way to tell if the user has been authenticated in templates. """ return True def set_password(self, raw_password): self.password = make_password(raw_password) def check_password(self, raw_password): """ Returns a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) self.save(update_fields=["password"]) return check_password(raw_password, self.password, setter) def set_unusable_password(self): # Sets a value that will never be a valid hash self.password = make_password(None) def has_usable_password(self): return is_password_usable(self.password) def get_session_auth_hash(self): """ Returns an HMAC of the password field. """ key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" return salted_hmac(key_salt, self.password).hexdigest() def get_reset_password_hash(self): key_salt = self.trading_desk_key data = "%s:%s:%s" % (self.pk, self.password, self.is_active) return salted_hmac(key_salt, data).hexdigest() def as_dict(self, difference_from=None): data = super(User, self).as_dict() del data['last_login'] del data['date_joined'] data['user_in_groups'] = self.user_in_groups data['user_in_trading_desks'] = self.user_in_trading_desks return data class Meta: swappable = 'AUTH_USER_MODEL' db_table = 'auth_user' app_label = 'restapi'
class CreativePruning(BaseModel): ad_id = models.ForeignKey(Ad, db_column='ad_id', primary_key=True) reason = models.CharField(max_length=255) action = models.CharField(max_length=40, choices=CREATIVE_PRUNING_STATUS_CHOICES, default='pause') ibid = models.FloatField() impression = models.FloatField(help_text=_( 'Number of times an actual ad was served to the publisher.')) win_rate = models.FloatField() click = models.FloatField() ctr = models.FloatField(verbose_name=_('CTR')) install = models.FloatField() # Install rate. ir = models.FloatField( verbose_name=_('IR'), help_text=_('Install rate = (Installs / Impressions) * 100')) ipm = models.FloatField() cpm = models.FloatField() rpm = models.FloatField(verbose_name=_('RPM')) ppm = models.FloatField(verbose_name=_('PPM')) cpi = models.FloatField() rpi = models.FloatField() cost = models.FloatField() revenue = models.FloatField() profit = models.FloatField() margin = models.FloatField() status = models.CharField(max_length=63) # Install rank. install_rank = models.FloatField() ir_rank = models.FloatField() rpm_rank = models.FloatField() ctr_rank = models.FloatField() ibid_rank = models.FloatField() # IPM rank. ipm_rank = models.FloatField(help_text='IPM Rank', verbose_name='IPM Rank') revenue_rank = models.FloatField() profit_rank = models.FloatField() # Impression rank. impression_rank = models.FloatField(help_text='Impression Rank') days = models.BigIntegerField(max_length=20) days_w_impression = models.BigIntegerField( max_length=20, verbose_name=_('Days with impression')) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = CreativePruningManager() objects_raw = models.Manager() # permission_check = True def __unicode__(self): return unicode(self.ad_id) class Meta: db_table = 'creative_pruning' app_label = 'restapi' @property def ad(self): return self.ad_id @property def ad_group_id(self): return self.ad_id.ad_group_id.ad_group_id @property def ad_group(self): return self.ad_id.ad_group_id.ad_group @property def campaign_id(self): return self.ad_id.ad_group_id.campaign_id.campaign_id @property def campaign(self): return self.ad_id.ad_group_id.campaign_id.campaign @property def advertiser_id(self): return self.ad_id.ad_group_id.campaign_id.advertiser_id.advertiser_id @property def advertiser(self): return self.ad_id.ad_group_id.campaign_id.advertiser_id.advertiser
class Event(BaseModel): event_id = models.AutoField(primary_key=True) campaign_id = models.ForeignKey(Campaign, db_column='campaign_id', related_name='events') event = models.CharField(max_length=255) description = models.TextField(blank=True, default='') default_args = models.TextField(blank=True, default='') base_event_id = models.IntegerField(blank=True, default=0) deleted = models.BooleanField(default=False, blank=True) max_frequency = models.IntegerField(blank=True, null=True) show_in_stats = models.BooleanField(default=True) frequency_interval = models.IntegerField(blank=True, null=True) accept_unencrypted = models.BooleanField(default=False, blank=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = EventManager() objects_raw = models.Manager() permission_check = True def is_own(self): """Returns True if event entity belongs to the current user.""" advertiser_ids = REGISTRY.get('user_advertiser_ids') if advertiser_ids and Event.objects.filter( campaign_id__advertiser_id__in=advertiser_ids, event_id=self.event_id).exists(): return True agency_ids = REGISTRY.get('user_agency_ids') if agency_ids and Event.objects.filter( campaign_id__advertiser_id__agency_id__in=agency_ids, event_id=self.event_id).exists(): return True trading_desk_ids = REGISTRY.get('user_trading_desk_ids') if trading_desk_ids and Event.objects.filter( campaign_id__advertiser_id__agency_id__trading_desk_id__in= trading_desk_ids, event_id=self.event_id).exists(): return True return False def autopopulate_by_ownership(self): pass @property def campaign(self): return self.campaign_id.campaign @property def advertiser_id(self): return self.campaign_id.advertiser_id.advertiser_id @property def advertiser(self): return self.campaign_id.advertiser_id.advertiser @property def agency(self): return self.campaign_id.advertiser_id.agency_id.agency @property def agency_id(self): return self.campaign_id.advertiser_id.agency_id.agency_id @property def trading_desk(self): return self.campaign_id.advertiser_id.agency_id.trading_desk_id.trading_desk @property def trading_desk_id(self): return self.campaign_id.advertiser_id.agency_id.trading_desk_id.trading_desk_id def __unicode__(self): return self.event @property def encrypted_event_id(self): from mng.commons.crypto import encrypt return encrypt(self.event_id) search_args = ('event_id', ) @property def search_result(self): return { 'level': 'event', 'event': self.event, 'event_id': self.event_id, 'campaign_id': self.campaign_id.pk, 'advertiser_id': self.campaign_id.advertiser_id.pk, 'description': self.description, 'last_update': self.last_update } class Meta: # managed = False unique_together = ('campaign_id', 'event') db_table = 'event' app_label = 'restapi'
class TwitterTweet(BaseModel): tw_tweet_id = models.BigIntegerField(primary_key=True) tw_twitter_user_id = models.BigIntegerField() text = models.TextField(blank=True) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterTweetManager() objects_raw = models.Manager() # permission_check = True def __unicode__(self): return self.name class Meta: db_table = 'tw_tweet' app_label = 'restapi' @classmethod def fetch_tweet(self, data, syncData=False, oauth_token=settings.TW_ACCESS_TOKEN, oauth_secret=settings.TW_ACCESS_SECRET): res = {} res['success'] = True account_id = data.get('account_id') tweet_id = data.get('tweet_id') if isinstance(account_id,(int,long)): account_id_int = account_id account_id = int_to_base36(account_id) if not _cache.get(account_id): try: tw_account = TwitterAccount.objects_raw.filter(tw_account_id=account_id_int).first() if tw_account.promotable_user_id.tw_twitter_user_id: _cache[account_id] = tw_account.promotable_user_id.tw_twitter_user_id except TwitterUser.DoesNotExist: _cache[account_id] = 0 except TwitterAccount.DoesNotExist: _cache[account_id] = 0 if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: api_domain = 'https://api.twitter.com' resource = '/{api_version}/statuses/show.json?id={tweet_id}'.format(api_version="1.1", tweet_id=tweet_id) response = Request(client, 'get', resource, domain=api_domain).perform() entities = response.body['entities'] #print response.headers['x-rate-limit-remaining'] if response.headers['x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT: send_twitter_alert_email({"account_id": account_id, "endpoint": resource}) expanded_url = None if entities and entities['urls'] and entities['urls'][0] and entities['urls'][0]['expanded_url']: expanded_url = entities['urls'][0]['expanded_url'] account = client.accounts(account_id) as_user_id = ('?as_user_id=%s' % _cache[account_id]) if _cache[account_id] else '' resource = '/{api_version}/accounts/{account_id}/tweet/preview/{tweet_id}{as_user_id}'.format(api_version=settings.TW_API_VERSION, account_id=account.id, tweet_id=tweet_id, as_user_id=as_user_id) response = Request(client, 'get', resource).perform() res['data'] = response.body['data'] res['card_url'] = expanded_url except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = { 'data': {}, 'success': False, 'message': str(e) } return res
class TwitterLineItem(BaseModel): tw_line_item_id = models.BigIntegerField(primary_key=True) tw_campaign_id = models.ForeignKey(TwitterCampaign, db_column='tw_campaign_id') name = models.CharField(max_length=255) currency = models.CharField(max_length=255, default='USD') start_time = DateTimeField() end_time = DateTimeField() product_type = models.CharField(max_length=40, choices=TW_PRODUCT_TYPES, default='PROMOTED_TWEETS') placements = models.TextField(blank=True) primary_web_event_tag = models.CharField(max_length=255) objective = models.CharField(max_length=40, choices=TW_OBJECTIVES, default='APP_INSTALLS') bid_amount_local_micro = models.BigIntegerField(max_length=20) bid_amount_computed_reason = models.TextField(default='') bid_amount_computed = models.BigIntegerField(max_length=20) bid_override = models.NullBooleanField() bid_type = models.CharField(max_length=40, choices=TW_BID_TYPES, default='AUTO') bid_unit = models.CharField(max_length=40, choices=TW_BID_UNITS, default='APP_INSTALL') optimization = models.CharField(max_length=40, choices=TW_OPTIMIZATIONS, default='APP_INSTALL') charge_by = models.CharField(max_length=40, choices=TW_CHARGE_BYS, default='APP_INSTALL') categories = models.TextField(blank=True) tracking_tags = models.TextField(blank=True) automatically_select_bid = models.NullBooleanField() total_budget_amount_local_micro = models.BigIntegerField(max_length=20) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterLineItemManager() objects_raw = models.Manager() # permission_check = True search_args = ( 'name__icontains', 'tw_line_item_id', ) @property def search_result(self): tw_campaign = self.tw_campaign_id campaign = tw_campaign.campaign_id advertiser = campaign.advertiser_id result = { 'level': 'twitterlineitem', 'campaign': campaign.campaign, 'campaign_id': campaign.campaign_id, 'tw_campaign': tw_campaign.name, 'tw_campaign_id': tw_campaign.tw_campaign_id, 'tw_line_item': "%s / %s" % (campaign.campaign, self.name), 'tw_line_item_id': self.tw_line_item_id, 'advertiser': advertiser.advertiser, 'advertiser_id': advertiser.advertiser_id, 'last_update': self.last_update } return result @property def _advertiser_id(self): return self.tw_campaign_id.campaign_id.advertiser_id.advertiser_id @property def _campaign_id(self): return self.tw_campaign_id.campaign_id.campaign_id @property def _tw_campaign_id(self): return self.tw_campaign_id.tw_campaign_id def __unicode__(self): return self.name class Meta: db_table = 'tw_line_item' app_label = 'restapi' def is_own(self): return bool( self.campaign_id.advertiser_id.agency_id.trading_desk_id. trading_desk_userprofiles.filter(user=REGISTRY['user'])) @classmethod def fetch_line_items(self, data, syncData=False, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): res = {} res['data'] = [] res['success'] = False account_id = data.get('account_id') campaign_id = data.get('campaign_id') line_item_id = data.get('line_item_id') campaign_id_int = None if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if isinstance(campaign_id, (int, long)): campaign_id_int = campaign_id campaign_id = int_to_base36(campaign_id) if isinstance(line_item_id, (int, long)): line_item_id = int_to_base36(line_item_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) resource = '/{api_version}/accounts/{account_id}/line_items?with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id) if campaign_id is not None: resource = '/{api_version}/accounts/{account_id}/line_items?campaign_ids={campaign_id}&count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, campaign_id=campaign_id) response = Request(client, 'get', resource).perform() if response.headers[ 'x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT: send_twitter_alert_email({ "account_id": account_id, "endpoint": resource }) res['data'] = response.body['data'] next_cursor = None if response.body['next_cursor'] and response.body[ 'next_cursor'] is not 0: next_cursor = response.body['next_cursor'] while next_cursor is not 0: resource = '/{api_version}/accounts/{account_id}/line_items?cursor={next_cursor}&count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, next_cursor=next_cursor) if campaign_id is not None: resource = '/{api_version}/accounts/{account_id}/line_items?campaign_ids={campaign_id}&count=1000&cursor={next_cursor}&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, campaign_id=campaign_id, next_cursor=next_cursor) response = Request(client, 'get', resource).perform() next_cursor = response.body['next_cursor'] or 0 res['data'] += response.body['data'] res['success'] = True except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} if syncData and res['data'] and res['success']: res['sync'] = {} if isinstance(res['data'], (list, tuple)): sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 for index, api_line_item in enumerate(res['data'], start=0): if campaign_id_int is None: campaign_id_int = base36_to_int( api_line_item['campaign_id']) line_item_res = self.sync_line_item( account_id_int, campaign_id_int, api_line_item) if 'success' in line_item_res and line_item_res[ 'success'] is True: if line_item_res['type'] == 'existing': existing_count += 1 if line_item_res['type'] == 'new': new_count += 1 sync_success += 1 elif 'success' in line_item_res and line_item_res[ 'success'] is False: sync_fail += 1 res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['total'] = sync_success if sync_fail == 0: res['sync']['success'] = True else: res['sync']['success'] = False elif isinstance(res['data'], dict): line_item_res = self.sync_line_item(account_id_int, campaign_id_int, res['data']) if 'success' in line_item_res and line_item_res[ 'success'] is True: res['data'] = line_item_res['data'] res['sync']['success'] = line_item_res['success'] res['sync']['type'] = {} res['sync']['total'] = 1 res['sync']['type'][line_item_res['type']] = 1 elif 'success' in line_item_res and line_item_res[ 'success'] is False: res['data'] = line_item_res['data'] res['sync']['success'] = line_item_res['success'] res['sync']['message'] = line_item_res['message'] return res @classmethod def get_line_item(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): res = {} account_id = data.get('account_id') line_item_id = data.get('line_item_id') if isinstance(line_item_id, (int, long)): line_item_id = int_to_base36(line_item_id) if isinstance(account_id, (int, long)): account_id = int_to_base36(account_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res if line_item_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Line Item ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) resource = '/{api_version}/accounts/{account_id}/line_items/{line_item_id}?count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, line_item_id=line_item_id) response = Request(client, 'get', resource).perform() res['data'] = response.body['data'] res['success'] = True except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} return res @classmethod def create(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_update_line_item(data, oauth_token, oauth_token_secret, "post") @classmethod def update(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_update_line_item(data, oauth_token, oauth_token_secret, "put") @classmethod def create_update_line_item(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET, request_type="post"): res = {} res['sync'] = {} account_id = data.get('account_id', None) campaign_id = data.get('campaign_id', None) line_item_id = data.get('line_item_id', None) campaign_id_int = None if isinstance(campaign_id, (int, long)): campaign_id_int = campaign_id campaign_id = int_to_base36(campaign_id) data['campaign_id'] = campaign_id if isinstance(line_item_id, (int, long)): line_item_id_int = line_item_id line_item_id = int_to_base36(line_item_id) if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if account_id is None: res = { 'data': [], 'success': False, 'message': "Missing Twitter Account ID" } return res if request_type == 'post': if campaign_id is None: res = { 'data': [], 'success': False, 'message': "Missing Twitter Campaign ID" } return res if request_type == 'put': if line_item_id is None: res = { 'data': [], 'success': False, 'message': "Missing Twitter Line Item ID" } return res params = {} params['advertiser_domain'] = data.get('advertiser_domain', None) # automatically_set_bid and bid_type cannot be set in the same request # See https://dev.twitter.com/ads/reference/post/accounts/%3Aaccount_id/line_items#api-param-line-item-bid_type if data.get('automatically_select_bid', False) is True: params['automatically_select_bid'] = str(True).lower() else: params['bid_type'] = data.get('bid_type', None) params['bid_amount_local_micro'] = data.get( 'bid_amount_local_micro', None) params['bid_amount_local_micro'] = data.get('bid_amount_local_micro', None) params['bid_type'] = data.get('bid_type', None) params['bid_unit'] = data.get('bid_unit', None) params['campaign_id'] = data.get('campaign_id', None) params['categories'] = data.get('categories', None) params['charge_by'] = data.get('charge_by', None) params['end_time'] = data.get('end_time', None) params['include_sentiment'] = data.get('include_sentiment', 'POSITIVE_ONLY') params['name'] = data.get('name', None) params['objective'] = data.get('objective', 'APP_INSTALLS') params['optimization'] = data.get('optimization', None) if data.get('paused', None) is not None: params['paused'] = 'true' if data.get('paused') else 'false' params['placements'] = data.get('placements', 'ALL_ON_TWITTER') params['product_type'] = data.get('product_type', 'PROMOTED_TWEETS') params['start_time'] = data.get('start_time', None) params['total_budget_amount_local_micro'] = data.get( 'total_budget_amount_local_micro', None) # total_budget_amount_local_micro = 0 is not permitted if not params['total_budget_amount_local_micro']: params['total_budget_amount_local_micro'] = None params = dict((k, v) for k, v in params.iteritems() if v is not None and v is not "") client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) if request_type == 'put': resource = '/{api_version}/accounts/{account_id}/line_items/{line_item_id}'.format( api_version=settings.TW_API_VERSION, account_id=account.id, line_item_id=line_item_id) elif request_type == 'post': resource = '/{api_version}/accounts/{account_id}/line_items'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, request_type, resource, params=params).perform() if response.code == 200 or response.code == 201: res['success'] = True res['data'] = response.body['data'] if res['data'] and res['success']: if campaign_id_int is None and res['data']['campaign_id']: campaign_id_int = base36_to_int(res['data']['campaign_id']) line_item_res = self.sync_line_item(account_id_int, campaign_id_int, res['data']) if 'success' in line_item_res and line_item_res[ 'success'] is True: res['data'] = line_item_res['data'] res['sync']['success'] = line_item_res['success'] res['sync']['type'] = {} res['sync']['total'] = 1 res['sync']['type'][line_item_res['type']] = 1 elif 'success' in line_item_res and line_item_res[ 'success'] is False: res['data'] = line_item_res['data'] res['sync']['success'] = line_item_res['success'] res['sync']['message'] = line_item_res['message'] except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} return res @classmethod def batch_create(self, data, account_id, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.batch_create_update_line_item(data, account_id, oauth_token, oauth_token_secret, "post") @classmethod def batch_update(self, data, account_id, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.batch_create_update_line_item(data, account_id, oauth_token, oauth_token_secret, "put") @classmethod def batch_create_update_line_item( self, data, account_id, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET, request_type="post"): res = {} res['success'] = False campaign_id_int = None if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if account_id is None: res = { 'data': [], 'success': False, 'message': "Missing Twitter Account ID" } return res if isinstance(data, (list, tuple)): post_data = [] for line_item in data: line_item_data = {} params = {} params['bid_amount_local_micro'] = line_item.get( 'bid_amount_local_micro', None) params['bid_type'] = line_item.get('bid_type', None) params['bid_unit'] = line_item.get('bid_unit', None) params['campaign_id'] = line_item.get('campaign_id', None) params['categories'] = line_item.get('categories', None) params['charge_by'] = line_item.get('charge_by', None) params['end_time'] = line_item.get('end_time', None) params['include_sentiment'] = line_item.get( 'include_sentiment', None) params['line_item_id'] = line_item.get('line_item_id', None) params['name'] = line_item.get('name', None) params['objective'] = line_item.get('objective', 'APP_INSTALLS') params['primary_web_event_tag'] = line_item.get( 'primary_web_event_tag', None) params['optimization'] = line_item.get('optimization', None) params['paused'] = str(line_item.get( 'paused')).lower() if line_item.get('paused') else None params['placements'] = line_item.get('placements', 'ALL_ON_TWITTER') params['product_type'] = line_item.get('product_type', 'PROMOTED_TWEETS') params['start_time'] = line_item.get('start_time', None) params['total_budget_amount_local_micro'] = line_item.get( 'total_budget_amount_local_micro', None) # total_budget_amount_local_micro = 0 is not permitted if not params['total_budget_amount_local_micro']: params['total_budget_amount_local_micro'] = None params = dict((k, v) for k, v in params.iteritems() if v is not None and v is not "") if request_type == 'post': line_item_data['operation_type'] = 'Create' if request_type == 'put': line_item_data['operation_type'] = 'Update' line_item_data['params'] = params post_data.append(line_item_data) client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX # Split up requests into batches of 20 batch = [] batches = [] for x in range(0, len(post_data), 20): batch = post_data[x:x + 20] batches.append(batch) success_batch = [] error_batch = [] error_details = [] success = False error = False for batch_post in batches: try: account = client.accounts(account_id) headers = {"Content-Type": "application/json"} resource = '/{api_version}/batch/accounts/{account_id}/line_items'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, 'post', resource, body=json.dumps(batch_post), headers=headers).perform() if response.code == 200 or response.code == 201: success = True success_batch.extend(response.body['data']) except Error as e: error = True if e.response.body.get('operation_errors', None) is not None: for err in e.response.body.get('operation_errors'): if err: if isinstance(err, dict): err = [err] error_details.extend(err) if e.response.body.get('errors', None) is not None: for err in e.response.body.get('errors'): if err: if isinstance(err, dict): err = [err] error_details.extend(err) error_batch.extend(batch_post) except Exception as e: res = {'data': [], 'success': False, 'message': str(e)} error_batch.extend(batch_post) if success_batch and success: res['sync'] = {} if isinstance(success_batch, dict): success_batch = [success_batch] sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 for index, api_line_item in enumerate(success_batch, start=0): #campaign_id could be different in line item bach campaign_id_int = base36_to_int( api_line_item['campaign_id']) line_item_res = self.sync_line_item( account_id_int, campaign_id_int, api_line_item) if 'success' in line_item_res and line_item_res[ 'success'] is True: if line_item_res['type'] == 'existing': existing_count += 1 if line_item_res['type'] == 'new': new_count += 1 sync_success += 1 elif 'success' in line_item_res and line_item_res[ 'success'] is False: sync_fail += 1 res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['total'] = sync_success if sync_fail == 0: res['sync']['success'] = True else: res['sync']['success'] = False res['success'] = success res['count'] = {} res['count']['success'] = len(success_batch) res['count']['total'] = len(data) res['count']['error'] = len(error_batch) res['data'] = success_batch if error: res['success'] = False res['error'] = {} res['error']['data'] = error_batch res['error']['messages'] = filter(None, error_details) return res elif isinstance(data, dict): if request_type == 'post': return self.create(data, oauth_token, oauth_token_secret) if request_type == 'put': return self.update(data, oauth_token, oauth_token_secret) @classmethod def sync_line_item(self, tw_account_id, tw_campaign_id, data): res = {} res['data'] = data res['success'] = False line_item_id_int = base36_to_int(data['id']) m_tw_campaign = None if isinstance(tw_campaign_id, TwitterCampaign): m_tw_campaign = tw_campaign_id else: try: m_tw_campaign = TwitterCampaign.objects_raw.get( tw_campaign_id=tw_campaign_id) except TwitterCampaign.DoesNotExist: m_tw_campaign = None if m_tw_campaign is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Line Item. Cannot find Twitter Campaign" } return res try: res['type'] = 'existing' m_tw_line_item = TwitterLineItem.objects_raw.get( tw_campaign_id=m_tw_campaign, tw_line_item_id=line_item_id_int) except TwitterLineItem.DoesNotExist: res['type'] = 'new' m_tw_line_item = TwitterLineItem(tw_campaign_id=m_tw_campaign, tw_line_item_id=line_item_id_int) m_tw_line_item.bid_amount_computed = data['bid_amount_local_micro'] if m_tw_line_item is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Line Item" } return res m_tw_line_item.name = data['name'] m_tw_line_item.product_type = data['product_type'] m_tw_line_item.objective = data['objective'] m_tw_line_item.placements = json.dumps(data['placements']) m_tw_line_item.primary_web_event_tag = data['primary_web_event_tag'] m_tw_line_item.bid_amount_local_micro = data['bid_amount_local_micro'] m_tw_line_item.bid_type = data['bid_type'] m_tw_line_item.bid_unit = data['bid_unit'] m_tw_line_item.optimization = data['optimization'] m_tw_line_item.charge_by = data['charge_by'] m_tw_line_item.categories = json.dumps(data['categories']) m_tw_line_item.automatically_select_bid = data[ 'automatically_select_bid'] m_tw_line_item.total_budget_amount_local_micro = data[ 'total_budget_amount_local_micro'] m_tw_line_item.tracking_tags = json.dumps(data['tracking_tags']) if data['start_time'] is not None: m_tw_line_item.start_time = data['start_time'] if data['end_time'] is not None: m_tw_line_item.end_time = data['end_time'] if data['paused'] == True: m_tw_line_item.status = STATUS_PAUSED elif data['paused'] == False: m_tw_line_item.status = STATUS_ENABLED if data['deleted'] == True: m_tw_line_item.status = STATUS_ARCHIVED m_tw_line_item.save_raw() res['success'] = True return res
class TwitterPromotedTweet(BaseModel): tw_promoted_tweet_id = models.BigIntegerField(primary_key=True) tw_line_item_id = models.ForeignKey(TwitterLineItem, db_column='tw_line_item_id') tw_tweet_id = models.BigIntegerField() tw_app_card_id = models.BigIntegerField() status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterPromotedTweetManager() objects_raw = models.Manager() # permission_check = True def __unicode__(self): return self.name class Meta: db_table = 'tw_promoted_tweet' app_label = 'restapi' @property def _advertiser_id(self): return self.tw_line_item_id.tw_campaign_id.campaign_id.advertiser_id.advertiser_id @property def _campaign_id(self): return self.tw_line_item_id.tw_campaign_id.campaign_id.campaign_id @property def _tw_campaign_id(self): return self.tw_line_item_id.tw_campaign_id.tw_campaign_id @property def _tw_line_item_id(self): return self.tw_line_item_id.tw_line_item_id @classmethod def fetch_promoted_tweet(self, data, syncData=False, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): res = {} res['data'] = [] res['success'] = False error_codes = {} account_id = data.get('account_id') line_item_id = data.get('line_item_id') os_platform = data.get('os_platform') line_item_id_int = None if isinstance(line_item_id, six.string_types): line_item_id_int = base36_to_int(line_item_id) if isinstance(line_item_id, (int, long)): line_item_id_int = line_item_id line_item_id = int_to_base36(line_item_id) else: line_item_id_int = base36_to_int(line_item_id) if isinstance(account_id, (int, long)): account_id_int = account_id account_id = int_to_base36(account_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res if line_item_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Line Item ID" } return res if line_item_id_int is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Line Item ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) resource = '/{api_version}/accounts/{account_id}/promoted_tweets?line_item_id={line_item_id}&count=1000&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, line_item_id=line_item_id) response = Request(client, 'get', resource).perform() if response.headers[ 'x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT: send_twitter_alert_email({ "account_id": account_id, "endpoint": resource }) res['data'] = response.body['data'] next_cursor = None if response.body['next_cursor'] and response.body[ 'next_cursor'] is not 0: next_cursor = response.body['next_cursor'] while next_cursor is not 0: resource = '/{api_version}/accounts/{account_id}/promoted_tweets?line_item_id={line_item_id}&count=1000&cursor={next_cursor}&with_deleted=true'.format( api_version=settings.TW_API_VERSION, account_id=account.id, line_item_id=line_item_id, next_cursor=next_cursor) response = Request(client, 'get', resource).perform() next_cursor = response.body['next_cursor'] or 0 res['data'] += response.body['data'] res['success'] = True except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} if syncData and res['data'] and res['success']: res['sync'] = {} if isinstance(res['data'], (list, tuple)): sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 for index, api_line_item_promoted_tweet in enumerate( res['data'], start=0): line_item_promoted_tweet_res = self.sync_promoted_tweet( account_id_int, line_item_id_int, api_line_item_promoted_tweet, os_platform) if 'success' in line_item_promoted_tweet_res and line_item_promoted_tweet_res[ 'success'] is True: if line_item_promoted_tweet_res['type'] == 'existing': existing_count += 1 if line_item_promoted_tweet_res['type'] == 'new': new_count += 1 sync_success += 1 elif 'success' in line_item_promoted_tweet_res and line_item_promoted_tweet_res[ 'success'] is False: sync_fail += 1 if line_item_promoted_tweet_res.get('errors', None): error_codes.update( line_item_promoted_tweet_res['errors']) res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['total'] = sync_success if sync_fail == 0: res['sync']['success'] = True else: res['sync']['success'] = False res['errors'] = error_codes elif isinstance(res['data'], dict): line_item_promoted_tweet_res = self.sync_promoted_tweet( account_id_int, line_item_id_int, res['data']) if 'success' in line_item_promoted_tweet_res and line_item_promoted_tweet_res[ 'success'] is True: res['data'] = line_item_promoted_tweet_res['data'] res['sync']['success'] = line_item_promoted_tweet_res[ 'success'] res['sync']['type'] = {} res['sync']['total'] = 1 res['sync']['type'][ line_item_promoted_tweet_res['type']] = 1 elif 'success' in line_item_promoted_tweet_res and line_item_promoted_tweet_res[ 'success'] is False: res['data'] = line_item_promoted_tweet_res['data'] res['sync']['success'] = line_item_promoted_tweet_res[ 'success'] res['sync']['message'] = line_item_promoted_tweet_res[ 'message'] if line_item_promoted_tweet_res.get('errors', None): error_codes.update( line_item_promoted_tweet_res['errors']) res['errors'] = error_codes return res @classmethod def set_promoted_tweet(self, data, account_id, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): res = {} res['success'] = True line_item_id = data.get('line_item_id', None) tweet_ids = data.get('tweet_ids', None) line_item_id_int = line_item_id line_item_id_base_36 = int_to_base36(line_item_id) account_id_int = int(account_id) account_id_base36 = int_to_base36(account_id) if account_id_base36 is None: res = { 'data': {}, 'success': False, 'message': "Invaid Twitter Account ID" } return res tweet_ids_list = map(lambda s: s.strip(), tweet_ids.split(',')) try: m_tw_line_item_promoted_tweet = TwitterPromotedTweet.objects_raw.filter( tw_line_item_id=line_item_id).exclude( status=STATUS_ARCHIVED).values() except TwitterPromotedTweet.DoesNotExist: m_tw_line_item_promoted_tweet = None m_tw_line_item_promoted_tweet_list = list( m_tw_line_item_promoted_tweet ) # converts ValuesQuerySet into Python list new_list = [] for item in tweet_ids_list: matching_item = False for m_item in m_tw_line_item_promoted_tweet_list: if int(item) == int(m_item['tw_tweet_id']): # Remove matching existing item from list m_tw_line_item_promoted_tweet_list.remove(m_item) matching_item = True break if matching_item is False: new_list.append(item) delete_list = m_tw_line_item_promoted_tweet_list new_res = None delete_res = None if new_list: data_post = { "account_id": account_id_int, "line_item_id": line_item_id_int, "tweet_ids": ",".join(new_list), } new_res = self.create(data_post, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET) if delete_list: for item in delete_list: data_post = { "account_id": account_id_int, "line_item_id": line_item_id_int, "promoted_tweet_id": int_to_base36(item['tw_promoted_tweet_id']), } delete_res = self.delete( data_post, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET) if new_res: if new_res['success'] is False: res['success'] = False if delete_res: if delete_res['success'] is False: res['success'] = False # res['data'] = new_res['data'] # res['sync'] = new_res['sync'] return res @classmethod def create(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_update_promoted_tweet(data, oauth_token, oauth_token_secret, "post") @classmethod def update(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_update_promoted_tweet(data, oauth_token, oauth_token_secret, "put") @classmethod def delete(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): return self.create_update_promoted_tweet(data, oauth_token, oauth_token_secret, "delete") @classmethod def create_update_promoted_tweet( self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET, request_type="post"): res = {} res['sync'] = {} account_id = data.get('account_id', None) line_item_id = data.get('line_item_id', None) tweet_ids = data.get('tweet_ids', None) promoted_tweet_id = data.get('promoted_tweet_id', None) line_item_id_int = None line_item_id_int = line_item_id line_item_id_base_36 = int_to_base36(line_item_id) account_id_int = account_id account_id_base36 = int_to_base36(account_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res if request_type == 'put' or request_type == 'delete': if promoted_tweet_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Promoted Tweet ID" } return res if request_type == 'post': if tweet_ids is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Tweet IDs" } return res if line_item_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Line Item ID" } return res params = {} params['display_properties'] = data.get('display_properties', None) params['tweet_ids'] = data.get('tweet_ids', None) params['line_item_id'] = line_item_id_base_36 params = dict((k, v) for k, v in params.iteritems() if v is not None and v is not "") client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id_base36) if request_type == 'put' or request_type == 'delete': resource = '/{api_version}/accounts/{account_id}/promoted_tweets/{promoted_tweet_id}'.format( api_version=settings.TW_API_VERSION, account_id=account.id, promoted_tweet_id=promoted_tweet_id) elif request_type == 'post': resource = '/{api_version}/accounts/{account_id}/promoted_tweets'.format( api_version=settings.TW_API_VERSION, account_id=account.id) response = Request(client, request_type, resource, params=params).perform() if response.code == 200 or response.code == 201: res['success'] = True res['data'] = response.body['data'] if res['data'] and res['success']: sync_success = 0 sync_fail = 0 new_count = 0 existing_count = 0 deleted_count = 0 if request_type == 'delete': res['data'] = [res['data']] for index, api_line_item_promoted_tweet in enumerate( res['data'], start=0): line_item_id_int = base36_to_int( api_line_item_promoted_tweet['line_item_id']) api_line_item_promoted_tweet['account_id'] = account_id line_item_promoted_tweet_res = self.sync_promoted_tweet( account_id_int, line_item_id_int, api_line_item_promoted_tweet) if 'success' in line_item_promoted_tweet_res and line_item_promoted_tweet_res[ 'success'] is True: if 'skip' in line_item_promoted_tweet_res: continue if line_item_promoted_tweet_res['type'] == 'existing': existing_count += 1 if line_item_promoted_tweet_res['type'] == 'new': new_count += 1 if line_item_promoted_tweet_res['type'] == 'delete': deleted_count += 1 sync_success += 1 elif 'success' in line_item_promoted_tweet_res and line_item_promoted_tweet_res[ 'success'] is False: sync_fail += 1 res['sync']['success'] = sync_fail == 0 res['sync']['type'] = {} res['sync']['type']['existing'] = existing_count res['sync']['type']['new'] = new_count res['sync']['type']['delete'] = deleted_count except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = {'data': {}, 'success': False, 'message': str(e)} return res @classmethod def sync_promoted_tweet(self, tw_account_id, tw_line_item_id, data, os_platform=None): from restapi.models.twitter.TwitterTweet import TwitterTweet res = {} res['data'] = data res['success'] = True promoted_tweet_id_int = base36_to_int(data['id']) m_tw_line_item_promoted_tweet = None if isinstance(tw_line_item_id, TwitterLineItem): m_tw_line_item = tw_line_item_id else: m_tw_line_item = TwitterLineItem.objects_raw.filter( tw_line_item_id=tw_line_item_id).first() if m_tw_line_item is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Line Item Targeting. Cannot find Twitter Line Item" } return res if 'os_platform' not in data: os_platform = 'web' manage_campaign = m_tw_line_item.tw_campaign_id.campaign_id if manage_campaign and manage_campaign.targeting: targeting = json.loads(manage_campaign.targeting) if targeting and 'os' in targeting: os_platform = targeting['os'] if os_platform == 'iOS': os_platform = 'iphone' try: res['type'] = 'existing' m_tw_line_item_promoted_tweet = TwitterPromotedTweet.objects_raw.get( tw_line_item_id=m_tw_line_item, tw_promoted_tweet_id=promoted_tweet_id_int) except TwitterPromotedTweet.DoesNotExist: res['type'] = 'new' m_tw_line_item_promoted_tweet = TwitterPromotedTweet( tw_line_item_id=m_tw_line_item, tw_promoted_tweet_id=promoted_tweet_id_int) sync_tweet = True if m_tw_line_item_promoted_tweet.tw_app_card_id is not None: sync_tweet = False if m_tw_line_item_promoted_tweet is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Line Item Promoted Tweet. Cannot find Twitter Promoted Tweet" } return res if data['paused'] == True: m_tw_line_item_promoted_tweet.status = STATUS_PAUSED elif data['paused'] == False: m_tw_line_item_promoted_tweet.status = STATUS_ENABLED if data['deleted'] == True: res['type'] = 'delete' m_tw_line_item_promoted_tweet.status = STATUS_ARCHIVED m_tw_line_item_promoted_tweet.save() m_tw_line_item_promoted_tweet.tw_tweet_id = data['tweet_id'] try: m_tw_line_item_tweet = TwitterTweet.objects_raw.get( tw_tweet_id=data['tweet_id']) sync_tweet = False except TwitterTweet.DoesNotExist: m_tw_line_item_tweet = TwitterTweet(tw_tweet_id=data['tweet_id']) if m_tw_line_item_tweet is None: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Line Item Tweet. Cannot find Twitter Tweet" } return res # Don't fetch tweet data if we already have tw_promoted_tweet.tw_app_card_id and tw_tweet.tw_tweet_id # https://managewiki.atlassian.net/browse/ADP-138 if sync_tweet: api_tweet_details_data = TwitterTweet.fetch_tweet( dict(account_id=tw_account_id, tweet_id=data['tweet_id'])) if api_tweet_details_data['success'] is False: res['success'] = False res['message'] = api_tweet_details_data[ 'message'] if 'message' in api_tweet_details_data else '' res['errors'] = api_tweet_details_data[ 'errors'] if 'errors' in api_tweet_details_data else {} # Check if App Card in Tweeet api_tweet_app_card = api_tweet_details_data.get('card_url') tweet_app_card = None if api_tweet_app_card is not None: try: tweet_app_card = TwitterAppCard.objects_raw.get( preview_url=api_tweet_app_card) except TwitterAppCard.DoesNotExist: tweet_app_card = None if tweet_app_card: m_tw_line_item_promoted_tweet.tw_app_card_id = tweet_app_card.tw_app_card_id if m_tw_line_item_tweet: api_tweet_details = api_tweet_details_data['data'] for api_tweet_detail in api_tweet_details: if api_tweet_detail['platform'] == os_platform.lower(): m_tw_line_item_tweet.text = api_tweet_detail['preview'] m_tw_line_item_tweet.save_raw() continue try: m_tw_line_item_promoted_tweet.save_raw() res['success'] = True except _mysql_exceptions.Warning, e: res = { 'data': {}, 'success': False, 'message': "Error syncing Twitter Line Item Targeting" } return res
class Campaign(BasePausedAtModelMixin, BaseModel): campaign_id = models.AutoField(primary_key=True) advertiser_id = models.ForeignKey(Advertiser, db_column='advertiser_id') campaign = models.CharField(max_length=255) notes = models.TextField(blank=True) sampling_rate = models.DecimalField(max_digits=9, decimal_places=8, blank=True, null=True) throttling_rate = models.DecimalField(max_digits=9, decimal_places=8, blank=True, null=True) domain = models.CharField(max_length=255) redirect_url = models.CharField(max_length=1024, blank=True) destination_url = models.CharField(max_length=511) viewthrough_url = models.CharField(max_length=1024, blank=True, default='') tracking_provider_id = models.ForeignKey(TrackingProvider, db_column='tracking_provider_id', null=True) inflator_text = models.CharField(max_length=1024, blank=True) frequency_map = OrderedJSONField(blank=True, default='', cmp=cmp_asterisk_last, default_json=str) priority = models.IntegerField(blank=True, default=0) daily_budget_type = models.CharField(max_length=10, blank=True, choices=BUDGET_TYPES_CHOICES, default=BUDGET_TYPES_CHOICES[0][0]) daily_budget_value = models.DecimalField(max_digits=9, decimal_places=2, default=0) daily_spend = models.DecimalField(max_digits=9, decimal_places=2, default=0) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_CHOICES[1][0]) distribution_app_sha1_mac = models.BooleanField(default=False, blank=True) distribution_app_sha1_udid = models.BooleanField(default=False, blank=True) distribution_app_sha1_android_id = models.BooleanField(default=False, blank=True) # pylint: disable=invalid-name distribution_app_ifa = models.BooleanField(default=False, blank=True) distribution_app_md5_ifa = models.BooleanField(default=False, blank=True) distribution_app_xid = models.BooleanField(default=False, blank=True) distribution_web = models.BooleanField(default=False, blank=True) flight_start_date = ZeroDateTimeField(default=None, null=True, blank=True) flight_end_date = ZeroDateTimeField(default=None, null=True, blank=True) flight_budget_type = models.CharField(max_length=10, blank=True, choices=BUDGET_TYPES_CHOICES, default=BUDGET_TYPES_CHOICES[0][0]) flight_budget_value = models.DecimalField(max_digits=9, decimal_places=2, default=0) attribution_window = models.IntegerField(blank=True, null=True) genre = models.IntegerField() capped = models.IntegerField(blank=True, default=0) hourly_capped = models.IntegerField(blank=True, default=0) is_test = models.IntegerField(blank=True, default=0) bidder_args = models.CharField(max_length=255, blank=True, default='') last_update = DateTimeField(null=True, default=None, auto_now=True) paused_at = ZeroDateTimeField(default=None, null=True, blank=True) external_id = models.CharField(max_length=100, blank=True, null=True) categories = models.TextField(blank=True) targeting = models.TextField(blank=True, default='') os = models.IntegerField(blank=True, default=1) overridden = models.BooleanField(default=False, blank=True) ignore_fatigue_segment = models.BooleanField(default=False, blank=True) ignore_suppression_segment = models.BooleanField(default=False, blank=True) total_cost_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) daily_cost_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) total_loss_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) daily_loss_cap = models.DecimalField(max_digits=9, decimal_places=2, default=0) app_install = models.BooleanField(default=False, blank=True) source_type = models.IntegerField(max_length=3, default=1) actions = ('filter_by_trading_desk', 'targeting_json_read', 'targeting_json_update') objects = CampaignManager() objects_raw = models.Manager() permission_check = True def is_own(self): """Returns True if campaign entity belongs to the current user.""" advertiser_ids = REGISTRY.get('user_advertiser_ids') if advertiser_ids and self.advertiser_id_id in advertiser_ids: return True agency_ids = REGISTRY.get('user_agency_ids') if agency_ids and Campaign.objects.filter(advertiser_id__agency_id__in=agency_ids, campaign_id=self.campaign_id).exists(): return True trading_desk_ids = REGISTRY.get('user_trading_desk_ids') if trading_desk_ids and Campaign.objects.filter(advertiser_id__agency_id__trading_desk_id__in=trading_desk_ids, campaign_id=self.campaign_id).exists(): return True return False def autopopulate_by_ownership(self): pass search_args = ('campaign_id', 'campaign__icontains') @property def search_result(self): advertiser = self.advertiser_id agency = self.advertiser_id.agency_id result = {'level': 'campaign', 'campaign': self.campaign, 'campaign_id': self.campaign_id, 'advertiser': advertiser.advertiser, 'advertiser_id': advertiser.advertiser_id, 'agency': agency.agency, 'agency_id': agency.agency_id, 'last_update': self.last_update} return result @property def other_iab_classification(self): classification = [] if self.categories is not None: for category in self.categories.split(" "): if category in dict(OTHER_IAB_CATEGORIES): classification.append(category) return classification @property def manage_classification(self): classification = [] if self.categories is not None: for category in self.categories.split(" "): if category in dict(MANAGE_CATEGORIES): classification.append(category) return classification @property def iab_classification(self): classification = [] if self.categories is not None: for category in self.categories.split(" "): if category in dict(IAB_CATEGORIES): classification.append(category) return classification @property def parent_name(self): return self.advertiser_id.advertiser @property def agency_name(self): return self.advertiser_id.agency_id.agency @property def trading_desk_name(self): return self.advertiser_id.agency_id.trading_desk_id.trading_desk @property def advertiser(self): return self.advertiser_id.advertiser @property def agency(self): return self.advertiser_id.agency_id.agency @property def agency_id(self): return self.advertiser_id.agency_id.agency_id @property def _advertiser_id(self): return self.advertiser_id.advertiser_id @property def trading_desk(self): return self.advertiser_id.agency_id.trading_desk_id.trading_desk @property def trading_desk_id(self): return self.advertiser_id.agency_id.trading_desk_id.trading_desk_id @property def ad_groups_enabled(self): from restapi.models.AdGroup import AdGroup as model return model.objects.getCount(campaign_id=self.pk, status=STATUS_ENABLED) @property def ad_groups_total(self): from restapi.models.AdGroup import AdGroup as model return model.objects.getCount(campaign_id=self.pk) @property def ads_enabled(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id__campaign_id=self.pk, status=STATUS_ENABLED) @property def ads_disapproved(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id__campaign_id=self.pk, adx_status=AD_STATUS_DISAPPROVED, status=STATUS_ENABLED) @property def a9_pending(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id__campaign_id=self.pk, a9_status=A9_STATUS_PENDING, status=STATUS_ENABLED) @property def a9_failed(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id__campaign_id=self.pk, a9_status=A9_STATUS_FAILED, status=STATUS_ENABLED) @property def ads_total(self): from restapi.models.Ad import Ad as model return model.objects.getCount(ad_group_id__campaign_id=self.pk) @property def black_lists_total(self): from restapi.models.BidderBlacklist import BidderBlacklistIds as model return model.objects.getCount(campaign_id=self.pk) @property def black_lists_campaign(self): from restapi.models.BidderBlacklist import BidderBlacklistIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id=0) @property def black_lists_ad_group(self): from restapi.models.BidderBlacklist import BidderBlacklistIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id__gt=0) @property def white_lists_total(self): from restapi.models.BidderWhitelist import BidderWhitelistIds as model return model.objects.getCount(campaign_id=self.pk) @property def white_lists_campaign(self): from restapi.models.BidderWhitelist import BidderWhitelistIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id=0) @property def white_lists_ad_group(self): from restapi.models.BidderWhitelist import BidderWhitelistIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id__gt=0) @property def custom_hints_total(self): from restapi.models.CustomHint import CustomHintIds as model return model.objects.getCount(campaign_id=self.pk) @property def custom_hints_campaign(self): from restapi.models.CustomHint import CustomHintIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id=0) @property def custom_hints_ad_group(self): from restapi.models.CustomHint import CustomHintIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id__gt=0) @property def discrete_pricing_total(self): from restapi.models.DiscretePricing import DiscretePricingIds as model return model.objects.getCount(campaign_id=self.pk) @property def discrete_pricing_campaign(self): from restapi.models.DiscretePricing import DiscretePricingIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id=0) @property def discrete_pricing_ad_group(self): from restapi.models.DiscretePricing import DiscretePricingIds as model return model.objects.getCount(campaign_id=self.pk, ad_group_id__gt=0) @property def ads_to_pause(self): """Returns a number of ads recommended to pause.""" from restapi.models.CreativePruning import CreativePruning return CreativePruning.objects.getCount(ad_id__ad_group_id__campaign_id=self.pk, action='pause') @property def ads_to_delete(self): """Returns a number of ads recommended to delete.""" from restapi.models.CreativePruning import CreativePruning return CreativePruning.objects.getCount(ad_id__ad_group_id__campaign_id=self.pk, action='delete') def __unicode__(self): return self.campaign class Meta: managed = True unique_together = ('advertiser_id', 'campaign') db_table = 'campaign' app_label = 'restapi' ordering = ('campaign',)
class TwitterAccount(BaseModel): tw_account_id = models.BigIntegerField(primary_key=True) tw_twitter_user_id = models.ForeignKey(TwitterUser, db_column='tw_twitter_user_id', related_name="tw_account_tw_twitter_user") promotable_user_id = models.ForeignKey(TwitterUser, db_column='promotable_user_id', related_name="tw_account_tw_promotable_user") advertiser_id = models.ForeignKey(Advertiser, db_column='advertiser_id') is_manage = models.BooleanField(default=False) name = models.CharField(max_length=255) status = models.CharField(max_length=8, choices=STATUS_CHOICES, default=STATUS_ENABLED) created_at = DateTimeField(auto_now_add=True) last_update = DateTimeField(null=True, default=None, auto_now=True) objects = TwitterAccountManager() objects_raw = models.Manager() # permission_check = True def __unicode__(self): return self.name class Meta: db_table = 'tw_account' app_label = 'restapi' @classmethod def fetch_accounts(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET): res = {} account_id = data.get('account_id') if isinstance(account_id,(int,long)): account_id = int_to_base36(account_id) if account_id is None: res = { 'data': {}, 'success': False, 'message': "Missing Twitter Account ID" } return res client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token, oauth_token_secret) if settings.TW_SANDBOX: client.sandbox = settings.TW_SANDBOX try: account = client.accounts(account_id) resource = '/{api_version}/accounts/{account_id}'.format(api_version=settings.TW_API_VERSION, account_id=account_id) response = Request(client, 'get', resource).perform() if response.headers['x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT: send_twitter_alert_email({"account_id": account_id, "endpoint": resource}) res['data'] = response.body['data'] res['success'] = True except Error as e: code = None if e.code: code = e.code elif e.details[0]['code']: code = e.details[0]['code'] res = { 'data': {}, 'success': False, 'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '', 'errors': { str(code): True } if code else {} } except Exception as e: res = { 'data': {}, 'success': False, 'message': str(e) } return res