Skip to content

Commit 0db4314

Browse files
authored
Merge pull request #164 from coderdojo-japan/support-facebook-api
Support Facebook API in Statistics::Aggregation
2 parents d3a2622 + 829a0e6 commit 0db4314

13 files changed

+202
-57
lines changed

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ gem 'faraday'
3131
# https://github.com/bundler/bundler/issues/5332
3232
gem 'faraday_middleware', '0.10'
3333

34+
gem 'koala'
35+
3436
group :development do
3537
gem 'web-console'
3638
gem 'spring'

Gemfile.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ GEM
106106
jquery-ui-rails (6.0.1)
107107
railties (>= 3.2.16)
108108
json (2.1.0)
109+
koala (3.0.0)
110+
addressable
111+
faraday
112+
json (>= 1.8)
109113
kramdown (1.15.0)
110114
launchy (2.4.3)
111115
addressable (~> 2.3)
@@ -305,6 +309,7 @@ DEPENDENCIES
305309
font-awesome-rails
306310
jbuilder
307311
jquery-rails
312+
koala
308313
kramdown
309314
listen
310315
minitest-retry

app/models/dojo_event_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
class DojoEventService < ApplicationRecord
22
belongs_to :dojo
3-
enum name: %i( connpass doorkeeper )
3+
enum name: %i( connpass doorkeeper facebook )
44
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class IntegerToStringOnGroupIdInDojoEventServices < ActiveRecord::Migration[5.0]
2+
def up
3+
change_column :dojo_event_services, :group_id, :string, null: false
4+
end
5+
6+
def down
7+
change_column :dojo_event_services, :group_id, :integer, null: false
8+
end
9+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class IntegerToStringOnServiceGroupIdAndEventIdInEventHistories < ActiveRecord::Migration[5.0]
2+
def up
3+
change_column :event_histories, :service_group_id, :string, null: false
4+
change_column :event_histories, :event_id, :string, null: false
5+
end
6+
7+
def down
8+
change_column :event_histories, :service_group_id, :integer, null: false
9+
change_column :event_histories, :event_id, :integer, null: false
10+
end
11+
end

db/schema.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
t.integer "dojo_id", null: false
1717
t.integer "name", null: false
1818
t.string "url"
19-
t.integer "group_id", null: false
19+
t.string "group_id", null: false
2020
t.datetime "created_at", null: false
2121
t.datetime "updated_at", null: false
2222
t.index ["dojo_id"], name: "index_dojo_event_services_on_dojo_id"
@@ -39,8 +39,8 @@
3939
t.integer "dojo_id", null: false
4040
t.string "dojo_name", null: false
4141
t.string "service_name", null: false
42-
t.integer "service_group_id", null: false
43-
t.integer "event_id", null: false
42+
t.string "service_group_id", null: false
43+
t.string "event_id", null: false
4444
t.string "event_url", null: false
4545
t.integer "participants", null: false
4646
t.datetime "evented_at", null: false

lib/statistics/aggregation.rb

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ class << self
44
def run(date:)
55
cnps_dojos = Dojo.joins(:dojo_event_service).where(dojo_event_services: { name: :connpass }).to_a
66
drkp_dojos = Dojo.joins(:dojo_event_service).where(dojo_event_services: { name: :doorkeeper }).to_a
7+
fsbk_dojos = Dojo.joins(:dojo_event_service).where(dojo_event_services: { name: :facebook }).to_a
78

89
Connpass.run(cnps_dojos, date)
910
Doorkeeper.run(drkp_dojos, date)
11+
Facebook.run(fsbk_dojos, date)
1012
end
1113
end
1214

@@ -20,7 +22,7 @@ def run(dojos, date)
2022

2123
dojos.each do |dojo|
2224
cnps.fetch_events(params.merge(series_id: dojo.dojo_event_service.group_id)).each do |e|
23-
next unless e.dig('series', 'id') == dojo.dojo_event_service.group_id
25+
next unless e.dig('series', 'id').to_s == dojo.dojo_event_service.group_id
2426

