rb_facebook 0.0.4

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 (2) hide show
  1. data/lib/rb_facebook.rb +335 -0
  2. metadata +87 -0
@@ -0,0 +1,335 @@
1
+ module RbFacebook
2
+
3
+ VERSION = '0.0.4'
4
+ class Api
5
+
6
+ attr_accessor :access_token, :app_id, :app_secret
7
+
8
+ STATS_METRICS = ["impressions", "clicks", "spent", "social_impressions", "social_clicks", "social_spent", "actions", "unique_impressions", "social_unique_impressions", "unique_clicks", "social_unique_clicks", "connections"]
9
+
10
+ def logged_in_user
11
+ graph_api_parse('me')
12
+ end
13
+
14
+ def ads_accounts
15
+ rest_api_parse('ads.getAccounts')
16
+ end
17
+
18
+ def user_accounts
19
+ graph_api_parse('me/accounts')['data']
20
+ end
21
+
22
+ def ads_campaigns(args = {})
23
+ account_id = args[:account_id]
24
+ params = {'account_id' => account_id}
25
+ params[:campaign_ids] = "[#{args[:campaign_ids].join(',')}]" if args[:campaign_ids]
26
+ rest_api_parse('ads.getCampaigns', :params => params, :log_call_url => true)
27
+ end
28
+
29
+ def insight_metrics(args = {})
30
+
31
+ metrics = args[:metrics]
32
+ object_id = args[:object_id]
33
+ start_date = args[:start_date]
34
+ end_date = args[:end_date]
35
+
36
+ data_calls = []
37
+ metrics_data = {}
38
+
39
+ metrics.each do |metric|
40
+ metrics_data[metric] = {}
41
+ current_date = start_date + 1.day
42
+ period = 1.day.seconds
43
+ while current_date <= end_date
44
+ data_calls << {
45
+ :fql_query =>
46
+ "SELECT object_id, metric, end_time, period, value FROM insights
47
+ WHERE object_id = #{object_id}
48
+ AND metric = '#{metric}'
49
+ AND end_time = end_time_date('#{current_date.strftime("%Y-%m-%d")}')
50
+ AND period = #{period}",
51
+ :metric => metric,
52
+ :date => current_date - 1.day
53
+ }
54
+ current_date += 1.day
55
+ end
56
+ end
57
+
58
+ data_calls.each_slice(50) do |call_slice|
59
+ call_slice.map do |call|
60
+ Thread.new do
61
+ #Rails.logger.warn(call)
62
+ result = rest_api_parse('fql.query', :params => {:query => call[:fql_query]})
63
+ #Rails.logger.warn(result)
64
+ Thread.exclusive do
65
+ begin
66
+ #Rails.logger.warn "RESULT: #{result.inspect}"
67
+ metrics_data[call[:metric]][call[:date]] = (result.first || {})['value'] || 0
68
+ rescue
69
+ Rails.logger.warn "ERROR THROWN: #{result.inspect}"
70
+ raise
71
+ end
72
+ end
73
+ end
74
+ end.each(&:join)
75
+ end
76
+
77
+ metrics_data
78
+ end
79
+
80
+ def ads_campaign_stats(args = {})
81
+ account_id = args[:account_id]
82
+ campaign_id = args[:campaign_id]
83
+ include_deleted = args[:include_deleted]
84
+ start_date = args[:start_date]
85
+ end_date = args[:end_date]
86
+ period = args[:interval]
87
+
88
+ data_calls = []
89
+ metrics_data = {}
90
+
91
+ current_date = start_date
92
+
93
+ while current_date <= end_date
94
+ metrics_data[current_date] = {}
95
+ data_calls << {
96
+ :start_date => current_date.strftime('%s'),
97
+ :end_date => (current_date + period).strftime('%s'),
98
+ :date => current_date
99
+ }
100
+ current_date += period
101
+ end
102
+
103
+ data_calls.each_slice(50) do |call_slice|
104
+ call_slice.map do |call|
105
+ Thread.new do
106
+ date_param = {"time_range" => {"time_start" => call[:start_date], "time_stop" => call[:end_date]}}.to_json
107
+ result = rest_api_parse('ads.getCampaignStats', :params => {
108
+ :account_id => account_id,
109
+ :campaign_ids => "[#{campaign_id}]",
110
+ :time_ranges => date_param,
111
+ :include_deleted => include_deleted
112
+ })
113
+ Thread.exclusive do
114
+ #Rails.logger.warn(result.inspect)
115
+ result.first['stats'].each do |id, stats|
116
+ stats.each do |metric, value|
117
+ next if metric == 'id'
118
+ metrics_data[call[:date]][metric] = metrics_data[call[:date]][metric].to_f + value.to_f
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end.each(&:join)
124
+ end
125
+
126
+ metrics_data
127
+ end
128
+
129
+ def ad_groups(args = {})
130
+ params = {:account_id => args[:account_id], :include_deleted => args[:include_deleted] || false}
131
+ params[:campaign_ids] = args[:campaign_ids] if args[:campaign_ids]
132
+ params[:campaign_ids] = "[#{args[:campaign_id]}]" if args[:campaign_id]
133
+ rest_api_parse('ads.getAdGroups', :params => params, :log_call_url => true)
134
+ end
135
+
136
+ def initialize
137
+ load_tokens
138
+ end
139
+
140
+ def load_tokens
141
+ tokens = YAML.load(File.open(File.join(Rails.root, 'config', 'facebook.yml')))
142
+ self.app_secret = tokens[:APP_SECRET]
143
+ self.app_id = tokens[:APP_ID]
144
+ end
145
+
146
+ def login_url(current_url, permissions=[:read_insights,:ads_management,:offline_access])
147
+ "https://www.facebook.com/dialog/oauth?client_id=#{self.app_id}&scope=#{permissions.join(',')}&redirect_uri=#{URI.escape(current_url, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}"
148
+ end
149
+
150
+ def logout_url(current_url)
151
+ "https://www.facebook.com/logout.php?next=#{URI.escape(current_url, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&access_token=#{URI.escape(self.access_token, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}"
152
+ end
153
+
154
+ def authenticate(args)
155
+ load_tokens
156
+ response = graph_api_fetch(
157
+ "oauth/access_token",
158
+ :params => {
159
+ :client_id => app_id,
160
+ :client_secret => app_secret,
161
+ :code => args[:code],
162
+ :redirect_uri => args[:url]
163
+ },
164
+ :no_access_token => true
165
+ )
166
+
167
+ self.access_token = CGI.parse(response.body)['access_token'].first
168
+ end
169
+
170
+ def logged_in?
171
+ check_auth
172
+ !self.access_token.nil?
173
+ end
174
+
175
+ def check_auth
176
+ if self.access_token
177
+ response = graph_api_fetch('me')
178
+ if response.code != '200'
179
+ self.access_token = nil
180
+ end
181
+ end
182
+ end
183
+
184
+ def graph_api_parse(method_name, args = {})
185
+ ActiveSupport::JSON.decode(graph_api_fetch(method_name, args).body)
186
+ end
187
+
188
+ def graph_api_fetch(method_name, args = {})
189
+ api_params = args[:params] || {}
190
+ use_access_token = !args[:no_access_token]
191
+ log_call_url = args[:log_call_url]
192
+
193
+ api_fetch('graph.facebook.com', method_name, api_params, use_access_token, log_call_url)
194
+ end
195
+
196
+ def rest_api_parse(method_name, args = {})
197
+ results = []
198
+ result = [:dummy]
199
+ previous_result = []
200
+
201
+ page = 0
202
+ per_page = 10
203
+
204
+ until result.empty? or result.is_a?(Hash) or result == previous_result
205
+ if method_name =~ /^ads./
206
+ args[:params] = {} unless args[:params]
207
+ args[:params][:limit] = per_page
208
+ args[:params][:offset] = per_page * page
209
+ #args[:params][:include_count] = 1
210
+ end
211
+
212
+ previous_result = result
213
+ result = ActiveSupport::JSON.decode(rest_api_fetch(method_name, args).body)
214
+
215
+ page += 1
216
+ results << result unless result == previous_result
217
+
218
+ result = [] unless method_name =~ /^ads\./
219
+ end
220
+ results.flatten
221
+ end
222
+
223
+ def rest_api_fetch(method_name, args = {})
224
+ api_params = args[:params] || {}
225
+ use_access_token = !args[:no_access_token]
226
+ log_call_url = args[:log_call_url]
227
+ multipart_post = args[:mp_post]
228
+
229
+ api_params["format"] = 'json-strings'
230
+
231
+ api_fetch('api.facebook.com/method', method_name, api_params, use_access_token, log_call_url, :mp_post => multipart_post)
232
+
233
+ end
234
+
235
+ def api_fetch(api_base_url, method_name, api_params, use_access_token, log_call_url, args = {})
236
+
237
+ api_params["access_token"] = self.access_token if use_access_token
238
+
239
+ params_fragment = api_params.map{|k,v|"#{k}=#{URI.escape("#{v}", Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}"}.join('&')
240
+
241
+ call_url = ['https:/', api_base_url, method_name].join('/')
242
+ call_url << "?#{params_fragment}" unless params_fragment.blank? or args[:mp_post]
243
+
244
+ Rails.logger.warn("About to fetch: #{call_url}") if log_call_url
245
+ Rails.logger.warn("Parameters: #{api_params.inspect}") if log_call_url
246
+
247
+ uri = URI.parse(call_url)
248
+ http = Net::HTTP.new(uri.host, uri.port)
249
+ http.use_ssl = true
250
+ if args[:mp_post]
251
+ mp_post = args[:mp_post]
252
+ api_params[mp_post[:name]] = UploadIO.new(mp_post[:file], mp_post[:mime], mp_post[:name])
253
+ post = Net::HTTP::Post::Multipart.new(
254
+ uri.request_uri,
255
+ api_params
256
+ )
257
+ response = http.request(post)
258
+ else
259
+ response = http.request(Net::HTTP::Get.new(uri.request_uri))
260
+ end
261
+
262
+ Rails.logger.warn("And returned #{response.inspect}: #{response.body}") if log_call_url
263
+
264
+ response
265
+ end
266
+
267
+ def self.format_targeting(targeting)
268
+ Rails.logger.warn '-0-0-0-0-0-0-0-0-0-0-'
269
+ Rails.logger.warn 'ABOUT TO FORMAT TARGETING'
270
+ Rails.logger.warn targeting.inspect
271
+ Rails.logger.warn '-0-0-0-0-0-0-0-0-0-0-'
272
+
273
+ begin
274
+
275
+ formatted_targeting = []
276
+ targeting = targeting.dup
277
+ Rails.logger.warn(targeting.inspect)
278
+ adgroup_id = targeting.delete('adgroup_id')
279
+
280
+ genders = targeting.delete('genders')
281
+ if genders.nil? or genders.length == 2
282
+ gender = 'People'
283
+ elsif genders == ['1']
284
+ gender = 'Men'
285
+ else
286
+ gender = 'Women'
287
+ end
288
+
289
+ #education_statuses =
290
+
291
+ age_min = targeting.delete('age_min')
292
+ age_max = targeting.delete('age_max')
293
+ if age_min and age_max
294
+ formatted_targeting << "#{gender} between #{age_min} and #{age_max} years of age"
295
+ elsif age_min
296
+ formatted_targeting << "#{gender} #{age_min} years of age and older"
297
+ elsif age_max
298
+ formatted_targeting << "#{gender} #{age_max} years of age and younger"
299
+ elsif gender != 'People'
300
+ formatted_targeting << "#{gender} only"
301
+ end
302
+
303
+ relationship_statuses = targeting.delete('relationship_statuses')
304
+ if relationship_statuses and !relationship_statuses.include?('0') #0 indicates ALL
305
+ ref = {'1' => 'single', '2' => 'in a relationship', '3' => 'married', '4' => 'engaged'}
306
+ formatted_targeting << "Are #{relationship_statuses.map{|s|ref[s]}.join(' or ')}"
307
+ end
308
+
309
+ education_statuses = targeting.delete('education_statuses')
310
+ if education_statuses and !education_statuses.include?('0') #0 indicates ALL
311
+ ref = {'1' => 'in high school', '2' => 'in college', '3' => 'college graduates'}
312
+ formatted_targeting << "Are #{education_statuses.map{|s|ref[s]}.join(' or ')}"
313
+ end
314
+
315
+ #puts "remaining keys: #{targeting.keys.inspect}"
316
+ targeting.each do |target_name, target_data|
317
+ target_string = "#{target_name}: "
318
+ target_data = target_data.map{|t|t['name']} if target_data.first.is_a?(Hash)
319
+ target_string << [target_data].flatten.join(', ')
320
+ formatted_targeting << target_string
321
+ end
322
+
323
+ return formatted_targeting
324
+ rescue => ex
325
+
326
+ Rails.logger.warn "ERROR FORMATTING TARGETING"
327
+ Rails.logger.warn ex.backtrace
328
+ return ["There was an error formatting the following raw targeting", targeting.inspect]
329
+ end
330
+
331
+ end
332
+
333
+ end
334
+
335
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb_facebook
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Joshua Levinson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-28 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: |
38
+ Provides a simple way to access all methods of the facebook REST and Graph APIs.
39
+
40
+ (c)2011 Raybeam, Inc. (www.raybeam.com)
41
+
42
+ email:
43
+ - pianojosh@gmail.com
44
+ executables: []
45
+
46
+ extensions: []
47
+
48
+ extra_rdoc_files: []
49
+
50
+ files:
51
+ - lib/rb_facebook.rb
52
+ has_rdoc: true
53
+ homepage: http://www.raybeam.com
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project: rb_facebook
82
+ rubygems_version: 1.6.2
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Lightweight wrapper around the facebook REST and Graph API
86
+ test_files: []
87
+