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.
- data/lib/rb_facebook.rb +335 -0
- metadata +87 -0
data/lib/rb_facebook.rb
ADDED
@@ -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
|
+
|