twitter-ads 5.2.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/twitter-ads.rb +4 -1
- data/lib/twitter-ads/account.rb +9 -8
- data/lib/twitter-ads/campaign/campaign.rb +1 -2
- data/lib/twitter-ads/campaign/funding_instrument.rb +1 -2
- data/lib/twitter-ads/campaign/line_item.rb +2 -3
- data/lib/twitter-ads/campaign/organic_tweet.rb +1 -3
- data/lib/twitter-ads/campaign/targeting_criteria.rb +0 -1
- data/lib/twitter-ads/campaign/tweet.rb +4 -49
- data/lib/twitter-ads/client.rb +2 -2
- data/lib/twitter-ads/creative/account_media.rb +4 -6
- data/lib/twitter-ads/creative/draft_tweet.rb +40 -0
- data/lib/twitter-ads/creative/image_app_download_card.rb +2 -2
- data/lib/twitter-ads/creative/image_conversation_card.rb +3 -2
- data/lib/twitter-ads/creative/media_creative.rb +1 -2
- data/lib/twitter-ads/creative/media_library.rb +2 -4
- data/lib/twitter-ads/creative/promoted_account.rb +1 -2
- data/lib/twitter-ads/creative/promoted_tweet.rb +1 -2
- data/lib/twitter-ads/creative/scheduled_tweet.rb +1 -12
- data/lib/twitter-ads/creative/tweets.rb +52 -0
- data/lib/twitter-ads/creative/video_app_download_card.rb +4 -6
- data/lib/twitter-ads/creative/video_conversation_card.rb +6 -6
- data/lib/twitter-ads/creative/video_website_card.rb +3 -5
- data/lib/twitter-ads/creative/website_card.rb +2 -2
- data/lib/twitter-ads/cursor.rb +6 -0
- data/lib/twitter-ads/enum.rb +10 -5
- data/lib/twitter-ads/error.rb +5 -15
- data/lib/twitter-ads/http/request.rb +30 -1
- data/lib/twitter-ads/http/response.rb +1 -13
- data/lib/twitter-ads/resources/analytics.rb +99 -47
- data/lib/twitter-ads/resources/dsl.rb +8 -1
- data/lib/twitter-ads/restapi.rb +29 -0
- data/lib/twitter-ads/settings/tax.rb +13 -1
- data/lib/twitter-ads/targeting_criteria/conversation.rb +23 -0
- data/lib/twitter-ads/utils.rb +23 -0
- data/lib/twitter-ads/version.rb +1 -1
- data/spec/fixtures/tweet_previews.json +23 -0
- data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +0 -1
- data/spec/twitter-ads/campaign/tweet_spec.rb +0 -59
- data/spec/twitter-ads/client_spec.rb +17 -1
- data/spec/twitter-ads/creative/tweet_previews_spec.rb +41 -0
- data/spec/twitter-ads/rate_limit_spec.rb +247 -0
- data/spec/twitter-ads/retry_count_spec.rb +61 -0
- metadata +14 -17
- data/lib/twitter-ads/audiences/audience_intelligence.rb +0 -68
- data/spec/fixtures/tweet_preview.json +0 -24
- data/spec/twitter-ads/creative/account_media_spec.rb +0 -32
- data/spec/twitter-ads/creative/image_app_download_card_spec.rb +0 -43
- data/spec/twitter-ads/creative/image_conversation_card_spec.rb +0 -40
- data/spec/twitter-ads/creative/video_app_download_card_spec.rb +0 -42
- data/spec/twitter-ads/creative/video_conversation_card_spec.rb +0 -51
- data/spec/twitter-ads/creative/website_card_spec.rb +0 -42
@@ -18,15 +18,13 @@ module TwitterAds
|
|
18
18
|
property :deleted, type: :bool, read_only: true
|
19
19
|
property :id, read_only: true
|
20
20
|
property :updated_at, type: :time, read_only: true
|
21
|
-
property :video_content_id, read_only: true
|
22
|
-
property :video_hls_url, read_only: true
|
23
21
|
property :video_owner_id, read_only: true
|
24
|
-
property :
|
25
|
-
property :
|
22
|
+
property :poster_media_url, read_only: true
|
23
|
+
property :media_url, read_only: true
|
26
24
|
|
27
25
|
property :country_code
|
28
26
|
property :app_cta
|
29
|
-
property :
|
27
|
+
property :poster_media_key
|
30
28
|
property :ipad_app_id
|
31
29
|
property :ipad_deep_link
|
32
30
|
property :iphone_app_id
|
@@ -34,7 +32,7 @@ module TwitterAds
|
|
34
32
|
property :googleplay_app_id
|
35
33
|
property :googleplay_deep_link
|
36
34
|
property :name
|
37
|
-
property :
|
35
|
+
property :media_key
|
38
36
|
|
39
37
|
RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \
|
40
38
|
'accounts/%{account_id}/cards/video_app_download' # @api private
|
@@ -18,14 +18,14 @@ module TwitterAds
|
|
18
18
|
property :deleted, type: :bool, read_only: true
|
19
19
|
property :id, read_only: true
|
20
20
|
property :updated_at, type: :time, read_only: true
|
21
|
-
property :
|
22
|
-
property :
|
21
|
+
property :media_url, read_only: true
|
22
|
+
property :poster_media_url, read_only: true
|
23
23
|
|
24
|
-
property :
|
25
|
-
property :
|
24
|
+
property :unlocked_image_media_key
|
25
|
+
property :unlocked_video_media_key
|
26
26
|
property :fourth_cta
|
27
27
|
property :fourth_cta_tweet
|
28
|
-
property :
|
28
|
+
property :poster_media_key
|
29
29
|
property :first_cta
|
30
30
|
property :first_cta_tweet
|
31
31
|
property :name
|
@@ -36,7 +36,7 @@ module TwitterAds
|
|
36
36
|
property :third_cta
|
37
37
|
property :third_cta_tweet
|
38
38
|
property :title
|
39
|
-
property :
|
39
|
+
property :media_key
|
40
40
|
|
41
41
|
RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \
|
42
42
|
'accounts/%{account_id}/cards/video_conversation' # @api private
|
@@ -19,21 +19,19 @@ module TwitterAds
|
|
19
19
|
property :deleted, type: :bool, read_only: true
|
20
20
|
property :id, read_only: true
|
21
21
|
property :updated_at, type: :time, read_only: true
|
22
|
-
property :video_content_id, read_only: true
|
23
22
|
property :video_height, read_only: true
|
24
|
-
property :video_hls_url, read_only: true
|
25
23
|
property :video_owner_id, read_only: true
|
26
24
|
property :video_poster_height, read_only: true
|
27
|
-
property :
|
25
|
+
property :poster_media_url, read_only: true
|
28
26
|
property :video_poster_width, read_only: true
|
29
|
-
property :
|
27
|
+
property :media_url, read_only: true
|
30
28
|
property :video_width, read_only: true
|
31
29
|
property :website_display_url, read_only: true
|
32
30
|
property :website_dest_url, read_only: true
|
33
31
|
|
34
32
|
property :name
|
35
33
|
property :title
|
36
|
-
property :
|
34
|
+
property :media_key
|
37
35
|
property :website_url
|
38
36
|
|
39
37
|
RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/accounts/%{account_id}/cards/video_website"
|
@@ -17,14 +17,14 @@ module TwitterAds
|
|
17
17
|
property :created_at, type: :time, read_only: true
|
18
18
|
property :deleted, type: :bool, read_only: true
|
19
19
|
property :id, read_only: true
|
20
|
-
property :
|
20
|
+
property :media_url, read_only: true
|
21
21
|
property :image_display_height, read_only: true
|
22
22
|
property :image_display_width, read_only: true
|
23
23
|
property :updated_at, type: :time, read_only: true
|
24
24
|
property :website_dest_url, read_only: true
|
25
25
|
property :website_display_url, read_only: true
|
26
26
|
|
27
|
-
property :
|
27
|
+
property :media_key
|
28
28
|
property :name
|
29
29
|
property :website_title
|
30
30
|
property :website_url
|
data/lib/twitter-ads/cursor.rb
CHANGED
@@ -112,6 +112,12 @@ module TwitterAds
|
|
112
112
|
def from_response(response)
|
113
113
|
@next_cursor = response.body[:next_cursor]
|
114
114
|
@total_count = response.body[:total_count].to_i if response.body.key?(:total_count)
|
115
|
+
|
116
|
+
TwitterAds::Utils.extract_response_headers(response.headers).each { |key, value|
|
117
|
+
singleton_class.class_eval { attr_accessor key }
|
118
|
+
instance_variable_set("@#{key}", value)
|
119
|
+
}
|
120
|
+
|
115
121
|
response.body.fetch(:data, []).each do |object|
|
116
122
|
@collection << if @klass&.method_defined?(:from_response)
|
117
123
|
@klass.new(
|
data/lib/twitter-ads/enum.rb
CHANGED
@@ -106,6 +106,7 @@ module TwitterAds
|
|
106
106
|
FUNDING_INSTRUMENT = 'FUNDING_INSTRUMENT'
|
107
107
|
CAMPAIGN = 'CAMPAIGN'
|
108
108
|
LINE_ITEM = 'LINE_ITEM'
|
109
|
+
PROMOTED_ACCOUNT = 'PROMOTED_ACCOUNT'
|
109
110
|
PROMOTED_TWEET = 'PROMOTED_TWEET'
|
110
111
|
ORGANIC_TWEET = 'ORGANIC_TWEET'
|
111
112
|
MEDIA_CREATIVE = 'MEDIA_CREATIVE'
|
@@ -143,7 +144,10 @@ module TwitterAds
|
|
143
144
|
end
|
144
145
|
|
145
146
|
module Optimizations
|
147
|
+
APP_CLICKS = 'APP_CLICKS'
|
148
|
+
APP_INSTALLS = 'APP_INSTALLS'
|
146
149
|
DEFAULT = 'DEFAULT'
|
150
|
+
ENGAGEMENTS = 'ENGAGEMENTS'
|
147
151
|
WEBSITE_CONVERSIONS = 'WEBSITE_CONVERSIONS'
|
148
152
|
end
|
149
153
|
|
@@ -218,11 +222,6 @@ module TwitterAds
|
|
218
222
|
HASHTAG = 'HASHTAG'
|
219
223
|
end
|
220
224
|
|
221
|
-
module AudienceDefinition
|
222
|
-
TARGETING_CRITERIA = 'TARGETING_CRITERIA'
|
223
|
-
KEYWORD_AUDIENCE = 'KEYWORD_AUDIENCE'
|
224
|
-
end
|
225
|
-
|
226
225
|
module LookalikeExpansion
|
227
226
|
DEFINED = 'DEFINED'
|
228
227
|
EXPANDED = 'EXPANDED'
|
@@ -233,5 +232,11 @@ module TwitterAds
|
|
233
232
|
PUBLISHED = 'PUBLISHED'
|
234
233
|
SCHEDULED = 'SCHEDULED'
|
235
234
|
end
|
235
|
+
|
236
|
+
module TimelineType
|
237
|
+
ALL = 'ALL'
|
238
|
+
NULLCAST = 'NULLCAST'
|
239
|
+
ORGANIC = 'ORGANIC'
|
240
|
+
end
|
236
241
|
end
|
237
242
|
end
|
data/lib/twitter-ads/error.rb
CHANGED
@@ -59,18 +59,7 @@ module TwitterAds
|
|
59
59
|
|
60
60
|
# Server Errors (5XX)
|
61
61
|
class ServerError < Error; end
|
62
|
-
|
63
|
-
class ServiceUnavailable < ServerError
|
64
|
-
attr_reader :retry_after
|
65
|
-
|
66
|
-
def initialize(object)
|
67
|
-
super object
|
68
|
-
if object.headers['retry-after']
|
69
|
-
@retry_after = object.headers['retry-after']
|
70
|
-
end
|
71
|
-
self
|
72
|
-
end
|
73
|
-
end
|
62
|
+
class ServiceUnavailable < ServerError; end
|
74
63
|
|
75
64
|
# Client Errors (4XX)
|
76
65
|
class ClientError < Error; end
|
@@ -80,12 +69,13 @@ module TwitterAds
|
|
80
69
|
class BadRequest < ClientError; end
|
81
70
|
|
82
71
|
class RateLimit < ClientError
|
83
|
-
attr_reader :reset_at
|
72
|
+
attr_reader :reset_at
|
84
73
|
|
85
74
|
def initialize(object)
|
86
75
|
super object
|
87
|
-
|
88
|
-
|
76
|
+
header = object.headers.fetch('x-account-rate-limit-reset', nil) ||
|
77
|
+
object.headers.fetch('x-rate-limit-reset', nil)
|
78
|
+
@reset_at = header.first.to_i
|
89
79
|
self
|
90
80
|
end
|
91
81
|
end
|
@@ -71,8 +71,37 @@ module TwitterAds
|
|
71
71
|
token = OAuth::AccessToken.new(consumer, @client.access_token, @client.access_token_secret)
|
72
72
|
request.oauth!(consumer.http, consumer, token)
|
73
73
|
|
74
|
+
handle_rate_limit = @client.options.fetch(:handle_rate_limit, false)
|
75
|
+
retry_max = @client.options.fetch(:retry_max, 0)
|
76
|
+
retry_delay = @client.options.fetch(:retry_delay, 1500)
|
77
|
+
retry_on_status = @client.options.fetch(:retry_on_status, [500, 503])
|
78
|
+
retry_count = 0
|
79
|
+
retry_after = nil
|
80
|
+
|
74
81
|
write_log(request) if @client.options[:trace]
|
75
|
-
|
82
|
+
while retry_count <= retry_max
|
83
|
+
response = consumer.http.request(request)
|
84
|
+
status_code = response.code.to_i
|
85
|
+
break if status_code >= 200 && status_code < 300
|
86
|
+
|
87
|
+
if handle_rate_limit && retry_after.nil?
|
88
|
+
rate_limit_reset = response.fetch('x-account-rate-limit-reset', nil) ||
|
89
|
+
response.fetch('x-rate-limit-reset', nil)
|
90
|
+
if status_code == 429
|
91
|
+
retry_after = rate_limit_reset.to_i - Time.now.to_i
|
92
|
+
@client.logger.warn('Request reached Rate Limit: resume in %d seconds' % retry_after)
|
93
|
+
sleep(retry_after + 5)
|
94
|
+
next
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if retry_max.positive?
|
99
|
+
break unless retry_on_status.include?(status_code)
|
100
|
+
sleep(retry_delay / 1000)
|
101
|
+
end
|
102
|
+
|
103
|
+
retry_count += 1
|
104
|
+
end
|
76
105
|
write_log(response) if @client.options[:trace]
|
77
106
|
|
78
107
|
Response.new(response.code, response.each {}, response.body)
|
@@ -9,9 +9,7 @@ module TwitterAds
|
|
9
9
|
attr_reader :code,
|
10
10
|
:headers,
|
11
11
|
:raw_body,
|
12
|
-
:body
|
13
|
-
:rate_limit_remaining,
|
14
|
-
:rate_limit_reset
|
12
|
+
:body
|
15
13
|
|
16
14
|
# Creates a new Response object instance.
|
17
15
|
#
|
@@ -37,16 +35,6 @@ module TwitterAds
|
|
37
35
|
@body = raw_body
|
38
36
|
end
|
39
37
|
|
40
|
-
if headers.key?('x-rate-limit-reset')
|
41
|
-
@rate_limit = headers['x-rate-limit-limit'].first
|
42
|
-
@rate_limit_remaining = headers['x-rate-limit-remaining'].first
|
43
|
-
@rate_limit_reset = headers['x-rate-limit-reset'].first.to_i
|
44
|
-
elsif headers.key?('x-cost-rate-limit-reset')
|
45
|
-
@rate_limit = headers['x-cost-rate-limit-limit'].first
|
46
|
-
@rate_limit_remaining = headers['x-cost-rate-limit-remaining'].first
|
47
|
-
@rate_limit_reset = Time.at(headers['x-cost-rate-limit-reset'].first.to_i)
|
48
|
-
end
|
49
|
-
|
50
38
|
self
|
51
39
|
end
|
52
40
|
|
@@ -5,15 +5,35 @@ require 'zlib'
|
|
5
5
|
require 'open-uri'
|
6
6
|
|
7
7
|
module TwitterAds
|
8
|
-
|
8
|
+
class Analytics
|
9
9
|
|
10
|
+
include TwitterAds::DSL
|
11
|
+
include TwitterAds::Resource
|
10
12
|
include TwitterAds::Enum
|
11
13
|
|
14
|
+
attr_reader :account
|
15
|
+
|
16
|
+
property :id, read_only: true
|
17
|
+
property :id_str, read_only: true
|
18
|
+
property :status, read_only: true
|
19
|
+
property :url, read_only: true
|
20
|
+
property :created_at, type: :time, read_only: true
|
21
|
+
property :expires_at, type: :time, read_only: true
|
22
|
+
property :updated_at, type: :time, read_only: true
|
23
|
+
property :start_time, type: :time, read_only: true
|
24
|
+
property :end_time, type: :time, read_only: true
|
25
|
+
|
26
|
+
property :entity, read_only: true
|
27
|
+
property :entity_ids, read_only: true
|
28
|
+
property :placement, read_only: true
|
29
|
+
property :granularity, read_only: true
|
30
|
+
property :metric_groups, read_only: true
|
31
|
+
|
12
32
|
ANALYTICS_MAP = {
|
13
33
|
'TwitterAds::Campaign' => Entity::CAMPAIGN,
|
14
34
|
'TwitterAds::LineItem' => Entity::LINE_ITEM,
|
15
35
|
'TwitterAds::OrganicTweet' => Entity::ORGANIC_TWEET,
|
16
|
-
'TwitterAds::Creative::PromotedAccount' => Entity::
|
36
|
+
'TwitterAds::Creative::PromotedAccount' => Entity::PROMOTED_ACCOUNT,
|
17
37
|
'TwitterAds::Creative::PromotedTweet' => Entity::PROMOTED_TWEET,
|
18
38
|
'TwitterAds::Creative::MediaCreative' => Entity::MEDIA_CREATIVE
|
19
39
|
}.freeze
|
@@ -25,42 +45,38 @@ module TwitterAds
|
|
25
45
|
RESOURCE_ACTIVE_ENTITIES = "/#{TwitterAds::API_VERSION}/" +
|
26
46
|
'stats/accounts/%{account_id}/active_entities' # @api private
|
27
47
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
48
|
+
def initialize(account)
|
49
|
+
@account = account
|
50
|
+
self
|
31
51
|
end
|
32
52
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
def stats(metric_groups, opts = {})
|
52
|
-
self.class.stats(account, [id], metric_groups, opts)
|
53
|
-
end
|
54
|
-
|
53
|
+
# Pulls a list of metrics for the current object instance.
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# metric_groups = [MetricGroup::MOBILE_CONVERSION, MetricGroup::ENGAGEMENT]
|
57
|
+
# object.stats(metrics)
|
58
|
+
#
|
59
|
+
# @param metric_groups [Array] A collection of metric groups to fetch.
|
60
|
+
# @param opts [Hash] An optional Hash of extended options.
|
61
|
+
# @option opts [Time] :start_time The starting time to use (default: 7 days ago).
|
62
|
+
# @option opts [Time] :end_time The end time to use (default: now).
|
63
|
+
# @option opts [Symbol] :granularity The granularity to use (default: :hour).
|
64
|
+
#
|
65
|
+
# @return [Array] The collection of stats requested.
|
66
|
+
#
|
67
|
+
# @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation
|
68
|
+
# @since 1.0.0
|
69
|
+
def stats(metric_groups, opts = {})
|
70
|
+
self.class.stats(account, [id], metric_groups, opts)
|
55
71
|
end
|
56
72
|
|
57
|
-
|
73
|
+
class << self
|
58
74
|
|
59
75
|
# Pulls a list of metrics for a specified set of object IDs.
|
60
76
|
#
|
61
77
|
# @example
|
62
78
|
# ids = ['7o4em', 'oc9ce', '1c5lji']
|
63
|
-
# metric_groups = [
|
79
|
+
# metric_groups = [MetricGroup::MOBILE_CONVERSION, MetricGroup::ENGAGEMENT]
|
64
80
|
# object.stats(account, ids, metric_groups)
|
65
81
|
#
|
66
82
|
# @param account [Account] The Account object instance.
|
@@ -70,7 +86,7 @@ module TwitterAds
|
|
70
86
|
# @option opts [Time] :start_time The starting time to use (default: 7 days ago).
|
71
87
|
# @option opts [Time] :end_time The end time to use (default: now).
|
72
88
|
# @option opts [Symbol] :granularity The granularity to use (default: :hour).
|
73
|
-
# @option opts [
|
89
|
+
# @option opts [String] :placement The placement of entity (default: ALL_ON_TWITTER).
|
74
90
|
#
|
75
91
|
# @return [Array] The collection of stats requested.
|
76
92
|
#
|
@@ -108,7 +124,7 @@ module TwitterAds
|
|
108
124
|
#
|
109
125
|
# @example
|
110
126
|
# ids = ['7o4em', 'oc9ce', '1c5lji']
|
111
|
-
# metric_groups = [
|
127
|
+
# metric_groups = [MetricGroup::MOBILE_CONVERSION, MetricGroup::ENGAGEMENT]
|
112
128
|
# object.create_async_job(account, ids, metric_groups)
|
113
129
|
#
|
114
130
|
# @param account [Account] The Account object instance.
|
@@ -118,16 +134,17 @@ module TwitterAds
|
|
118
134
|
# @option opts [Time] :start_time The starting time to use (default: 7 days ago).
|
119
135
|
# @option opts [Time] :end_time The end time to use (default: now).
|
120
136
|
# @option opts [Symbol] :granularity The granularity to use (default: :hour).
|
121
|
-
# @option opts [
|
122
|
-
# @option opts [
|
137
|
+
# @option opts [String] :placement The placement of entity (default: ALL_ON_TWITTER).
|
138
|
+
# @option opts [String] :segmentation_type The segmentation type to use (default: none).
|
123
139
|
#
|
124
140
|
# @return The response of creating job
|
125
141
|
#
|
126
142
|
# @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation
|
127
|
-
# @
|
143
|
+
# @since 1.0.0
|
128
144
|
|
129
145
|
def create_async_job(account, ids, metric_groups, opts = {})
|
130
146
|
# set default metric values
|
147
|
+
entity = opts.fetch(:entity, name)
|
131
148
|
end_time = opts.fetch(:end_time, (Time.now - Time.now.sec - (60 * Time.now.min)))
|
132
149
|
start_time = opts.fetch(:start_time, end_time - 604_800) # 7 days ago
|
133
150
|
granularity = opts.fetch(:granularity, :hour)
|
@@ -143,7 +160,7 @@ module TwitterAds
|
|
143
160
|
start_time: TwitterAds::Utils.to_time(start_time, granularity, start_utc_offset),
|
144
161
|
end_time: TwitterAds::Utils.to_time(end_time, granularity, end_utc_offset),
|
145
162
|
granularity: granularity.to_s.upcase,
|
146
|
-
entity: ANALYTICS_MAP[
|
163
|
+
entity: ANALYTICS_MAP[entity],
|
147
164
|
placement: placement,
|
148
165
|
country: country,
|
149
166
|
platform: platform
|
@@ -153,19 +170,18 @@ module TwitterAds
|
|
153
170
|
params['entity_ids'] = ids.join(',')
|
154
171
|
|
155
172
|
resource = self::RESOURCE_ASYNC_STATS % { account_id: account.id }
|
156
|
-
puts 'my resource is ' + resource
|
157
173
|
response = Request.new(account.client, :post, resource, params: params).perform
|
158
|
-
response.body[:data]
|
174
|
+
TwitterAds::Analytics.new(account).from_response(response.body[:data], response.headers)
|
159
175
|
end
|
160
176
|
|
161
177
|
# Check async job status.
|
162
178
|
# GET /#{TwitterAds::API_VERSION}/stats/jobs/accounts/:account_id
|
163
179
|
#
|
164
180
|
# @example
|
165
|
-
# TwitterAds::LineItem.check_async_job_status(account,
|
181
|
+
# TwitterAds::LineItem.check_async_job_status(account, job_ids: ['1357343438724431305'])
|
166
182
|
#
|
167
183
|
# @param account [Account] The Account object instance.
|
168
|
-
# @option opts [
|
184
|
+
# @option opts [Array] :job_ids A collection of job IDs to fetch.
|
169
185
|
#
|
170
186
|
# @return A cursor of job statuses
|
171
187
|
|
@@ -173,18 +189,18 @@ module TwitterAds
|
|
173
189
|
# set default values
|
174
190
|
job_ids = opts.fetch(:job_ids, nil)
|
175
191
|
params = {}
|
176
|
-
params[:job_ids] =
|
192
|
+
params[:job_ids] = job_ids.join(',') if job_ids
|
177
193
|
|
178
194
|
resource = self::RESOURCE_ASYNC_STATS % { account_id: account.id }
|
179
195
|
request = Request.new(account.client, :get, resource, params: params)
|
180
|
-
Cursor.new(
|
196
|
+
Cursor.new(TwitterAds::Analytics, request, init_with: [account])
|
181
197
|
end
|
182
198
|
|
183
199
|
# Fetch async job data for a completed job.
|
184
200
|
# Raises HTTP 404 exception, otherwise retries up to 5 times with exponential backoff.
|
185
201
|
#
|
186
202
|
# @example
|
187
|
-
# response_data = TwitterAds::LineItem.fetch_async_job_data(account,
|
203
|
+
# response_data = TwitterAds::LineItem.fetch_async_job_data(account, data_url)
|
188
204
|
#
|
189
205
|
# @param data_url [String] The URL from the successful completion of an async job.
|
190
206
|
#
|
@@ -209,21 +225,57 @@ module TwitterAds
|
|
209
225
|
end
|
210
226
|
end
|
211
227
|
|
228
|
+
# Retrieve details about which entities' analytics metrics
|
229
|
+
# have changed in a given time period.
|
230
|
+
#
|
231
|
+
# @example
|
232
|
+
# time = Time.now
|
233
|
+
# utc_offset = '+09:00'
|
234
|
+
# start_time = time - (60 * 60 * 24) # -1 day
|
235
|
+
# end_time = time
|
236
|
+
# active_entities = TwitterAds::LineItem.active_entities(
|
237
|
+
# account,
|
238
|
+
# line_item_ids: %w(exrfs),
|
239
|
+
# start_time: start_time,
|
240
|
+
# end_time: end_time,
|
241
|
+
# utc_offset: utc_offset,
|
242
|
+
# granularity: :day)
|
243
|
+
#
|
244
|
+
# @param account [Account] The Account object instance.
|
245
|
+
# @param entity [String] The entity type to retrieve data for.
|
246
|
+
# @param start_time [Time] Scopes the retrieved data to the specified start time.
|
247
|
+
# @param end_time [Time] Scopes the retrieved data to the specified end time.
|
248
|
+
# @option opts [Array] :campaign_ids A collection of IDs to be fetched.
|
249
|
+
# @option opts [Array] :funding_instrument_ids A collection of IDs to be fetched.
|
250
|
+
# @option opts [Array] :line_item_ids A collection of IDs to be fetched.
|
251
|
+
#
|
252
|
+
# @return A list of entity details.
|
253
|
+
#
|
254
|
+
# @see https://developer.twitter.com/en/docs/ads/analytics/api-reference/active-entities
|
255
|
+
|
212
256
|
def active_entities(account, start_time:, end_time:, **opts)
|
213
|
-
|
257
|
+
entity = opts.fetch(:entity, name)
|
214
258
|
granularity = opts.fetch(:granularity, nil)
|
215
259
|
start_utc_offset = opts[:start_utc_offset] || opts[:utc_offset]
|
216
260
|
end_utc_offset = opts[:end_utc_offset] || opts[:utc_offset]
|
217
261
|
|
218
|
-
if
|
219
|
-
raise "'OrganicTweet' not
|
262
|
+
if entity == 'OrganicTweet'
|
263
|
+
raise "'OrganicTweet' is not supported with 'active_entities'"
|
220
264
|
end
|
221
265
|
|
222
266
|
params = {
|
223
|
-
entity: ANALYTICS_MAP[
|
267
|
+
entity: ANALYTICS_MAP[entity],
|
224
268
|
start_time: TwitterAds::Utils.to_time(start_time, granularity, start_utc_offset),
|
225
269
|
end_time: TwitterAds::Utils.to_time(end_time, granularity, end_utc_offset)
|
226
|
-
}
|
270
|
+
}
|
271
|
+
|
272
|
+
opts.each { |k, v|
|
273
|
+
params[k] = if v.instance_of?(Array)
|
274
|
+
v.join(',')
|
275
|
+
else
|
276
|
+
v
|
277
|
+
end
|
278
|
+
}
|
227
279
|
|
228
280
|
resource = self::RESOURCE_ACTIVE_ENTITIES % { account_id: account.id }
|
229
281
|
response = Request.new(account.client, :get, resource, params: params).perform
|