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/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/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/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_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 4931818..d4fc7ad 100644 --- a/twitter_ads/__init__.py +++ b/twitter_ads/__init__.py @@ -1,8 +1,8 @@ # Copyright (C) 2015 Twitter, Inc. -VERSION = (10, 0, 0) -API_VERSION = '10' +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 c7c87a8..61443a1 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, - Card, VideoWebsiteCard, PromotedTweet) + Card, PromotedTweet) from twitter_ads.audience import CustomAudience from twitter_ads.campaign import (AppList, Campaign, FundingInstrument, LineItem, PromotableUser, TrackingTags, ScheduledPromotedTweet) @@ -154,12 +154,6 @@ def tracking_tags(self, id=None, **kwargs): """ return self._load_resource(TrackingTags, id, **kwargs) - def video_website_cards(self, id=None, **kwargs): - """ - Returns a collection of video website cards available to the current account. - """ - return self._load_resource(VideoWebsiteCard, id, **kwargs) - def cards(self, id=None, **kwargs): """ Returns a collection of Cards available to the current account. 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 71ca4b9..0df63dc 100644 --- a/twitter_ads/campaign.py +++ b/twitter_ads/campaign.py @@ -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) @@ -301,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) diff --git a/twitter_ads/creative.py b/twitter_ads/creative.py index ab150f0..6abd865 100644 --- a/twitter_ads/creative.py +++ b/twitter_ads/creative.py @@ -126,128 +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, 'ios_app_store_identifier') -resource_property(ImageAppDownloadCard, 'ios_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, 'ios_app_store_identifier') -resource_property(VideoAppDownloadCard, 'ios_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 = {} @@ -333,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) @@ -358,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) @@ -596,34 +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' +)