twitter-ads 0.3.4 → 1.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/twitter-ads.rb +18 -0
  5. data/lib/twitter-ads/account.rb +20 -5
  6. data/lib/twitter-ads/audiences/tailored_audience.rb +4 -4
  7. data/lib/twitter-ads/campaign/app_list.rb +2 -2
  8. data/lib/twitter-ads/campaign/campaign.rb +4 -3
  9. data/lib/twitter-ads/campaign/funding_instrument.rb +2 -2
  10. data/lib/twitter-ads/campaign/line_item.rb +12 -14
  11. data/lib/twitter-ads/campaign/promotable_user.rb +2 -2
  12. data/lib/twitter-ads/campaign/targeting_criteria.rb +2 -2
  13. data/lib/twitter-ads/campaign/tweet.rb +5 -4
  14. data/lib/twitter-ads/creative/account_media.rb +36 -0
  15. data/lib/twitter-ads/creative/app_download_card.rb +2 -2
  16. data/lib/twitter-ads/creative/image_app_download_card.rb +2 -2
  17. data/lib/twitter-ads/creative/image_conversation_card.rb +2 -2
  18. data/lib/twitter-ads/creative/lead_gen_card.rb +2 -2
  19. data/lib/twitter-ads/creative/media_creative.rb +37 -0
  20. data/lib/twitter-ads/creative/promoted_account.rb +4 -3
  21. data/lib/twitter-ads/creative/promoted_tweet.rb +4 -3
  22. data/lib/twitter-ads/creative/video.rb +2 -2
  23. data/lib/twitter-ads/creative/video_app_download_card.rb +2 -2
  24. data/lib/twitter-ads/creative/video_conversation_card.rb +2 -2
  25. data/lib/twitter-ads/creative/website_card.rb +2 -2
  26. data/lib/twitter-ads/enum.rb +49 -2
  27. data/lib/twitter-ads/http/request.rb +2 -2
  28. data/lib/twitter-ads/resources/analytics.rb +129 -24
  29. data/lib/twitter-ads/resources/dsl.rb +1 -1
  30. data/lib/twitter-ads/targeting/reach_estimate.rb +17 -10
  31. data/lib/twitter-ads/targeting_criteria/app_store_category.rb +22 -0
  32. data/lib/twitter-ads/targeting_criteria/behavior.rb +26 -0
  33. data/lib/twitter-ads/targeting_criteria/behavior_taxonomy.rb +23 -0
  34. data/lib/twitter-ads/targeting_criteria/device.rb +24 -0
  35. data/lib/twitter-ads/targeting_criteria/event.rb +32 -0
  36. data/lib/twitter-ads/targeting_criteria/interest.rb +22 -0
  37. data/lib/twitter-ads/targeting_criteria/language.rb +21 -0
  38. data/lib/twitter-ads/targeting_criteria/location.rb +22 -0
  39. data/lib/twitter-ads/targeting_criteria/network_operator.rb +22 -0
  40. data/lib/twitter-ads/targeting_criteria/platform.rb +22 -0
  41. data/lib/twitter-ads/targeting_criteria/platform_version.rb +23 -0
  42. data/lib/twitter-ads/targeting_criteria/tv_channel.rb +20 -0
  43. data/lib/twitter-ads/targeting_criteria/tv_genre.rb +20 -0
  44. data/lib/twitter-ads/targeting_criteria/tv_market.rb +22 -0
  45. data/lib/twitter-ads/targeting_criteria/tv_show.rb +22 -0
  46. data/lib/twitter-ads/version.rb +1 -1
  47. data/spec/spec_helper.rb +1 -1
  48. data/spec/twitter-ads/campaign/app_list_spec.rb +1 -1
  49. data/spec/twitter-ads/campaign/line_item_spec.rb +0 -22
  50. data/spec/twitter-ads/campaign/reach_estimate_spec.rb +16 -11
  51. data/spec/twitter-ads/campaign/tweet_spec.rb +2 -2
  52. data/spec/twitter-ads/client_spec.rb +1 -1
  53. data/spec/twitter-ads/creative/account_media_spec.rb +32 -0
  54. data/spec/twitter-ads/creative/media_creative_spec.rb +32 -0
  55. metadata +44 -22
  56. metadata.gz.sig +0 -0
@@ -23,7 +23,7 @@ module TwitterAds
23
23
  # Creates a new Request object instance.
24
24
  #
25
25
  # @example
