From 3f91354e8370640e34471a41caacfeaebe0708e3 Mon Sep 17 00:00:00 2001 From: Tushar Bhushan Date: Fri, 8 Feb 2019 12:42:53 -0500 Subject: [PATCH] Update Python SDK to v4 (#174) * v4 Updates * removed/updated in v4 * minor typo * fix flake8 errors * more flake8 fixes * finally fixed flake8 * fix under-indented lines --- twitter_ads/__init__.py | 4 +-- twitter_ads/account.py | 11 +++--- twitter_ads/audience.py | 76 +++++++++++++---------------------------- twitter_ads/campaign.py | 53 ++++++++++++++++++++++++++++ twitter_ads/creative.py | 35 ++----------------- twitter_ads/enum.py | 6 ++++ 6 files changed, 92 insertions(+), 93 deletions(-) diff --git a/twitter_ads/__init__.py b/twitter_ads/__init__.py index 2454def..1f9ea68 100644 --- a/twitter_ads/__init__.py +++ b/twitter_ads/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) 2015 Twitter, Inc. -VERSION = (3, 0, 0) -API_VERSION = '3' +VERSION = (4, 0, 0) +API_VERSION = '4' from twitter_ads.utils import get_version diff --git a/twitter_ads/account.py b/twitter_ads/account.py index 0942639..1ff1475 100644 --- a/twitter_ads/account.py +++ b/twitter_ads/account.py @@ -10,7 +10,7 @@ from twitter_ads.resource import resource_property, Resource from twitter_ads.creative import (AccountMedia, MediaCreative, ScheduledTweet, - Video, VideoWebsiteCard, PromotedTweet) + VideoWebsiteCard, PromotedTweet) from twitter_ads.audience import TailoredAudience from twitter_ads.campaign import (AppList, Campaign, FundingInstrument, LineItem, PromotableUser, ScheduledPromotedTweet) @@ -119,12 +119,6 @@ def tailored_audiences(self, id=None, **kwargs): """ return self._load_resource(TailoredAudience, id, **kwargs) - def videos(self, id=None, **kwargs): - """ - Returns a collection of videos available to the current account. - """ - return self._load_resource(Video, id, **kwargs) - def account_media(self, id=None, **kwargs): """ Returns a collection of account media available to the current account. @@ -186,3 +180,6 @@ def scoped_timeline(self, *id, **kwargs): resource_property(Account, 'timezone_switch_at', readonly=True, transform=TRANSFORM.TIME) resource_property(Account, 'created_at', readonly=True, transform=TRANSFORM.TIME) resource_property(Account, 'updated_at', readonly=True, transform=TRANSFORM.TIME) +# writable +resource_property(Account, 'account_name') +resource_property(Account, 'industry_type') diff --git a/twitter_ads/audience.py b/twitter_ads/audience.py index e161aa8..a9bbe2e 100644 --- a/twitter_ads/audience.py +++ b/twitter_ads/audience.py @@ -2,9 +2,9 @@ """Container for all audience management logic used by the Ads API SDK.""" -from twitter_ads.enum import TA_OPERATIONS, TRANSFORM +from twitter_ads.enum import TRANSFORM from twitter_ads.resource import resource_property, Resource -from twitter_ads.http import TONUpload, Request +from twitter_ads.http import Request from twitter_ads.error import BadRequest from twitter_ads.cursor import Cursor from twitter_ads import API_VERSION @@ -15,47 +15,43 @@ class TailoredAudience(Resource): PROPERTIES = {} - RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/tailored_audiences' RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/tailored_audiences/{id}' - RESOURCE_UPDATE = '/' + API_VERSION + '/accounts/{account_id}/tailored_audience_changes' + RESOURCE_USERS = '/' + API_VERSION + '/accounts/{account_id}/tailored_audiences/\ +{id}/users' RESOURCE_PERMISSIONS = '/' + API_VERSION + '/accounts/{account_id}/tailored_audiences/\ {id}/permissions' - OPT_OUT = '/' + API_VERSION + '/accounts/{account_id}/tailored_audiences/global_opt_out' @classmethod - def create(klass, account, file_path, name, list_type): + def create(klass, account, name): """ - Uploads and creates a new tailored audience. + Creates a new tailored audience. """ - upload = TONUpload(account.client, file_path) audience = klass(account) - getattr(audience, '__create_audience__')(name, list_type) + getattr(audience, '__create_audience__')(name) try: - getattr(audience, '__update_audience__')(upload.perform(), list_type, TA_OPERATIONS.ADD) return audience.reload() except BadRequest as e: audience.delete() raise e - @classmethod - def opt_out(klass, account, file_path, list_type): + def users(self, params): """ - Updates the global opt-out list for the specified advertiser account. + This is a private API and requires whitelisting from Twitter. + This endpoint will allow partners to add, update and remove users from a given + tailored_audience_id. + The endpoint will also accept multiple user identifier types per user as well. """ - upload = TONUpload(account.client, file_path) - params = {'input_file_path': upload.perform(), 'list_type': list_type} - resource = klass.OPT_OUT.format(account_id=account.id) - Request(account.client, 'put', resource, params=params).perform() - return True - - def update(self, file_path, list_type, operation=TA_OPERATIONS.ADD): - """ - Updates the current tailored audience instance. - """ - upload = TONUpload(self.account.client, file_path) - getattr(self, '__update_audience__')(upload.perform(), list_type, operation) - return self.reload() + resource = self.RESOURCE_USERS.format(account_id=self.account.id, id=self.id) + headers = {'Content-Type': 'application/json'} + response = Request(self.account.client, + 'post', + resource, + headers=headers, + body=json.dumps(params)).perform() + success_count = response.body['data']['success_count'] + total_count = response.body['data']['total_count'] + return (success_count, total_count) def delete(self): """ @@ -65,19 +61,6 @@ def delete(self): response = Request(self.account.client, 'delete', resource).perform() return self.from_response(response.body['data']) - def status(self): - """ - Returns the status of all changes for the current tailored audience instance. - """ - if not self.id: - return None - - resource = self.RESOURCE_UPDATE.format(account_id=self.account.id) - request = Request(self.account.client, 'get', resource, params=self.to_params()) - cursor = list(Cursor(None, request)) - - return filter(lambda change: change['tailored_audience_id'] == self.id, cursor) - def permissions(self, **kwargs): """ Returns a collection of permissions for the curent tailored audience. @@ -85,23 +68,12 @@ def permissions(self, **kwargs): self._validate_loaded() return TailoredAudiencePermission.all(self.account, self.id, **kwargs) - def __create_audience__(self, name, list_type): - params = {'name': name, 'list_type': list_type} + def __create_audience__(self, name): + params = {'name': name} resource = self.RESOURCE_COLLECTION.format(account_id=self.account.id) response = Request(self.account.client, 'post', resource, params=params).perform() return self.from_response(response.body['data']) - def __update_audience__(self, location, list_type, operation): - params = { - 'tailored_audience_id': self.id, - 'input_file_path': location, - 'list_type': list_type, - 'operation': operation - } - - resource = self.RESOURCE_UPDATE.format(account_id=self.account.id) - return Request(self.account.client, 'post', resource, params=params).perform() - # tailored audience properties # read-only diff --git a/twitter_ads/campaign.py b/twitter_ads/campaign.py index 3670096..ccfea60 100644 --- a/twitter_ads/campaign.py +++ b/twitter_ads/campaign.py @@ -366,3 +366,56 @@ def create(klass, account, **kwargs): resource = klass.TWEET_CREATE.format(account_id=account.id) response = Request(account.client, 'post', resource, params=params).perform() return response.body['data'] + + +class UserSettings(Resource, Persistence): + + PROPERTIES = {} + + RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/user_settings/{id}' + + +# user settings properties +# writable +resource_property(UserSettings, 'notification_email') +resource_property(UserSettings, 'contact_phone') +resource_property(UserSettings, 'contact_phone_extension') +resource_property(UserSettings, 'subscribed_email_types') +resource_property(UserSettings, 'user_id') + + +class TaxSettings(Resource, Persistence): + + PROPERTIES = {} + + RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/tax_settings/{id}' + + +# tax settings properties +# writable +resource_property(TaxSettings, 'address_city') +resource_property(TaxSettings, 'address_country') +resource_property(TaxSettings, 'address_email') +resource_property(TaxSettings, 'address_first_name') +resource_property(TaxSettings, 'address_last_name') +resource_property(TaxSettings, 'address_name') +resource_property(TaxSettings, 'address_postal_code') +resource_property(TaxSettings, 'address_region') +resource_property(TaxSettings, 'address_street1') +resource_property(TaxSettings, 'address_street2') +resource_property(TaxSettings, 'bill_to') +resource_property(TaxSettings, 'business_relationship') +resource_property(TaxSettings, 'client_address_city') +resource_property(TaxSettings, 'client_address_country') +resource_property(TaxSettings, 'client_address_email') +resource_property(TaxSettings, 'client_address_first_name') +resource_property(TaxSettings, 'client_address_last_name') +resource_property(TaxSettings, 'client_address_name') +resource_property(TaxSettings, 'client_address_postal_code') +resource_property(TaxSettings, 'client_address_region') +resource_property(TaxSettings, 'client_address_street1') +resource_property(TaxSettings, 'client_address_street2') +resource_property(TaxSettings, 'invoice_jurisdiction') +resource_property(TaxSettings, 'tax_category') +resource_property(TaxSettings, 'tax_exemption_id') +resource_property(TaxSettings, 'tax_id') diff --git a/twitter_ads/creative.py b/twitter_ads/creative.py index 48d04fd..2ed4df0 100644 --- a/twitter_ads/creative.py +++ b/twitter_ads/creative.py @@ -69,35 +69,6 @@ def save(self): resource_property(PromotedTweet, 'tweet_id') # SDK limitation -class Video(Resource, Persistence): - - PROPERTIES = {} - - RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/videos' - RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/videos/{id}' - - -# video properties -# read-only -resource_property(Video, 'aspect_ratio', readonly=True) -resource_property(Video, 'created_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(Video, 'deleted', readonly=True, transform=TRANSFORM.BOOL) -resource_property(Video, 'duration', readonly=True, transform=TRANSFORM.INT) -resource_property(Video, 'id', readonly=True) -resource_property(Video, 'media_key', readonly=True) -resource_property(Video, 'poster_url', readonly=True) -resource_property(Video, 'preview_url', readonly=True) -resource_property(Video, 'ready_to_tweet', readonly=True, transform=TRANSFORM.BOOL) -resource_property(Video, 'reasons_not_servable', readonly=True, transform=TRANSFORM.LIST) -resource_property(Video, 'tweeted', readonly=True, transform=TRANSFORM.BOOL) -resource_property(Video, 'updated_at', readonly=True, transform=TRANSFORM.TIME) -# writable -resource_property(Video, 'description') -resource_property(Video, 'poster_image_media_id') -resource_property(Video, 'title') -resource_property(Video, 'video_media_id') - - class AccountMedia(Resource, Persistence): PROPERTIES = {} @@ -228,7 +199,7 @@ class ImageAppDownloadCard(Resource, Persistence): resource_property(ImageAppDownloadCard, 'updated_at', readonly=True, transform=TRANSFORM.TIME) resource_property(ImageAppDownloadCard, 'deleted', readonly=True, transform=TRANSFORM.BOOL) # writable -resource_property(ImageAppDownloadCard, 'app_country_code') +resource_property(ImageAppDownloadCard, 'country_code') resource_property(ImageAppDownloadCard, 'app_cta') resource_property(ImageAppDownloadCard, 'iphone_app_id') resource_property(ImageAppDownloadCard, 'iphone_deep_link') @@ -263,7 +234,7 @@ class VideoAppDownloadCard(Resource, Persistence): resource_property(VideoAppDownloadCard, 'video_poster_url', readonly=True) resource_property(VideoAppDownloadCard, 'video_url', readonly=True) # writable -resource_property(VideoAppDownloadCard, 'app_country_code') +resource_property(VideoAppDownloadCard, 'country_code') resource_property(VideoAppDownloadCard, 'app_cta') resource_property(VideoAppDownloadCard, 'image_media_id') resource_property(VideoAppDownloadCard, 'ipad_app_id') @@ -521,7 +492,7 @@ def reload(self): # card properties # read-only -resource_property(CardsFetch, 'app_country_code', readonly=True) +resource_property(CardsFetch, 'country_code', readonly=True) resource_property(CardsFetch, 'app_cta', readonly=True) resource_property(CardsFetch, 'card_type', readonly=True) resource_property(CardsFetch, 'card_uri', readonly=True) diff --git a/twitter_ads/enum.py b/twitter_ads/enum.py index 374b4c9..2278e0f 100644 --- a/twitter_ads/enum.py +++ b/twitter_ads/enum.py @@ -182,3 +182,9 @@ def enum(**enums): HANDLE='TARGETING_CRITERIA', EVENT='EVENT' ) + +LOOKALIKE_EXPANSION = enum( + BROAD='BROAD', + DEFINED='DEFINED', + EXPANDED='EXPANDED' +)