diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 2a7e337..9d80dce 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [pypy2, pypy3, 3.5, 3.6, 3.7] + python-version: [pypy-3.7, 3.5, 3.6, 3.7] os: [macos-latest, ubuntu-latest] steps: diff --git a/examples/audience_summary.py b/examples/audience_estimate.py similarity index 85% rename from examples/audience_summary.py rename to examples/audience_estimate.py index 5f0bdcd..110fe4e 100644 --- a/examples/audience_summary.py +++ b/examples/audience_estimate.py @@ -1,5 +1,5 @@ from twitter_ads.client import Client -from twitter_ads.targeting import AudienceSummary +from twitter_ads.targeting import AudienceEstimate CONSUMER_KEY = 'your consumer key' CONSUMER_SECRET = 'your consumer secret' @@ -35,6 +35,6 @@ ] } -audience_summary = AudienceSummary.load(account=account, params=params) +audience_estimate = AudienceEstimate.load(account=account, params=params) -print (audience_summary.audience_size) +print (audience_estimate.audience_size) diff --git a/examples/cards.py b/examples/cards.py index 79acdcd..9b8aeae 100644 --- a/examples/cards.py +++ b/examples/cards.py @@ -1,14 +1,13 @@ from twitter_ads.client import Client from twitter_ads.creative import Card -from twitter_ads.campaign import Tweet -from twitter_ads.restapi import UserIdLookup +from twitter_ads.http import Request CONSUMER_KEY = 'your consumer key' CONSUMER_SECRET = 'your consumer secret' -ACCESS_TOKEN = 'access token' -ACCESS_TOKEN_SECRET = 'access token secret' -ACCOUNT_ID = 'account id' +ACCESS_TOKEN = 'user access token' +ACCESS_TOKEN_SECRET = 'user access token secret' +ACCOUNT_ID = 'ads account id' # initialize the client client = Client(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET) @@ -16,14 +15,75 @@ # load the advertiser account instance account = client.accounts(ACCOUNT_ID) -# create the card -name = 'video website card' -components = [{"type":"MEDIA","media_key":"13_1191948012077092867"},{"type":"DETAILS","title":"Twitter","destination":{"type":"WEBSITE", "url":"http://twitter.com/"}}] -video_website_card = Card.create(account, name=name, components=components) +# fetch all +card = Card.all(account, card_ids="1502039998987587584").first + +# fetch by card-id +card = Card.load(account=account, id="1502039998987587584") + +# edit card destination.url +card.components= [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/newvalue", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ] + +card.save() +print(card.components) + +# create new card +newcard = Card(account=account) +newcard.name="my new card" +components= [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/login", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ] +newcard.components=components +newcard.save() +print(newcard.id) # get user_id for as_user_id parameter user_id = UserIdLookup.load(account, screen_name='your_twitter_handle_name').id # create a tweet using this new card -Tweet.create(account, text='Created from the SDK', as_user_id=user_id, card_uri=video_website_card.card_uri) +Tweet.create(account, text='Created from the SDK', as_user_id=user_id, card_uri=card.card_uri) # https://twitter.com/apimctestface/status/1372283476615958529 diff --git a/examples/draft_tweet.py b/examples/draft_tweet.py index 0c15e22..03150be 100644 --- a/examples/draft_tweet.py +++ b/examples/draft_tweet.py @@ -22,7 +22,7 @@ # fetch draft tweets from a given account tweets = DraftTweet.all(account) for tweet in tweets: - print(tweet.id_str) + print(tweet.id) print(tweet.text) # create a new draft tweet @@ -30,19 +30,19 @@ draft_tweet.text = 'draft tweet - new' draft_tweet.as_user_id = user_id draft_tweet = draft_tweet.save() -print(draft_tweet.id_str) +print(draft_tweet.id) print(draft_tweet.text) # fetch single draft tweet metadata -tweet_id = draft_tweet.id_str +tweet_id = draft_tweet.id draft_tweet = draft_tweet.load(account, tweet_id) -print(draft_tweet.id_str) +print(draft_tweet.id) print(draft_tweet.text) # update (PUT) metadata draft_tweet.text = 'draft tweet - update' draft_tweet = draft_tweet.save() -print(draft_tweet.id_str) +print(draft_tweet.id) print(draft_tweet.text) # create a nullcasted tweet using draft tweet metadata diff --git a/tests/fixtures/accounts_all.json b/tests/fixtures/accounts_all.json index d265699..7ab5ba1 100644 --- a/tests/fixtures/accounts_all.json +++ b/tests/fixtures/accounts_all.json @@ -9,7 +9,6 @@ "timezone_switch_at": "2014-11-17T08:00:00Z", "id": "2iqph", "created_at": "2015-03-04T10:50:42Z", - "salt": "5ab2pizq7qxjjqrx3z67f4wbko61o7xs", "updated_at": "2015-04-11T05:20:08Z", "approval_status": "ACCEPTED", "deleted": false @@ -20,7 +19,6 @@ "timezone_switch_at": "2014-11-17T08:00:00Z", "id": "pz6ec", "created_at": "2015-05-29T00:52:16Z", - "salt": "39ku32xvhdt0jax8thps2c70e2fv3yok", "updated_at": "2015-05-29T00:52:16Z", "approval_status": "ACCEPTED", "deleted": false @@ -31,7 +29,6 @@ "timezone_switch_at": "2014-11-17T08:00:00Z", "id": "j9ozo", "created_at": "2015-05-01T12:08:10Z", - "salt": "winwfne3y6oyikl4tm84bj9r50waxj37", "updated_at": "2015-05-01T12:08:10Z", "approval_status": "ACCEPTED", "deleted": false @@ -42,7 +39,6 @@ "timezone_switch_at": "2014-11-17T08:00:00Z", "id": "9ttgd", "created_at": "2015-06-24T18:51:20Z", - "salt": "tj9hmt5xylm5zztrq05w7hwh4mkpkg5r", "updated_at": "2015-06-26T06:13:24Z", "approval_status": "ACCEPTED", "deleted": false @@ -53,7 +49,6 @@ "timezone_switch_at": "2013-05-22T07:00:00Z", "id": "47d0v", "created_at": "2015-05-28T05:42:03Z", - "salt": "1ms1mq1nww7zl7169865gwqt89s9127m", "updated_at": "2015-05-28T05:42:03Z", "approval_status": "ACCEPTED", "deleted": false diff --git a/tests/fixtures/analytics_async_get.json b/tests/fixtures/analytics_async_get.json index 05f1fa9..d189c01 100644 --- a/tests/fixtures/analytics_async_get.json +++ b/tests/fixtures/analytics_async_get.json @@ -12,7 +12,7 @@ "start_time": "2019-01-01T00:00:00Z", "segmentation_type": null, "url": "https://ton.twimg.com/advertiser-api-async-analytics/stats.json.gz", - "id_str": "111111111111111111", + "id": "111111111111111111", "entity_ids": [ "aaaa" ], diff --git a/tests/fixtures/analytics_async_post.json b/tests/fixtures/analytics_async_post.json index 8140e70..ec5cd54 100644 --- a/tests/fixtures/analytics_async_post.json +++ b/tests/fixtures/analytics_async_post.json @@ -18,7 +18,7 @@ "start_time": "2019-01-01T00:00:00Z", "segmentation_type": null, "url": null, - "id_str": "111111111111111111", + "id": "111111111111111111", "entity_ids": [ "aaaa" ], diff --git a/tests/fixtures/audience_summary.json b/tests/fixtures/audience_estimate.json similarity index 100% rename from tests/fixtures/audience_summary.json rename to tests/fixtures/audience_estimate.json diff --git a/tests/fixtures/cards_all.json b/tests/fixtures/cards_all.json new file mode 100644 index 0000000..1feec12 --- /dev/null +++ b/tests/fixtures/cards_all.json @@ -0,0 +1,836 @@ +{ + "request": { + "params": { + "account_id": "2iqph" + } + }, + "next_cursor": null, + "data": [ + { + "name": "website carousel", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "https://www.dell.de/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1340029888649076737", + "created_at": "2020-12-18T20:23:16Z", + "card_uri": "card://1340029888649076737", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410461519343591424", + "created_at": "2021-07-01T04:53:25Z", + "card_uri": "card://1410461519343591424", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410461595289853954", + "created_at": "2021-07-01T04:53:44Z", + "card_uri": "card://1410461595289853954", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410461798390591491", + "created_at": "2021-07-01T04:54:32Z", + "card_uri": "card://1410461798390591491", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410461877499359237", + "created_at": "2021-07-01T04:54:51Z", + "card_uri": "card://1410461877499359237", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410461997494276100", + "created_at": "2021-07-01T04:55:19Z", + "card_uri": "card://1410461997494276100", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410462120282521603", + "created_at": "2021-07-01T04:55:49Z", + "card_uri": "card://1410462120282521603", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410462217586102272", + "created_at": "2021-07-01T04:56:12Z", + "card_uri": "card://1410462217586102272", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410462382632042496", + "created_at": "2021-07-01T04:56:51Z", + "card_uri": "card://1410462382632042496", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410702122887237632", + "created_at": "2021-07-01T20:49:30Z", + "card_uri": "card://1410702122887237632", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410702325044301827", + "created_at": "2021-07-01T20:50:18Z", + "card_uri": "card://1410702325044301827", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410702487212883969", + "created_at": "2021-07-01T20:50:57Z", + "card_uri": "card://1410702487212883969", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410702742541148162", + "created_at": "2021-07-01T20:51:58Z", + "card_uri": "card://1410702742541148162", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410703177888849923", + "created_at": "2021-07-01T20:53:41Z", + "card_uri": "card://1410703177888849923", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "video website card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1410703377667821568", + "created_at": "2021-07-01T20:54:29Z", + "card_uri": "card://1410703377667821568", + "updated_at": "2021-08-26T19:09:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "new card pytest", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1502039535651196928", + "created_at": "2022-03-10T21:51:46Z", + "card_uri": "card://1502039535651196928", + "updated_at": "2022-03-10T21:51:46Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "new card pytest", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1502039980394254337", + "created_at": "2022-03-10T21:53:32Z", + "card_uri": "card://1502039980394254337", + "updated_at": "2022-03-10T21:53:32Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "new card pytest", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/newvalue", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1502039983049232388", + "created_at": "2022-03-10T21:53:33Z", + "card_uri": "card://1502039983049232388", + "updated_at": "2022-03-10T21:53:33Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "new card pytest", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1502039996441628673", + "created_at": "2022-03-10T21:53:36Z", + "card_uri": "card://1502039996441628673", + "updated_at": "2022-03-10T21:53:36Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "new card pytest", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/newvalue", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1502039998987587584", + "created_at": "2022-03-10T21:53:36Z", + "card_uri": "card://1502039998987587584", + "updated_at": "2022-03-10T21:53:36Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "my new card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/login", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1503829009708175360", + "created_at": "2022-03-15T20:22:30Z", + "card_uri": "card://1503829009708175360", + "updated_at": "2022-03-15T20:22:30Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "my new card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/login", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1503829523753697280", + "created_at": "2022-03-15T20:24:32Z", + "card_uri": "card://1503829523753697280", + "updated_at": "2022-03-15T20:24:32Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "my new card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/login", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1503829695233617920", + "created_at": "2022-03-15T20:25:13Z", + "card_uri": "card://1503829695233617920", + "updated_at": "2022-03-15T20:25:13Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "my new card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/login", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1503829883746598912", + "created_at": "2022-03-15T20:25:58Z", + "card_uri": "card://1503829883746598912", + "updated_at": "2022-03-15T20:25:58Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + }, + { + "name": "my new card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/login", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1503831318555086849", + "created_at": "2022-03-15T20:31:40Z", + "card_uri": "card://1503831318555086849", + "updated_at": "2022-03-15T20:31:40Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + } + ] + } + \ No newline at end of file diff --git a/tests/fixtures/cards_load.json b/tests/fixtures/cards_load.json new file mode 100644 index 0000000..502f874 --- /dev/null +++ b/tests/fixtures/cards_load.json @@ -0,0 +1,42 @@ +{ + "request": { + "params": { + "account_id": "2iqph", + "card_id": "1503831318555086849" + } + }, + "data": { + "name": "my new card", + "components": [ + { + "media_key": "13_794652834998325248", + "media_metadata": { + "13_794652834998325248": { + "type": "VIDEO", + "url": "https://video.twimg.com/amplify_video/794652834998325248/vid/640x360/pUgE2UKcfPwF_5Uh.mp4", + "width": 640, + "height": 360, + "video_duration": 7967, + "video_aspect_ratio": "16:9" + } + }, + "type": "MEDIA" + }, + { + "title": "Twitter", + "destination": { + "url": "http://twitter.com/login", + "type": "WEBSITE" + }, + "type": "DETAILS" + } + ], + "id": "1503831318555086849", + "created_at": "2022-03-15T20:31:40Z", + "card_uri": "card://1503831318555086849", + "updated_at": "2022-03-15T20:31:40Z", + "deleted": false, + "card_type": "VIDEO_WEBSITE" + } + } + \ No newline at end of file diff --git a/tests/fixtures/line_items_all.json b/tests/fixtures/line_items_all.json index 24bf7d3..a886d6c 100644 --- a/tests/fixtures/line_items_all.json +++ b/tests/fixtures/line_items_all.json @@ -13,7 +13,6 @@ "ALL_ON_TWITTER" ], "bid_amount_local_micro": 2000000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -39,7 +38,6 @@ "ALL_ON_TWITTER" ], "bid_amount_local_micro": 2000000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -65,7 +63,6 @@ "TWITTER_SEARCH" ], "bid_amount_local_micro": 100000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -91,7 +88,6 @@ "TWITTER_SEARCH" ], "bid_amount_local_micro": 500000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -117,7 +113,6 @@ "TWITTER_TIMELINE" ], "bid_amount_local_micro": 50000000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -143,7 +138,6 @@ "TWITTER_TIMELINE" ], "bid_amount_local_micro": 50000000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -169,7 +163,6 @@ "TWITTER_SEARCH" ], "bid_amount_local_micro": 50000000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -195,7 +188,6 @@ "TWITTER_TIMELINE" ], "bid_amount_local_micro": 500000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -221,7 +213,6 @@ "TWITTER_SEARCH" ], "bid_amount_local_micro": 50000000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", @@ -247,7 +238,6 @@ "TWITTER_TIMELINE" ], "bid_amount_local_micro": 2009999, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", diff --git a/tests/fixtures/line_items_load.json b/tests/fixtures/line_items_load.json index 7af7320..b5607d3 100644 --- a/tests/fixtures/line_items_load.json +++ b/tests/fixtures/line_items_load.json @@ -8,7 +8,6 @@ "ALL_ON_TWITTER" ], "bid_amount_local_micro": 2000000, - "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", diff --git a/tests/fixtures/tweets_get.json b/tests/fixtures/tweets_get.json index b51fe27..4760c6f 100644 --- a/tests/fixtures/tweets_get.json +++ b/tests/fixtures/tweets_get.json @@ -28,14 +28,13 @@ "favorite_count": 0, "in_reply_to_status_id_str": null, "geo": null, - "id_str": "1166476031668015104", + "id": "1166476031668015104", "scopes": { "followers": false }, "in_reply_to_user_id": null, "truncated": false, "retweet_count": 0, - "id": 1166476031668015104, "in_reply_to_status_id": null, "nullcast": true, "created_at": "Tue Aug 27 22:22:12 +0000 2019", @@ -51,10 +50,9 @@ "in_reply_to_screen_name": null, "in_reply_to_user_id_str": null, "user": { - "id": 756201191646691328, - "id_str": "756201191646691328" + "id": "756201191646691328" }, "tweet_id": "1166476031668015104" } ] -} \ No newline at end of file +} diff --git a/tests/test_analytics_async.py b/tests/test_analytics_async.py index 0b6e123..d051e94 100644 --- a/tests/test_analytics_async.py +++ b/tests/test_analytics_async.py @@ -72,7 +72,7 @@ def test_analytics_async(): assert stats2.concurrent_job_limit == '100' # call async_stats_job_result() through Campaign class (inheritance) - job_id = stats.id_str + job_id = stats.id job_result = Campaign.async_stats_job_result( account, job_ids=[job_id]).first diff --git a/tests/test_audience_summary.py b/tests/test_audience_summary.py index 20bfa67..995564d 100644 --- a/tests/test_audience_summary.py +++ b/tests/test_audience_summary.py @@ -5,7 +5,7 @@ from twitter_ads.account import Account from twitter_ads.client import Client -from twitter_ads.targeting import AudienceSummary +from twitter_ads.targeting import AudienceEstimate from twitter_ads import API_VERSION @@ -17,8 +17,8 @@ def test_audience_summary(): content_type='application/json') responses.add(responses.POST, - with_resource('/' + API_VERSION + '/accounts/2iqph/audience_summary'), - body=with_fixture('audience_summary'), + with_resource('/' + API_VERSION + '/accounts/2iqph/audience_estimate'), + body=with_fixture('audience_estimate'), content_type='application/json') client = Client( @@ -51,7 +51,7 @@ def test_audience_summary(): ] } - audience_summary = AudienceSummary.load( + audience_summary = AudienceEstimate.load( account=account, params=params ) diff --git a/tests/test_cards.py b/tests/test_cards.py new file mode 100644 index 0000000..f168b62 --- /dev/null +++ b/tests/test_cards.py @@ -0,0 +1,69 @@ +import responses +import unittest + +from tests.support import with_resource, with_fixture, characters + +from twitter_ads.account import Account +from twitter_ads.campaign import Campaign +from twitter_ads.creative import Card +from twitter_ads.client import Client +from twitter_ads.cursor import Cursor +from twitter_ads import API_VERSION + + +@responses.activate +def test_cards_all(): + responses.add(responses.GET, + with_resource('/' + API_VERSION + '/accounts/2iqph'), + body=with_fixture('accounts_load'), + content_type='application/json') + + responses.add(responses.GET, + with_resource('/' + API_VERSION + '/accounts/2iqph/cards'), + body=with_fixture('cards_all'), + content_type='application/json') + + client = Client( + characters(40), + characters(40), + characters(40), + characters(40) + ) + + account = Account.load(client, '2iqph') + + cursor = Card.all(account) + assert cursor is not None + assert isinstance(cursor, Cursor) + + card = cursor.next() + assert card.id == '1340029888649076737' + assert card.card_type == 'VIDEO_WEBSITE' + assert len(card.components) == 2 + + +@responses.activate +def test_card_load(): + responses.add(responses.GET, + with_resource('/' + API_VERSION + '/accounts/2iqph'), + body=with_fixture('accounts_load'), + content_type='application/json') + + responses.add(responses.GET, + with_resource('/' + API_VERSION + '/accounts/2iqph/cards/1503831318555086849'), + body=with_fixture('cards_load'), + content_type='application/json') + + client = Client( + characters(40), + characters(40), + characters(40), + characters(40) + ) + + account = Account.load(client, '2iqph') + + card = Card.load(account, '1503831318555086849') + assert card.id == '1503831318555086849' + assert card.card_type == 'VIDEO_WEBSITE' + assert card.card_uri == 'card://1503831318555086849' diff --git a/twitter_ads/__init__.py b/twitter_ads/__init__.py index bd1a77f..d4fc7ad 100644 --- a/twitter_ads/__init__.py +++ b/twitter_ads/__init__.py @@ -1,7 +1,8 @@ # Copyright (C) 2015 Twitter, Inc. -VERSION = (9, 0, 1) -API_VERSION = '9' + +VERSION = (11, 0, 0) +API_VERSION = '11' from twitter_ads.utils import get_version diff --git a/twitter_ads/account.py b/twitter_ads/account.py index 88cc2a4..61443a1 100644 --- a/twitter_ads/account.py +++ b/twitter_ads/account.py @@ -10,10 +10,10 @@ from twitter_ads.resource import resource_property, Resource from twitter_ads.creative import (AccountMedia, MediaCreative, ScheduledTweet, - Card, VideoWebsiteCard, PromotedTweet) + Card, PromotedTweet) from twitter_ads.audience import CustomAudience from twitter_ads.campaign import (AppList, Campaign, FundingInstrument, LineItem, - PromotableUser, ScheduledPromotedTweet) + PromotableUser, TrackingTags, ScheduledPromotedTweet) class Account(Resource): @@ -148,11 +148,11 @@ def scheduled_promoted_tweets(self, id=None, **kwargs): """ return self._load_resource(ScheduledPromotedTweet, id, **kwargs) - def video_website_cards(self, id=None, **kwargs): + def tracking_tags(self, id=None, **kwargs): """ - Returns a collection of video website cards available to the current account. + Returns a collection of Tracking Tags available to the current account. """ - return self._load_resource(VideoWebsiteCard, id, **kwargs) + return self._load_resource(TrackingTags, id, **kwargs) def cards(self, id=None, **kwargs): """ @@ -164,7 +164,6 @@ def cards(self, id=None, **kwargs): # account properties resource_property(Account, 'id', readonly=True) resource_property(Account, 'name', readonly=True) -resource_property(Account, 'salt', readonly=True) resource_property(Account, 'timezone', readonly=True) resource_property(Account, 'approval_status', readonly=True) resource_property(Account, 'deleted', readonly=True, transform=TRANSFORM.BOOL) diff --git a/twitter_ads/analytics.py b/twitter_ads/analytics.py index 1690b9f..3d6458e 100644 --- a/twitter_ads/analytics.py +++ b/twitter_ads/analytics.py @@ -147,7 +147,6 @@ def active_entities(klass, account, start_time, end_time, **kwargs): # Analytics properties # read-only resource_property(Analytics, 'id', readonly=True) -resource_property(Analytics, 'id_str', readonly=True) resource_property(Analytics, 'status', readonly=True) resource_property(Analytics, 'url', readonly=True) resource_property(Analytics, 'created_at', readonly=True, transform=TRANSFORM.TIME) diff --git a/twitter_ads/campaign.py b/twitter_ads/campaign.py index fbf2717..0df63dc 100644 --- a/twitter_ads/campaign.py +++ b/twitter_ads/campaign.py @@ -141,7 +141,6 @@ def tv_shows(klass, account, **kwargs): resource_property(TargetingCriteria, 'operator_type') resource_property(TargetingCriteria, 'targeting_type') resource_property(TargetingCriteria, 'targeting_value') -resource_property(TargetingCriteria, 'custom_audience_expansion') # sdk-only resource_property(TargetingCriteria, 'to_delete', transform=TRANSFORM.BOOL) @@ -228,6 +227,7 @@ class Campaign(Analytics, Resource, Persistence, Batch): resource_property(Campaign, 'created_at', readonly=True, transform=TRANSFORM.TIME) resource_property(Campaign, 'currency', readonly=True) resource_property(Campaign, 'deleted', readonly=True, transform=TRANSFORM.BOOL) +resource_property(Campaign, 'effective_status', readonly=True) resource_property(Campaign, 'id', readonly=True) resource_property(Campaign, 'reasons_not_servable', readonly=True) resource_property(Campaign, 'servable', readonly=True, transform=TRANSFORM.BOOL) @@ -235,14 +235,13 @@ class Campaign(Analytics, Resource, Persistence, Batch): # writable resource_property(Campaign, 'daily_budget_amount_local_micro') resource_property(Campaign, 'duration_in_days', transform=TRANSFORM.INT) -resource_property(Campaign, 'end_time', transform=TRANSFORM.TIME) resource_property(Campaign, 'entity_status') resource_property(Campaign, 'frequency_cap', transform=TRANSFORM.INT) resource_property(Campaign, 'funding_instrument_id') resource_property(Campaign, 'name') resource_property(Campaign, 'standard_delivery', transform=TRANSFORM.BOOL) -resource_property(Campaign, 'start_time', transform=TRANSFORM.TIME) resource_property(Campaign, 'total_budget_amount_local_micro') +resource_property(Campaign, 'budget_optimization') # sdk-only resource_property(Campaign, 'to_delete', transform=TRANSFORM.BOOL) @@ -267,13 +266,6 @@ def targeting_criteria(self, id=None, **kwargs): return TargetingCriteria.load(self.account, id, **kwargs) def save(self): - # automatically_select_bid and bid_type are exclusive parameters - if self.automatically_select_bid and self.bid_type: - if self.bid_type == 'AUTO': - self.bid_type = None - self.automatically_select_bid = True - else: - self.automatically_select_bid = None super(LineItem, self).save() @@ -289,7 +281,6 @@ def save(self): # writable resource_property(LineItem, 'advertiser_domain') resource_property(LineItem, 'advertiser_user_id') -resource_property(LineItem, 'automatically_select_bid', transform=TRANSFORM.BOOL) resource_property(LineItem, 'bid_amount_local_micro') resource_property(LineItem, 'bid_strategy') resource_property(LineItem, 'campaign_id') @@ -309,6 +300,7 @@ def save(self): resource_property(LineItem, 'start_time', transform=TRANSFORM.TIME) resource_property(LineItem, 'total_budget_amount_local_micro') resource_property(LineItem, 'tracking_tags') +resource_property(Campaign, 'standard_delivery', transform=TRANSFORM.BOOL) # sdk-only resource_property(LineItem, 'to_delete', transform=TRANSFORM.BOOL) @@ -360,6 +352,26 @@ class ScheduledPromotedTweet(Resource, Persistence): resource_property(ScheduledPromotedTweet, 'scheduled_tweet_id') +class TrackingTags(Resource, Persistence): + + PROPERTIES = {} + + RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/tracking_tags' + RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/tracking_tags/{id}' + + +# tracking tags properties +# read-only +resource_property(TrackingTags, 'created_at', readonly=True, transform=TRANSFORM.TIME) +resource_property(TrackingTags, 'id', readonly=True) +resource_property(TrackingTags, 'deleted', readonly=True, transform=TRANSFORM.BOOL) +resource_property(TrackingTags, 'updated_at', readonly=True, transform=TRANSFORM.TIME) +# writable +resource_property(TrackingTags, 'line_item_id') +resource_property(TrackingTags, 'tracking_tag_type') +resource_property(TrackingTags, 'tracking_tag_url') + + class Tweet(object): TWEET_CREATE = '/' + API_VERSION + '/accounts/{account_id}/tweet' diff --git a/twitter_ads/creative.py b/twitter_ads/creative.py index ac2a36a..6abd865 100644 --- a/twitter_ads/creative.py +++ b/twitter_ads/creative.py @@ -126,132 +126,6 @@ class MediaCreative(Analytics, Resource, Persistence): resource_property(MediaCreative, 'line_item_id') -class WebsiteCard(Resource, Persistence): - - PROPERTIES = {} - - RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/cards/website' - RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/cards/website/{id}' - - -# website card properties -# read-only -resource_property(WebsiteCard, 'card_type', readonly=True) -resource_property(WebsiteCard, 'card_uri', readonly=True) -resource_property(WebsiteCard, 'created_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(WebsiteCard, 'id', readonly=True) -resource_property(WebsiteCard, 'media_url', readonly=True) -resource_property(WebsiteCard, 'image_display_height', readonly=True) -resource_property(WebsiteCard, 'image_display_width', readonly=True) -resource_property(WebsiteCard, 'deleted', readonly=True, transform=TRANSFORM.BOOL) -resource_property(WebsiteCard, 'website_dest_url', readonly=True) -resource_property(WebsiteCard, 'website_display_url', readonly=True) -resource_property(WebsiteCard, 'updated_at', readonly=True, transform=TRANSFORM.TIME) -# writable -resource_property(WebsiteCard, 'media_key') -resource_property(WebsiteCard, 'name') -resource_property(WebsiteCard, 'website_title') -resource_property(WebsiteCard, 'website_url') - - -class VideoWebsiteCard(Resource, Persistence): - - PROPERTIES = {} - - RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/cards/video_website' - RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/cards/video_website/{id}' - - -# video website card properties -# read-only -resource_property(VideoWebsiteCard, 'account_id', readonly=True) -resource_property(VideoWebsiteCard, 'card_type', readonly=True) -resource_property(VideoWebsiteCard, 'card_uri', readonly=True) -resource_property(VideoWebsiteCard, 'created_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(VideoWebsiteCard, 'deleted', readonly=True, transform=TRANSFORM.BOOL) -resource_property(VideoWebsiteCard, 'id', readonly=True) -resource_property(VideoWebsiteCard, 'updated_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(VideoWebsiteCard, 'video_height', readonly=True) -resource_property(VideoWebsiteCard, 'video_owner_id', readonly=True) -resource_property(VideoWebsiteCard, 'video_poster_height', readonly=True) -resource_property(VideoWebsiteCard, 'poster_media_url', readonly=True) -resource_property(VideoWebsiteCard, 'video_poster_width', readonly=True) -resource_property(VideoWebsiteCard, 'media_url', readonly=True) -resource_property(VideoWebsiteCard, 'video_width', readonly=True) -resource_property(VideoWebsiteCard, 'website_dest_url', readonly=True) -resource_property(VideoWebsiteCard, 'website_display_url', readonly=True) -# writable -resource_property(VideoWebsiteCard, 'name') -resource_property(VideoWebsiteCard, 'title') -resource_property(VideoWebsiteCard, 'media_key') -resource_property(VideoWebsiteCard, 'website_url') - - -class ImageAppDownloadCard(Resource, Persistence): - - PROPERTIES = {} - - RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/cards/image_app_download' - RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/cards/image_app_download/{id}' - - -# image app download card properties -# read-only -resource_property(ImageAppDownloadCard, 'id', readonly=True) -resource_property(ImageAppDownloadCard, 'image_display_height', readonly=True) -resource_property(ImageAppDownloadCard, 'image_display_width', readonly=True) -resource_property(ImageAppDownloadCard, 'media_url', readonly=True) -resource_property(ImageAppDownloadCard, 'card_uri', readonly=True) -resource_property(ImageAppDownloadCard, 'card_type', readonly=True) -resource_property(ImageAppDownloadCard, 'created_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(ImageAppDownloadCard, 'updated_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(ImageAppDownloadCard, 'deleted', readonly=True, transform=TRANSFORM.BOOL) -# writable -resource_property(ImageAppDownloadCard, 'country_code') -resource_property(ImageAppDownloadCard, 'app_cta') -resource_property(ImageAppDownloadCard, 'iphone_app_id') -resource_property(ImageAppDownloadCard, 'iphone_deep_link') -resource_property(ImageAppDownloadCard, 'ipad_app_id') -resource_property(ImageAppDownloadCard, 'ipad_deep_link') -resource_property(ImageAppDownloadCard, 'googleplay_app_id') -resource_property(ImageAppDownloadCard, 'googleplay_deep_link') -resource_property(ImageAppDownloadCard, 'name') -resource_property(ImageAppDownloadCard, 'media_key') - - -class VideoAppDownloadCard(Resource, Persistence): - - PROPERTIES = {} - - RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/cards/video_app_download' - RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/cards/video_app_download/{id}' - - -# video app download card properties -# read-only -resource_property(VideoAppDownloadCard, 'card_uri', readonly=True) -resource_property(VideoAppDownloadCard, 'card_type', readonly=True) -resource_property(VideoAppDownloadCard, 'created_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(VideoAppDownloadCard, 'deleted', readonly=True, transform=TRANSFORM.BOOL) -resource_property(VideoAppDownloadCard, 'id', readonly=True) -resource_property(VideoAppDownloadCard, 'updated_at', readonly=True, transform=TRANSFORM.TIME) -resource_property(VideoAppDownloadCard, 'video_owner_id', readonly=True) -resource_property(VideoAppDownloadCard, 'poster_media_url', readonly=True) -resource_property(VideoAppDownloadCard, 'media_url', readonly=True) -# writable -resource_property(VideoAppDownloadCard, 'country_code') -resource_property(VideoAppDownloadCard, 'app_cta') -resource_property(VideoAppDownloadCard, 'poster_media_key') -resource_property(VideoAppDownloadCard, 'ipad_app_id') -resource_property(VideoAppDownloadCard, 'ipad_deep_link') -resource_property(VideoAppDownloadCard, 'iphone_app_id') -resource_property(VideoAppDownloadCard, 'iphone_deep_link') -resource_property(VideoAppDownloadCard, 'googleplay_app_id') -resource_property(VideoAppDownloadCard, 'googleplay_deep_link') -resource_property(VideoAppDownloadCard, 'name') -resource_property(VideoAppDownloadCard, 'media_key') - - class ImageConversationCard(Resource, Persistence): PROPERTIES = {} @@ -337,7 +211,6 @@ class ScheduledTweet(Resource, Persistence): resource_property(ScheduledTweet, 'created_at', readonly=True, transform=TRANSFORM.TIME) resource_property(ScheduledTweet, 'completed_at', read_only=True, transform=TRANSFORM.TIME) resource_property(ScheduledTweet, 'id', read_only=True) -resource_property(ScheduledTweet, 'id_str', read_only=True) resource_property(ScheduledTweet, 'scheduled_status', read_only=True) resource_property(ScheduledTweet, 'tweet_id', readonly=True) resource_property(ScheduledTweet, 'updated_at', readonly=True, transform=TRANSFORM.TIME) @@ -362,7 +235,6 @@ class DraftTweet(Resource, Persistence): # draft tweet properties # read-only resource_property(DraftTweet, 'id', read_only=True) -resource_property(DraftTweet, 'id_str', read_only=True) resource_property(DraftTweet, 'created_at', read_only=True, transform=TRANSFORM.TIME) resource_property(DraftTweet, 'updated_at', readonly=True, transform=TRANSFORM.TIME) resource_property(DraftTweet, 'user_id', read_only=True) @@ -530,10 +402,8 @@ def reload(self): resource_property(CardsFetch, 'image', readonly=True) resource_property(CardsFetch, 'image_display_height', readonly=True) resource_property(CardsFetch, 'image_display_width', readonly=True) -resource_property(CardsFetch, 'ipad_app_id', readonly=True) -resource_property(CardsFetch, 'ipad_deep_link', readonly=True) -resource_property(CardsFetch, 'iphone_app_id', readonly=True) -resource_property(CardsFetch, 'iphone_deep_link', readonly=True) +resource_property(CardsFetch, 'ios_app_store_identifier', readonly=True) +resource_property(CardsFetch, 'ios_deep_link', readonly=True) resource_property(CardsFetch, 'name', readonly=True) resource_property(CardsFetch, 'recipient_user_id', readonly=True) resource_property(CardsFetch, 'second_choice', readonly=True) @@ -602,33 +472,44 @@ class Card(Resource): PROPERTIES = {} + RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/cards/{id}' RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts/{account_id}/cards' - @classmethod - def create(klass, account, name, components): - method = 'post' - resource = klass.RESOURCE_COLLECTION.format(account_id=account.id) + def save(self): + if self.id: + method = 'put' + resource = self.RESOURCE.format(account_id=self.account.id, id=self.id) + else: + method = 'post' + resource = self.RESOURCE_COLLECTION.format(account_id=self.account.id) + headers = {'Content-Type': 'application/json'} - payload = {'name': name, 'components': components} - response = Request( - account.client, method, resource, - headers=headers, body=json.dumps(payload) - ).perform() - return klass(account).from_response(response.body['data']) + payload = {'name': self.name, 'components': self.components} + response = Request(self.account.client, method, resource, headers=headers, + body=json.dumps(payload) + ).perform() + return self.from_response(response.body['data']) - def load(klass): - raise AttributeError("'Card' object has no attribute 'load'") + @classmethod + @FlattenParams + def load(klass, account, id, **kwargs): + resource = klass.RESOURCE.format(account_id=account.id, id=id) + response = Request(account.client, 'get', resource, params=kwargs).perform() + return klass(account).from_response(response.body['data']) - def reload(klass): - raise AttributeError("'Card' object has no attribute 'reload'") + def reload(self): + if self.id: + self.load(self.account, card_id=self.id) # card properties # read-only +resource_property(Card, 'id', readonly=True) resource_property(Card, 'card_uri', readonly=True) +resource_property(Card, 'card_type', readonly=True) resource_property(Card, 'created_at', readonly=True, transform=TRANSFORM.TIME) resource_property(Card, 'deleted', readonly=True, transform=TRANSFORM.BOOL) resource_property(Card, 'updated_at', readonly=True, transform=TRANSFORM.TIME) -# these are writable, but not in the sense that they can be set on an object and then saved -resource_property(Card, 'name', readonly=True) -resource_property(Card, 'components', readonly=True, transform=TRANSFORM.LIST) +# these are writable +resource_property(Card, 'name') +resource_property(Card, 'components', transform=TRANSFORM.LIST) diff --git a/twitter_ads/enum.py b/twitter_ads/enum.py index 9ab2716..741fc45 100644 --- a/twitter_ads/enum.py +++ b/twitter_ads/enum.py @@ -226,3 +226,7 @@ def enum(**enums): SIMILAR_TO_FOLLOWERS_OF_USER='SIMILAR_TO_FOLLOWERS_OF_USER', TV_SHOWS='TV_SHOWS' ) + +BUDGET_OPTIMIZATION = enum( + CAMPAIGN='CAMPAIGN' +) diff --git a/twitter_ads/targeting.py b/twitter_ads/targeting.py index b185ba0..8253f1c 100644 --- a/twitter_ads/targeting.py +++ b/twitter_ads/targeting.py @@ -9,10 +9,10 @@ import json -class AudienceSummary(Resource, Persistence): +class AudienceEstimate(Resource, Persistence): PROPERTIES = {} - RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/audience_summary' + RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/audience_estimate' @classmethod @FlattenParams @@ -27,4 +27,4 @@ def load(klass, account, params): return klass(account).from_response(response.body['data']) -resource_property(AudienceSummary, 'audience_size') +resource_property(AudienceEstimate, 'audience_size')