26
- # request = Request.new(client, :get, '/0/accounts')
26
+ # request = Request.new(client, :get, '/1/accounts')
27
27
  #
28
28
  # @param client [Client] The Client object instance.
29
29
  # @param method [Symbol] The HTTP method to be used.
@@ -47,7 +47,7 @@ module TwitterAds
47
47
  # Executes the current Request object.
48
48
  #
49
49
  # @example
50
- # request = Request.new(client, :get, '/0/accounts')
50
+ # request = Request.new(client, :get, '/1/accounts')
51
51
  # request.perform
52
52
  #
53
53
  # @since 0.1.0
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
  # Copyright (C) 2015 Twitter, Inc.
3
3
 
4
+ require 'zlib'
5
+ require 'open-uri'
6
+
4
7
  module TwitterAds
5
8
  module Analytics
6
9
 
7
- CLASS_ID_MAP = {
8
- 'TwitterAds::LineItem' => :line_item_ids,
9
- 'TwitterAds::OrganicTweet' => :tweet_ids,
10
- 'TwitterAds::Tweet' => :tweet_ids,
11
- 'TwitterAds::Creative::PromotedTweet' => :promoted_tweet_ids
12
- }.freeze # @api private
10
+ ANALYTICS_MAP = {
11
+ 'TwitterAds::LineItem' => 'LINE_ITEM'.freeze,
12
+ 'TwitterAds::OrganicTweet' => 'ORGANIC_TWEET'.freeze,
13
+ 'TwitterAds::Creative::PromotedTweet' => 'PROMOTED_TWEET'.freeze
14
+ }.freeze
13
15
 
14
16
  def self.included(klass)
15
17
  klass.send :include, InstanceMethods
@@ -21,22 +23,21 @@ module TwitterAds
21
23
  # Pulls a list of metrics for the current object instance.
22
24
  #
23
25
  # @example
24
- # metrics = [:promoted_tweet_timeline_clicks, :promoted_tweet_search_clicks]
26
+ # metric_groups = [:promoted_tweet_timeline_clicks, :promoted_tweet_search_clicks]
25
27
  # object.stats(metrics)
26
28
  #
27
- # @param metrics [Array] A collection of valid metrics to be fetched.
29
+ # @param metric_groups [Array] A collection of metric groups to fetch.
28
30
  # @param opts [Hash] An optional Hash of extended options.
29
31
  # @option opts [Time] :start_time The starting time to use (default: 7 days ago).
30
32
  # @option opts [Time] :end_time The end time to use (default: now).
31
33
  # @option opts [Symbol] :granularity The granularity to use (default: :hour).
32
- # @option opts [Symbol] :segmentation_type The segmentation type to use (default: none).
33
34
  #
34
35
  # @return [Array] The collection of stats requested.
35
36
  #
36
37
  # @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation
37
- # @since 0.1.0
38
- def stats(metrics, opts = {})
39
- self.class.stats(account, [id], metrics, opts)
38
+ # @since 1.0.0
39
+ def stats(metric_groups, opts = {})
40
+ self.class.stats(account, [id], metric_groups, opts)
40
41
  end
41
42
 
42
43
  end
@@ -47,43 +48,147 @@ module TwitterAds
47
48
  #
48
49
  # @example
49
50
  # ids = ['7o4em', 'oc9ce', '1c5lji']
50
- # metrics = [:promoted_tweet_timeline_clicks, :promoted_tweet_search_clicks]
51
- # object.stats(account, ids, metrics)
51
+ # metric_groups = [MetricGroups.MOBILE_CONVERSION, MetricGroups.ENGAGEMENT]
52
+ # object.stats(account, ids, metric_groups)
52
53
  #
53
54
  # @param account [Account] The Account object instance.
54
55
  # @param ids [Array] A collection of object IDs being targeted.
55
- # @param metrics [Array] A collection of valid metrics to be fetched.
56
+ # @param metric_groups [Array] A collection of metric_groups to be fetched.
56
57
  # @param opts [Hash] An optional Hash of extended options.
57
58
  # @option opts [Time] :start_time The starting time to use (default: 7 days ago).
58
59
  # @option opts [Time] :end_time The end time to use (default: now).
59
60
  # @option opts [Symbol] :granularity The granularity to use (default: :hour).
60
- # @option opts [Symbol] :segmentation_type The segmentation type to use (default: none).
61
+ # @option opts [Symbol] :placement The placement of entity (default: ALL_ON_TWITTER).
61
62
  #
62
63
  # @return [Array] The collection of stats requested.