2527
EventHistory.create!(dojo_id: dojo.id,
2628
dojo_name: dojo.name,
@@ -47,7 +49,7 @@ def run(dojos, date)
4749

4850
dojos.each do |dojo|
4951
drkp.fetch_events(params.merge(group_id: dojo.dojo_event_service.group_id)).each do |e|
50-
next unless e['group'] == dojo.dojo_event_service.group_id
52+
next unless e['group'].to_s == dojo.dojo_event_service.group_id
5153

5254
EventHistory.create!(dojo_id: dojo.id,
5355
dojo_name: dojo.name,
@@ -62,5 +64,32 @@ def run(dojos, date)
6264
end
6365
end
6466
end
67+
68+
class Facebook
69+
class << self
70+
def run(dojos, date)
71+
fsbk = Client::Facebook.new
72+
params = {
73+
since_at: date.beginning_of_month,
74+
until_at: date.end_of_month
75+
}
76+
77+
dojos.each do |dojo|
78+
fsbk.fetch_events(params.merge(group_id: dojo.dojo_event_service.group_id)).each do |e|
79+
next unless e.dig('owner', 'id') == dojo.dojo_event_service.group_id
80+
81+
EventHistory.create!(dojo_id: dojo.id,
82+
dojo_name: dojo.name,
83+
service_name: dojo.dojo_event_service.name,
84+
service_group_id: dojo.dojo_event_service.group_id,
85+
event_id: e['id'],
86+
event_url: "https://www.facebook.com/events/#{e['id']}",
87+
participants: e['attending_count'],
88+
evented_at: Time.zone.parse(e['start_time']))
89+
end
90+
end
91+
end
92+
end
93+
end
6594
end
6695
end

lib/statistics/client.rb

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
module Statistics
22
class Client
3-
class APIRateLimitError < ::StandardError; end
4-
53
class_attribute :debug
64
self.debug = false
75

@@ -81,32 +79,62 @@ def search(keyword:)
8179
end
8280

8381
def fetch_events(group_id:, since_at: @default_since, until_at: @default_until)
84-
params = {
85-
page: 1,
86-
since: since_at,
87-
until: until_at
88-
}
89-
events = []
82+
begin
83+
params = {
84+
page: 1,
85+
since: since_at,
86+
until: until_at
87+
}
88+
events = []
9089

91-
loop do
92-
part = @client.get("groups/#{group_id}/events", params)
90+
loop do
91+
part = @client.get("groups/#{group_id}/events", params)
92+
93+
break if part.size.zero?
94+
95+
events.push(*part.map { |e| e['event'] })
9396

94-
break if part.size.zero?
97+
break if part.size < 25 # 25 items / 1 request
9598

96-
events.push(*part.map { |e| e['event'] })
99+
params[:page] += 1
100+
end
97101

98-
break if part.size < 25 # 25 items / 1 request
102+
events
103+
rescue Faraday::ClientError => e
104+
raise e unless e.response[:status] == 429
99105

100-
params[:page] += 1
106+
puts 'API rate limit exceeded.'
107+
puts "This task will retry in 60 seconds from now(#{Time.zone.now})."
108+
sleep 60
109+
retry
101110
end
111+
end
112+
end
102113

103-
events
104-
rescue Faraday::ClientError => e
105-
if e.response[:status] == 429
106-
raise Client::APIRateLimitError
107-
else
108-
raise e
114+
class Facebook
115+
class_attribute :access_token
116+
117+
def initialize
118+
@client = Koala::Facebook::API.new(self.access_token)
119+
end
120+
121+
def fetch_events(group_id:, since_at: nil, until_at: nil)
122+
params = {
123+
fields: %i(attending_count start_time owner),
124+
limit: 100,
125+
since: since_at,
126+
until: until_at
127+
}.compact
128+
129+
events = []
130+
131+
collection = @client.get_object("#{group_id}/events", params)
132+
events.push(*collection.to_a)
133+
while !collection.empty? && collection.paging['next'] do
134+
events.push(*collection.next_page.to_a)
109135
end
136+
137+
events
110138
end
111139
end
112140
end

lib/tasks/oauth.rake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace :oauth do
2+
desc 'Facebookのaccess tokenを取得します'
3+
task :facebook_access_token, [:app_id, :app_secret] => :environment do |_tasks, args|
4+
puts 'Access Token: ' + Koala::Facebook::OAuth.new(args[:app_id], args[:app_secret]).get_app_access_token
5+
end
6+
end

lib/tasks/statistics.rake

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ namespace :statistics do
1616
d
1717
}
1818

19+
notify_idobata = -> (msg) {
20+
puts `curl --data-urlencode "source=#{msg}" -s #{ENV['IDOBATA_HOOK_URL']} -o /dev/null -w "idobata: %{http_code}"` if ENV.key?('IDOBATA_HOOK_URL')
21+
}
22+
1923
from = if args[:from]
2024
if args[:from].length == 4
2125
date_from_str.call(args[:from]).beginning_of_year
@@ -35,23 +39,24 @@ namespace :statistics do
3539
Time.current.prev_month.end_of_month
3640
end
3741

42+
Statistics::Client::Facebook.access_token = Koala::Facebook::OAuth.new(ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET']).get_app_access_token
43+
3844
EventHistory.where(evented_at: from..to).delete_all
3945

40-
loop.with_object([from]) { |_, list|
41-
nm = list.last.next_month
42-
raise StopIteration if nm > to
43-
list << nm
44-
}.each { |date|
45-
begin
46+
begin
47+
loop.with_object([from]) { |_, list|
48+
nm = list.last.next_month
49+
raise StopIteration if nm > to
50+
list << nm
51+
}.each { |date|
4652
puts "Aggregate for #{date.strftime('%Y/%m')}"
4753
Statistics::Aggregation.run(date: date)
48-
rescue Statistics::Client::APIRateLimitError
49-
puts 'API rate limit exceeded.'
50-
puts "This task will retry in 60 seconds from now(#{Time.zone.now})."
51-
sleep 60
52-
retry
53-
end
54-
}
54+
}
55+
56+
notify_idobata.call("#{from.strftime('%Y/%m')}~#{to.strftime('%Y/%m')}のイベント履歴の集計を行いました")
57+
rescue
58+
notify_idobata.call("#{from.strftime('%Y/%m')}~#{to.strftime('%Y/%m')}のイベント履歴の集計でエラーが発生しました")
59+
end
5560
end
5661

5762
desc 'キーワードからイベント情報を検索します'

spec/lib/statistics/aggregation_spec.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
require 'statistics'
33

44
RSpec.describe Statistics::Aggregation do
5-
include_context 'Use stubs for Faraday'
5+
include_context 'Use stubs for Connpass'
6+
include_context 'Use stubs for Doorkeeper'
7+
include_context 'Use stubs for Facebook'
68

79
before(:all) do
810
Dojo.destroy_all
@@ -16,14 +18,16 @@
1618
before do
1719
d1 = Dojo.create(name: 'Dojo1', email: 'info@dojo1.com', description: 'CoderDojo1', tags: %w(CoderDojo1), url: 'https://dojo1.com')
1820
d2 = Dojo.create(name: 'Dojo2', email: 'info@dojo2.com', description: 'CoderDojo2', tags: %w(CoderDojo2), url: 'https://dojo2.com')
21+
d3 = Dojo.create(name: 'Dojo3', email: 'info@dojo3.com', description: 'CoderDojo3', tags: %w(CoderDojo3), url: 'https://dojo3.com')
1922
DojoEventService.create(dojo_id: d1.id, name: :connpass, group_id: 9876)
2023
DojoEventService.create(dojo_id: d2.id, name: :doorkeeper, group_id: 5555)
24+
DojoEventService.create(dojo_id: d3.id, name: :facebook, group_id: 123451234512345)
2125
end
2226

2327
subject { Statistics::Aggregation.run(date: Time.current) }
2428

2529
it do
26-
expect{ subject }.to change{EventHistory.count}.from(0).to(2)
30+
expect{ subject }.to change{EventHistory.count}.from(0).to(3)
2731
end
2832
end
2933
end

spec/lib/statistics/client_spec.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
require 'statistics'
33

44
RSpec.describe Statistics::Client do
5-
include_context 'Use stubs for Faraday'
6-
75
context 'Connpass' do
6+
include_context 'Use stubs for Connpass'
7+
88
describe '#search' do
99
subject { Statistics::Client::Connpass.new.search(keyword: 'coderdojo') }
1010

@@ -32,6 +32,8 @@
3232
end
3333

3434
context 'Doorkeeper' do
35+
include_context 'Use stubs for Doorkeeper'
36+
3537
describe '#search' do
3638
subject { Statistics::Client::Doorkeeper.new.search(keyword: 'coderdojo') }
3739

@@ -54,4 +56,19 @@
5456
end
5557
end
5658
end
59+
60+
context 'Facebook' do
61+
include_context 'Use stubs for Facebook'
62+
63+
describe '#fetch_events' do
64+
subject { Statistics::Client::Facebook.new.fetch_events(group_id: 123451234512345) }
65+
66+
it do
67+
expect(subject).to be_instance_of(Array)
68+
expect(subject.size).to eq 1
69+
expect(subject.first['id']).to eq '125500978166443'
70+
expect(subject.first.dig('owner', 'id')).to eq '123451234512345'
71+
end
72+
end
73+
end
5774
end

0 commit comments

Comments
 (0)