Skip to content

Commit b5e11cc

Browse files
authored
Add new decorator (#226)
1 parent 43074bd commit b5e11cc

File tree

5 files changed

+52
-66
lines changed

5 files changed

+52
-66
lines changed

tests/test_analytics_async.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_analytics_async():
7575
job_id = stats.id_str
7676
job_result = Campaign.async_stats_job_result(
7777
account,
78-
[job_id]).first
78+
job_ids=[job_id]).first
7979

8080
assert job_result is not None
8181
assert isinstance(job_result, Analytics)
@@ -84,7 +84,7 @@ def test_analytics_async():
8484
# call async_stats_job_result() from Analytics class directly
8585
job_result = Analytics.async_stats_job_result(
8686
account,
87-
[job_id]).first
87+
job_ids=[job_id]).first
8888

8989
assert job_result is not None
9090
assert isinstance(job_result, Analytics)

twitter_ads/campaign.py

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from twitter_ads.resource import resource_property, Resource, Persistence, Batch, Analytics
77
from twitter_ads.http import Request
88
from twitter_ads.cursor import Cursor
9+
from twitter_ads.utils import FlattenParams
910
from twitter_ads import API_VERSION
1011

1112

@@ -20,14 +21,11 @@ class TargetingCriteria(Resource, Persistence, Batch):
2021
RESOURCE_OPTIONS = '/' + API_VERSION + '/targeting_criteria/'
2122

2223
@classmethod
23-
def all(klass, account, line_item_ids, **kwargs):
24+
@FlattenParams
25+
def all(klass, account, **kwargs):
2426
"""Returns a Cursor instance for a given resource."""
25-
params = {'line_item_ids': ','.join(map(str, line_item_ids))}
26-
params.update(kwargs)
27-
2827
resource = klass.RESOURCE_COLLECTION.format(account_id=account.id)
29-
request = Request(account.client, 'get', resource, params=params)
30-
28+
request = Request(account.client, 'get', resource, params=kwargs)
3129
return Cursor(klass, request, init_with=[account])
3230

3331
@classmethod
@@ -202,15 +200,12 @@ class AppList(Resource, Persistence):
202200
RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/app_lists'
203201
RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/app_lists/{id}'
204202

205-
def create(self, name, *ids):
206-
if isinstance(ids, list):
207-
ids = ','.join(map(str, ids))
208-
209-
resource = self.RESOURCE_COLLECTION.format(account_id=self.account.id)
210-
params = self.to_params.update({'app_store_identifiers': ids, 'name': name})
211-
response = Request(self.account.client, 'post', resource, params=params).perform()
212-
213-
return self.from_response(response.body['data'])
203+
@classmethod
204+
@FlattenParams
205+
def create(klass, account, **kwargs):
206+
resource = klass.RESOURCE_COLLECTION.format(account_id=account.id)
207+
response = Request(account.client, 'post', resource, params=kwargs).perform()
208+
return klass(account).from_response(response.body['data'])
214209

215210
def apps(self):
216211
if self.id and not hasattr(self, '_apps'):
@@ -340,19 +335,13 @@ def __init__(self):
340335
'Error! {name} cannot be instantiated.'.format(name=self.__class__.__name__))
341336

342337
@classmethod
338+
@FlattenParams
343339
def create(klass, account, **kwargs):
344340
"""
345341
Creates a "Promoted-Only" Tweet using the specialized Ads API end point.
346342
"""
347-
params = {}
348-
params.update(kwargs)
349-
350-
# handles array to string conversion for media keys
351-
if 'media_keys' in params and isinstance(params['media_keys'], list):
352-
params['media_keys'] = ','.join(map(str, params['media_keys']))
353-
354343
resource = klass.TWEET_CREATE.format(account_id=account.id)
355-
response = Request(account.client, 'post', resource, params=params).perform()
344+
response = Request(account.client, 'post', resource, params=kwargs).perform()
356345
return response.body['data']
357346

358347

twitter_ads/creative.py

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from twitter_ads.enum import TRANSFORM
99
from twitter_ads.http import Request
1010
from twitter_ads.resource import resource_property, Resource, Persistence, Analytics
11-
from twitter_ads.utils import Deprecated
11+
from twitter_ads.utils import Deprecated, FlattenParams
1212

1313

1414
class PromotedAccount(Analytics, Resource, Persistence):
@@ -60,16 +60,13 @@ def save(self):
6060
return self.from_response(response.body['data'][0])
6161

6262
@classmethod
63-
def attach(klass, account, line_item_id=None, tweet_ids=None):
63+
@FlattenParams
64+
def attach(klass, account, **kwargs):
6465
"""
6566
Associate one or more Tweets with the specified line item.
6667
"""
67-
params = {}
68-
params['line_item_id'] = line_item_id
69-
params['tweet_ids'] = ",".join(map(str, tweet_ids))
70-
7168
resource = klass.RESOURCE_COLLECTION.format(account_id=account.id)
72-
request = Request(account.client, 'post', resource, params=params)
69+
request = Request(account.client, 'post', resource, params=kwargs)
7370
return Cursor(klass, request, init_with=[account])
7471

7572

@@ -482,24 +479,21 @@ def all(klass):
482479
raise AttributeError("'CardsFetch' object has no attribute 'all'")
483480