63
64
  #
64
65
  # @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation
65
- # @since 0.1.0
66
- def stats(account, ids, metrics, opts = {})
66
+ # @since 1.0.0
67
+
68
+ def stats(account, ids, metric_groups, opts = {})
67
69
  # set default metric values
68
- end_time = opts.fetch(:end_time, Time.now)
70
+ end_time = opts.fetch(:end_time, (Time.now - Time.now.sec - (60 * Time.now.min)))
69
71
  start_time = opts.fetch(:start_time, end_time - 604_800) # 7 days ago
70
72
  granularity = opts.fetch(:granularity, :hour)
71
- segmentation_type = opts.fetch(:segmentation_type, nil)
73
+ placement = opts.fetch(:placement, Placement::ALL_ON_TWITTER)
72
74
 
73
75
  params = {
74
- metrics: metrics.join(','),
76
+ metric_groups: metric_groups.join(','),
75
77
  start_time: TwitterAds::Utils.to_time(start_time, granularity),
76
78
  end_time: TwitterAds::Utils.to_time(end_time, granularity),
77
- granularity: granularity.to_s.upcase
79
+ granularity: granularity.to_s.upcase,
80
+ entity: ANALYTICS_MAP[name],
81
+ placement: placement
78
82
  }
79
- params[:segmentation_type] = segmentation_type.to_s.upcase if segmentation_type
80
- params[TwitterAds::Analytics::CLASS_ID_MAP[name]] = ids.join(',')
83
+
84
+ params['entity_ids'] = ids.join(',')
81
85
 
82
86
  resource = self::RESOURCE_STATS % { account_id: account.id }
83
87
  response = Request.new(account.client, :get, resource, params: params).perform
84
88
  response.body[:data]
85
89
  end
86
90
 
91
+ # Create an asynchronous analytics job for a given ads account.
92
+ # A job_id is returned, which you can use to poll the
93
+ # GET /1/stats/jobs/accounts/:account_id endpoint, checking until the job is successful.
94
+ #
95
+ # @example
96
+ # ids = ['7o4em', 'oc9ce', '1c5lji']
97
+ # metric_groups = [MetricGroups.MOBILE_CONVERSION, MetricGroups.ENGAGEMENT]
98
+ # object.create_async_job(account, ids, metric_groups)
99
+ #
100
+ # @param account [Account] The Account object instance.
101
+ # @param ids [Array] A collection of object IDs being targeted.
102
+ # @param metric_groups [Array] A collection of metric_groups to be fetched.
103
+ # @param opts [Hash] An optional Hash of extended options.
104
+ # @option opts [Time] :start_time The starting time to use (default: 7 days ago).
105
+ # @option opts [Time] :end_time The end time to use (default: now).
106
+ # @option opts [Symbol] :granularity The granularity to use (default: :hour).
107
+ # @option opts [Symbol] :placement The placement of entity (default: ALL_ON_TWITTER).
108
+ # @option opts [Symbol] :segmentation_type The segmentation type to use (default: none).
109
+ #
110
+ # @return The response of creating job
111
+ #
112
+ # @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation
113
+ # @sync 1.0.0
114
+
115
+ def create_async_job(account, ids, metric_groups, opts = {})
116
+ # set default metric values
117
+ end_time = opts.fetch(:end_time, (Time.now - Time.now.sec - (60 * Time.now.min)))
118
+ start_time = opts.fetch(:start_time, end_time - 604_800) # 7 days ago
119
+ granularity = opts.fetch(:granularity, :hour)
120
+ placement = opts.fetch(:placement, Placement::ALL_ON_TWITTER)
121
+ segmentation_type = opts.fetch(:segmentation_type, nil)
122
+
123
+ params = {
124
+ metric_groups: metric_groups.join(','),
125
+ start_time: TwitterAds::Utils.to_time(start_time, granularity),
126
+ end_time: TwitterAds::Utils.to_time(end_time, granularity),
127
+ granularity: granularity.to_s.upcase,
128
+ entity: ANALYTICS_MAP[name],
129
+ placement: placement
130
+ }
131
+
132
+ params[:segmentation_type] = segmentation_type.to_s.upcase if segmentation_type
133
+ params['entity_ids'] = ids.join(',')
134
+
135
+ resource = self::RESOURCE_ASYNC_STATS % { account_id: account.id }
136
+ puts 'my resource is ' + resource
137
+ response = Request.new(account.client, :post, resource, params: params).perform
138
+ response.body[:data]
139
+ end
140
+
141
+ # Check async job status.
142
+ # GET /1/stats/jobs/accounts/:account_id
143
+ #
144
+ # @example
145
+ # TwitterAds::LineItem.check_async_job_status(account, job_id: '1357343438724431305')
146
+ #
147
+ # @param account [Account] The Account object instance.
148
+ # @option opts [String] :job_id The starting time to use (default: 7 days ago).
149
+ #
150
+ # @return A cursor of job statuses
151
+
152
+ def check_async_job_status(account, opts = {})
153
+ # set default values
154
+ job_id = opts.fetch(:job_id, nil)
155
+ params = {}
156
+ params[:job_id] = job_id if job_id
157
+
158
+ resource = self::RESOURCE_ASYNC_STATS % { account_id: account.id }
159
+ request = Request.new(account.client, :get, resource, params: params)
160
+ Cursor.new(nil, request, init_with: [account])
161
+ end
162
+
163
+ # Fetch async job data for a completed job.
164
+ # Raises HTTP 404 exception, otherwise retries up to 5 times with exponential backoff.
165
+ #
166
+ # @example
167
+ # response_data = TwitterAds::LineItem.fetch_async_job_data(account, file_url)
168
+ #
169
+ # @param data_url [String] The URL from the successful completion of an async job.
170
+ #
171
+ # @return A cursor of job statuses
172
+
173
+ def fetch_async_job_data(data_url)
174
+ tries = 0
175
+ begin
176
+ tries += 1
177
+ raw_file = open(data_url)
178
+ unzipped_file = Zlib::GzipReader.new(raw_file)
179
+ response_data = unzipped_file.read
180
+ response = JSON.parse(response_data)
181
+ response['data']
182
+ rescue OpenURI::HTTPError => e
183
+ unless e.io.status[0] == '404'
184
+ if tries < 5
185
+ sleep(2**tries)
186
+ retry
187
+ end
188
+ end
189
+ end
190
+ end
191
+
87
192
  end
