twitter-ads 0.3.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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