484481
@classmethod
485-
def load(klass, account, card_uris=None, card_id=None, with_deleted=None):
482+
@FlattenParams
483+
def load(klass, account, **kwargs):
486484
# check whether both are specified or neither are specified
487-
if all([card_uris, card_id]) or not any([card_uris, card_id]):
485+
if all([kwargs.get('card_uris'), kwargs.get('card_id')]) or \
486+
not any([kwargs.get('card_uris'), kwargs.get('card_id')]):
488487
raise ValueError('card_uris and card_id are exclusive parameters. ' +
489488
'Please supply one or the other, but not both.')
490-
params = {}
491-
if with_deleted:
492-
params['with_deleted'] = 'true'
493489

494-
if card_uris:
495-
params['card_uris'] = ','.join(map(str, card_uris))
490+
if kwargs.get('card_uris'):
496491
resource = klass.FETCH_URI.format(account_id=account.id)
497-
request = Request(account.client, 'get', resource, params=params)
492+
request = Request(account.client, 'get', resource, params=kwargs)
498493
return Cursor(klass, request, init_with=[account])
499494
else:
500-
params['card_id'] = card_id
501-
resource = klass.FETCH_ID.format(account_id=account.id, id=card_id)
502-
response = Request(account.client, 'get', resource, params=params).perform()
495+
resource = klass.FETCH_ID.format(account_id=account.id, id=kwargs.get('card_id'))
496+
response = Request(account.client, 'get', resource, params=kwargs).perform()
503497
return klass(account).from_response(response.body['data'])
504498

505499
def reload(self):
@@ -575,13 +569,10 @@ class TweetPreview(Resource):
575569
RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/tweet_previews'
576570

577571
@classmethod
578-
def load(klass, account, tweet_ids=None, tweet_type=None):
579-
params = {}
580-
581-
params['tweet_ids'] = ','.join(map(str, tweet_ids))
582-
params['tweet_type'] = tweet_type
572+
@FlattenParams
573+
def load(klass, account, **kwargs):
583574
resource = klass.RESOURCE_COLLECTION.format(account_id=account.id)
584-
request = Request(account.client, 'get', resource, params=params)
575+
request = Request(account.client, 'get', resource, params=kwargs)
585576
return Cursor(klass, request, init_with=[account])
586577

587578

twitter_ads/resource.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from twitter_ads.http import Request
1616
from twitter_ads.cursor import Cursor
1717
from twitter_ads import API_VERSION
18-
from twitter_ads.utils import extract_response_headers
18+
from twitter_ads.utils import extract_response_headers, FlattenParams
1919

2020

2121
def resource_property(klass, name, **kwargs):
@@ -295,17 +295,13 @@ def queue_async_stats_job(klass, account, ids, metric_groups, **kwargs):
295295
return Analytics(account).from_response(response.body['data'], headers=response.headers)
296296

297297
@classmethod
298-
def async_stats_job_result(klass, account, job_ids=None, **kwargs):
298+
@FlattenParams
299+
def async_stats_job_result(klass, account, **kwargs):
299300
"""
300301
Returns the results of the specified async job IDs
301302
"""
302-
params = {}
303-
params.update(kwargs)
304-
if isinstance(job_ids, list):
305-
params['job_ids'] = ','.join(map(str, job_ids))
306-
307303
resource = klass.RESOURCE_ASYNC.format(account_id=account.id)
308-
request = Request(account.client, 'get', resource, params=params)
304+
request = Request(account.client, 'get', resource, params=kwargs)
309305

310306
return Cursor(Analytics, request, init_with=[account])
311307

@@ -323,30 +319,26 @@ def async_stats_job_data(klass, account, url, **kwargs):
323319
return response.body
324320

325321
@classmethod
322+
@FlattenParams
326323
def active_entities(klass, account, start_time, end_time, **kwargs):
327324
"""
328325
Returns the details about which entities' analytics metrics
329326
have changed in a given time period.
330327
"""
331-
entity_type = klass.__name__
332-
if entity_type == 'OrganicTweet':
328+
entity = kwargs.get('entity') or klass.ANALYTICS_MAP[klass.__name__]
329+
if entity == klass.ANALYTICS_MAP['OrganicTweet']:
333330
raise ValueError("'OrganicTweet' not support with 'active_entities'")
334331

335332
# The start and end times must be expressed in whole hours
336333
validate_whole_hours(start_time)
337334
validate_whole_hours(end_time)
338335

339336
params = {
340-
'entity': klass.ANALYTICS_MAP[entity_type],
337+
'entity': entity,
341338
'start_time': to_time(start_time, None),
342339
'end_time': to_time(end_time, None)
343340
}
344-
345-
for k in kwargs:
346-
if isinstance(kwargs[k], list):
347-
params[k] = ','.join(map(str, kwargs[k]))
348-
else:
349-
params[k] = kwargs[k]
341+
params.update(kwargs)
350342

351343
resource = klass.RESOURCE_ACTIVE_ENTITIES.format(account_id=account.id)
352344
response = Request(account.client, 'get', resource, params=params).perform()

twitter_ads/utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,17 @@ def wrapper(*args, **kwargs):
102102
)
103103
return decorated(*args, **kwargs)
104104
return wrapper
105+
106+
107+
class FlattenParams(object):
108+
def __init__(self, function):
109+
self._func = function
110+
111+
def __call__(self, *args, **kwargs):
112+
params = kwargs
113+
for i in params:
114+
if isinstance(params[i], list):
115+
params[i] = ','.join(map(str, params[i]))
116+
elif isinstance(params[i], bool):
117+
params[i] = str(params[i]).lower()
118+
return self._func(*args, **params)

0 commit comments

Comments
 (0)