88
193
 
89
194
  end
@@ -56,7 +56,7 @@ module TwitterAds
56
56
  elsif type == :bool
57
57
  params[name] = TwitterAds::Utils.to_bool(value)
58
58
  elsif value.is_a?(Array)
59
- next if value.size < 1
59
+ next if value.empty?
60
60
  params[name] = value.join(',')
61
61
  else
62
62
  params[name] = value
@@ -13,20 +13,20 @@ module TwitterAds
13
13
  # account,
14
14
  # 'PROMOTED_TWEETS',
15
15
  # 'WEBSITE_CLICKS',
16
- # 2153688540,
16
+ # 5500000,
17
+ # 30000000,
17
18
  # similar_to_followers_of_user: 2153688540,
18
19
  # gender: 2
19
20
  # )
20
21
  #
21
22
  # @param client [Client] The Client object instance.
22
- # @param account [Account] The Ads Account instance for this request.
23
23
  # @param product_type [String] The product type being targeted.
24
24
  # @param objective [String] The objective being targeted.
25
- # @param user_id [Long] The ID of the user whose content will be promoted.
25
+ # @param campaign_daily_budget_amount_local_micro [Long] Daily budget in micros.
26
26
  # @param opts [Hash] A Hash of extended options.
27
27
  #
28
- # @option opts [String] :bid_type The bidding mechanism.
29
28
  # @option opts [Long] :bid_amount_local_micro Bid amount in local currency micros.
29
+ # @option opts [String] :bid_type The bidding mechanism.
30
30
  # @option opts [String] :currency ISO-4217 Currency code for bid amount.
31
31
  # @option opts [String] :followers_of_users Comma-separated user IDs.
32
32
  # @option opts [String] :similar_to_followers_of_users Comma-separated user IDs.
@@ -44,14 +44,20 @@ module TwitterAds
44
44
  # @option opts [String] :campaign engagement Campaign ID for Tweet Engager Retargeting.
45
45
  # @option opts [String] :user_engagement Promoted User ID for Tweet Engager Retargeting.
46
46
  # @option opts [String] :engagement_type engagement type for Tweet Engager Retargeting.
47
+ # @option opts [String] :network_operators Network operators to target
48
+ # @option opts [String] :app_store_categories App store categories to target.
49
+ # @option opts [String] :app_store_categories_expanded App store categories with lookalikes.
47
50
  #
48
51
  # @return [Hash] A hash containing count and infinite_bid_count.
49
52
  #
50
- # @since 0.2.0
51
- # @see https://dev.twitter.com/ads/reference/get/accounts/%3Aaccount_id/reach_estimate
52
- def fetch(account, product_type, objective, user_id, opts = {})
53
- resource = "/0/accounts/#{account.id}/reach_estimate"
54
- params = { product_type: product_type, objective: objective, user_id: user_id }.merge!(opts)
53
+ # @since 1.0.0
54
+ # @see https://dev.twitter.com/ads/reference/1/get/accounts/%3Aaccount_id/reach_estimate
55
+ def fetch(account, product_type, objective, campaign_daily_budget,
56
+ opts = {})
57
+ resource = "/1/accounts/#{account.id}/reach_estimate"
58
+ params = { product_type: product_type, objective: objective,
59
+ campaign_daily_budget_amount_local_micro: campaign_daily_budget
60
+ }.merge!(opts)
55
61
 
56
62
  # The response value count is "bid sensitive", we default to bid_type=AUTO here to preserve
57
63
  # expected behavior despite an API change that occurred in December 2015.
@@ -59,7 +65,8 @@ module TwitterAds
59
65
  params = { bid_type: 'AUTO' }.merge!(params)
60
66
  end
61
67
 
62
- response = TwitterAds::Request.new(account.client, :get, resource, params: params).perform
68
+ response = TwitterAds::Request.new(account.client, :get,
69
+ resource, params: params).perform
63
70
  response.body[:data]
64
71
  end
65
72
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class AppStoreCategory
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ property :name, read_only: true
11
+ property :os_type, read_only: true
12
+ property :targeting_type, read_only: true
13
+ property :targeting_value, read_only: true
14
+
15
+ RESOURCE_COLLECTION = '/1/targeting_criteria/app_store_categories'.freeze # @api private
16
+
17
+ def initialize(account)
18
+ @account = account
19
+ self
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class Behavior
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ property :id, read_only: true
11
+ property :name, read_only: true
12
+ property :targeting_type, read_only: true
13
+ property :targeting_value, read_only: true
14
+ property :audience_code, read_only: true
15
+ property :country_code, read_only: true
16
+ property :partner_source, read_only: true
17
+ property :targetable_type, read_only: true
18
+
19
+ RESOURCE_COLLECTION = '/1/targeting_criteria/behaviors'.freeze # @api private
20
+
21
+ def initialize(account)
22
+ @account = account
23
+ self
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class BehaviorTaxonomy
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ property :id, read_only: true
11
+ property :name, read_only: true
12
+ property :parent_id, read_only: true
13
+ property :created_at, read_only: true
14
+ property :updated_at, read_only: true
15
+
16
+ RESOURCE_COLLECTION = '/1/targeting_criteria/behavior_taxonomies'.freeze # @api private
17
+
18
+ def initialize(account)
19
+ @account = account
20
+ self
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class Device
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ property :id, read_only: true
11
+ property :name, read_only: true
12
+ property :targeting_type, read_only: true
13
+ property :targeting_value, read_only: true
14
+ property :platform, read_only: true
15
+ property :manufacturer, read_only: true
16
+
17
+ RESOURCE_COLLECTION = '/1/targeting_criteria/devices'.freeze # @api private
18
+
19
+ def initialize(account)
20
+ @account = account
21
+ self
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class Event
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ property :id, read_only: true
11
+ property :name, read_only: true
12
+ property :reach, read_only: true
13
+ property :start_time, read_only: true
14
+ property :end_time, read_only: true
15
+ property :top_users, read_only: true
16
+ property :top_tweets, read_only: true
17
+ property :top_hashtags, read_only: true
18
+ property :country_code, read_only: true
19
+ property :is_global, read_only: true
20
+ property :category, read_only: true
21
+ property :gender_breakdown_percentage, read_only: true
22
+ property :device_breakdown_percentage, read_only: true
23
+ property :country_breakdown_percentage, read_only: true
24
+
25
+ RESOURCE_COLLECTION = '/1/targeting_criteria/events'.freeze # @api private
26
+
27
+ def initialize(account)
28
+ @account = account
29
+ self
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class Interest
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ property :name, read_only: true
11
+ property :targeting_type, read_only: true
12
+ property :targeting_value, read_only: true
13
+ property :localized_name, read_only: true
14
+
15
+ RESOURCE_COLLECTION = '/1/targeting_criteria/interests'.freeze # @api private
16
+
17
+ def initialize(account)
18
+ @account = account
19
+ self
20
+ end
21
+ end
22
+ end