Skip to content

Commit 33a16a6

Browse files
committed
近日開催イベント情報を収集する upcoming_events:aggregation のタスクを追加
1 parent 7f21570 commit 33a16a6

14 files changed

+356
-4
lines changed

app/models/dojo.rb

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class Dojo < ApplicationRecord
66
belongs_to :prefecture
77
has_many :dojo_event_services, dependent: :destroy
88
has_many :event_histories, dependent: :destroy
9+
has_many :upcoming_events, dependent: :destroy
910

1011
serialize :tags
1112
before_save { self.email = self.email.downcase }

app/models/dojo_event_service.rb

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ class DojoEventService < ApplicationRecord
33
INTERNAL_SERVICES = %i( static_yaml )
44

55
belongs_to :dojo
6+
67
enum name: EXTERNAL_SERVICES + INTERNAL_SERVICES
78

89
validates :name, presence: true

app/models/upcoming_event.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class UpcomingEvent < ApplicationRecord
2+
belongs_to :dojo
3+
4+
validates :dojo_name, presence: true
5+
validates :service_name, presence: true, uniqueness: { scope: :event_id }
6+
validates :event_id, presence: true
7+
validates :event_url, presence: true
8+
validates :event_at, presence: true
9+
validates :participants, presence: true
10+
11+
scope :for, ->(service) { where(dojo_event_service: DojoEventService.for(service)) }
12+
scope :until, ->(date) { where('event_at < ?', date.beginning_of_day) }
13+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class ModColumnsToUpcomingEvent < ActiveRecord::Migration[5.1]
2+
def up
3+
remove_index :upcoming_events, :dojo_event_service_id
4+
remove_index :upcoming_events, :event_at
5+
remove_column :upcoming_events, :dojo_event_service_id
6+
7+
add_reference :upcoming_events, :dojo, foreign_key: true, index: true, null: false
8+
add_column :upcoming_events, :dojo_name, :string, null: false
9+
add_column :upcoming_events, :service_name, :string, null: false
10+
add_column :upcoming_events, :participants, :integer, null: false
11+
end
12+
13+
def down
14+
remove_reference :upcoming_events, :dojo, findex: true
15+
remove_column :upcoming_events, :dojo_name, :string, null: false
16+
remove_column :upcoming_events, :service_name, :string, null: false
17+
remove_column :upcoming_events, :participants, :integer, null: false
18+
19+
add_column :upcoming_events, :dojo_event_service_id, :integer, null: false, default: 1
20+
add_index :upcoming_events, :dojo_event_service_id, name: "index_upcoming_events_on_dojo_event_service_id"
21+
add_index :upcoming_events, :event_at, name: "index_upcoming_events_on_event_at"
22+
end
23+
end

db/schema.rb

+7-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema.define(version: 20190423141200) do
13+
ActiveRecord::Schema.define(version: 20190526151359) do
1414

1515
# These are extensions that must be enabled in order to support this database
1616
enable_extension "plpgsql"
@@ -80,14 +80,17 @@
8080
end
8181

8282
create_table "upcoming_events", force: :cascade do |t|
83-
t.integer "dojo_event_service_id", null: false
8483
t.string "event_id", null: false
8584
t.string "event_url", null: false
8685
t.datetime "event_at", null: false
87-
t.index ["dojo_event_service_id"], name: "index_upcoming_events_on_dojo_event_service_id"
88-
t.index ["event_at"], name: "index_upcoming_events_on_event_at"
86+
t.bigint "dojo_id", null: false
87+
t.string "dojo_name", null: false
88+
t.string "service_name", null: false
89+
t.integer "participants", null: false
90+
t.index ["dojo_id"], name: "index_upcoming_events_on_dojo_id"
8991
end
9092

9193
add_foreign_key "dojo_event_services", "dojos"
9294
add_foreign_key "event_histories", "dojos"
95+
add_foreign_key "upcoming_events", "dojos"
9396
end

lib/tasks/upcoming_events.rake

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require_relative '../upcoming_events.rb'
2+
3+
namespace :upcoming_events do
4+
desc '指定期間/プロバイダのイベント履歴を集計します'
5+
task :aggregation, [:provider] => :environment do |tasks, args|
6+
UpcomingEvent.transaction do
7+
UpcomingEvents::Aggregation.new(args).run
8+
end
9+
end
10+
end

lib/upcoming_events.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module UpcomingEvents; end
2+
3+
require_relative 'upcoming_events/tasks'
4+
require_relative 'upcoming_events/aggregation'
5+
require_relative 'event_service'

lib/upcoming_events/aggregation.rb

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
module UpcomingEvents
2+
class Aggregation
3+
def initialize(args)
4+
@from = Time.zone.today
5+
@to = @from + 2.months
6+
@provider = args[:provider]
7+
dojos = fetch_dojos(@provider)
8+
# NOTE: 対象は一旦収集可能な connpass, doorkeeper のみにする
9+
@externals = dojos[:externals]
10+
# @internals = dojos[:internals]
11+
end
12+
13+
def run
14+
puts "UpcomingEvents aggregate"
15+
with_notifying do
16+
delete_upcoming_events
17+
execute
18+
end
19+
end
20+
21+
private
22+
23+
def fetch_dojos(provider)
24+
if provider.blank?
25+
# 全プロバイダ対象
26+
external_services = DojoEventService::EXTERNAL_SERVICES
27+
internal_services = DojoEventService::INTERNAL_SERVICES
28+
else
29+
external_services = []
30+
internal_services = []
31+
case provider
32+
when 'connpass', 'doorkeeper', 'facebook'
33+
external_services = [provider]
34+
when 'static_yaml'
35+
internal_services = [provider]
36+
end
37+
end
38+
39+
{
40+
externals: find_dojos_by(external_services),
41+
internals: find_dojos_by(internal_services)
42+
}
43+
end
44+
45+
def find_dojos_by(services)
46+
services.each.with_object({}) do |name, hash|
47+
hash[name] = Dojo.eager_load(:dojo_event_services).where(dojo_event_services: { name: name }).to_a
48+
end
49+
end
50+
51+
def with_notifying
52+
yield
53+
Notifier.notify_success(@provider)
54+
rescue => e
55+
Notifier.notify_failure(@provider, e)
56+
end
57+
58+
def delete_upcoming_events
59+
UpcomingEvent.until(@from).delete_all
60+
end
61+
62+
def execute
63+
target_period = @from..@to
64+
@externals.each do |kind, list|
65+
if kind == :facebook
66+
puts "Aggregate of #{kind} --> skip"
67+
next
68+
end
69+
puts "Aggregate of #{kind}"
70+
"UpcomingEvents::Tasks::#{kind.to_s.camelize}".constantize.new(list, target_period).run
71+
end
72+
end
73+
74+
class Notifier
75+
class << self
76+
def notify_success(provider)
77+
notify("近日開催イベント情報#{provider_info(provider)}を収集しました")
78+
end
79+
80+
def notify_failure(provider, exception)
81+
notify("近日開催イベント情報の収集#{provider_info(provider)}でエラーが発生しました\n#{exception.message}\n#{exception.backtrace.join("\n")}")
82+
end
83+
84+
private
85+
86+
def provider_info(provider)
87+
provider ? "(#{provider})" : nil
88+
end
89+
90+
def idobata_hook_url
91+
return @idobata_hook_url if defined?(@idobata_hook_url)
92+
@idobata_hook_url = ENV['IDOBATA_HOOK_URL']
93+
end
94+
95+
def notifierable?
96+
idobata_hook_url.present?
97+
end
98+
99+
def notify(msg)
100+
$stdout.puts msg
101+
puts `curl --data-urlencode "source=#{msg}" -s #{idobata_hook_url} -o /dev/null -w "idobata: %{http_code}"` if notifierable?
102+
end
103+
end
104+
end
105+
end
106+
end

lib/upcoming_events/tasks.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module UpcomingEvents
2+
module Tasks
3+
end
4+
end
5+
6+
require_relative 'tasks/connpass'
7+
require_relative 'tasks/doorkeeper'
8+
# require_relative 'tasks/facebook'
9+
# require_relative 'tasks/static_yaml'

lib/upcoming_events/tasks/connpass.rb

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
module UpcomingEvents
2+
module Tasks
3+
class Connpass
4+
def initialize(dojos, period)
5+
@client = EventService::Providers::Connpass.new
6+
@dojos = dojos
7+
@params = build_params(period)
8+
end
9+
10+
def run
11+
@dojos.each do |dojo|
12+
dojo.dojo_event_services.for(:connpass).each do |dojo_event_service|
13+
@client.fetch_events(@params.merge(series_id: dojo_event_service.group_id)).each do |e|
14+
next unless e.dig('series', 'id').to_s == dojo_event_service.group_id
15+
16+
record = UpcomingEvent.find_or_initialize_by(dojo_id: dojo.id,
17+
service_name: dojo_event_service.name,
18+
event_id: e['event_id'])
19+
record.update!(dojo_name: dojo.name,
20+
event_url: e['event_url'],
21+
event_at: Time.zone.parse(e['started_at']),
22+
participants: e['accepted'])
23+
end
24+
end
25+
end
26+
end
27+
28+
private
29+
30+
def build_params(period)
31+
yyyymmdd = []
32+
yyyymm = []
33+
34+
st_date = period.first
35+
ed_date = period.last
36+
37+
date = period.first
38+
while date <= ed_date
39+
if date.day == 1 && date.end_of_month <= ed_date
40+
yyyymm << date.strftime('%Y%m')
41+
date += 1.month
42+
else
43+
yyyymmdd << date.strftime('%Y%m%d')
44+
date += 1.day
45+
end
46+
end
47+
48+
{
49+
yyyymmdd: yyyymmdd,
50+
yyyymm: yyyymm
51+
}
52+
end
53+
end
54+
end
55+
end
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module UpcomingEvents
2+
module Tasks
3+
class Doorkeeper
4+
def initialize(dojos, period)
5+
@client = EventService::Providers::Doorkeeper.new
6+
@dojos = dojos
7+
@params = build_params(period)
8+
end
9+
10+
def run
11+
@dojos.each do |dojo|
12+
dojo.dojo_event_services.for(:doorkeeper).each do |dojo_event_service|
13+
@client.fetch_events(@params.merge(group_id: dojo_event_service.group_id)).each do |e|
14+
next unless e['group'].to_s == dojo_event_service.group_id
15+
16+
record = UpcomingEvent.find_or_initialize_by(dojo_id: dojo.id,
17+
service_name: dojo_event_service.name,
18+
event_id: e['id'])
19+
record.update!(dojo_name: dojo.name,
20+
event_url: e['public_url'],
21+
participants: e['participants'],
22+
event_at: Time.zone.parse(e['starts_at']))
23+
end
24+
end
25+
end
26+
end
27+
28+
private
29+
30+
def build_params(period)
31+
{
32+
since_at: period.first.beginning_of_day,
33+
until_at: period.last.end_of_day
34+
}
35+
end
36+
end
37+
end
38+
end

spec/factories/upcoming_events.rb

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require 'factory_bot'
2+
3+
FactoryBot.define do
4+
factory :upcoming_event do
5+
# dojo_id
6+
dojo_name { 'Dojo Name' }
7+
service_name { :connpass }
8+
event_id { '1234' }
9+
event_url { 'http:/www.aaa.com/events/1224' }
10+
event_at { '2019-05-01 10:00'.in_time_zone }
11+
participants { 1 }
12+
end
13+
end
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
require 'rails_helper'
2+
require 'upcoming_events'
3+
4+
RSpec.describe UpcomingEvents::Aggregation do
5+
include_context 'Use stubs UpcomingEvents for Connpass'
6+
include_context 'Use stubs UpcomingEvents for Doorkeeper'
7+
8+
describe '.run' do
9+
before do
10+
@d1 = create(:dojo, name: 'Dojo1', email: '[email protected]', description: 'CoderDojo1', tags: %w(CoderDojo1), url: 'https://dojo1.com')
11+
@d2 = create(:dojo, name: 'Dojo2', email: '[email protected]', description: 'CoderDojo2', tags: %w(CoderDojo2), url: 'https://dojo2.com')
12+
create(:dojo_event_service, dojo_id: @d1.id, name: :connpass, group_id: 9876)
13+
create(:dojo_event_service, dojo_id: @d2.id, name: :doorkeeper, group_id: 5555)
14+
end
15+
16+
it 'プロバイダ指定なし' do
17+
expect{ UpcomingEvents::Aggregation.new({}).run }.to change{ UpcomingEvent.count }.from(0).to(3)
18+
end
19+
20+
it 'プロバイダ指定(connpass)' do
21+
expect{ UpcomingEvents::Aggregation.new(provider: 'connpass').run }.to change{ UpcomingEvent.count }.from(0).to(1)
22+
end
23+
24+
it 'プロバイダ指定(doorkeeper)' do
25+
expect{ UpcomingEvents::Aggregation.new(provider: 'doorkeeper').run }.to change{ UpcomingEvent.count }.from(0).to(2)
26+
end
27+
28+
it '昨日分までは削除' do
29+
create(:upcoming_event, dojo_id: @d1.id, service_name: 'connpass', event_id: '1111', event_at: "#{Time.zone.today - 3.days} 13:00:00".in_time_zone)
30+
create(:upcoming_event, dojo_id: @d1.id, service_name: 'connpass', event_id: '2222', event_at: "#{Time.zone.today - 2.days} 14:00:00".in_time_zone)
31+
create(:upcoming_event, dojo_id: @d1.id, service_name: 'connpass', event_id: '3333', event_at: "#{Time.zone.today - 1.days} 15:00:00".in_time_zone)
32+
create(:upcoming_event, dojo_id: @d2.id, service_name: 'doorkeeper', event_id: '4444', event_at: "#{Time.zone.today - 2.days} 10:00:00".in_time_zone)
33+
create(:upcoming_event, dojo_id: @d2.id, service_name: 'doorkeeper', event_id: '5555', event_at: "#{Time.zone.today - 1.days} 11:00:00".in_time_zone)
34+
35+
expect{ UpcomingEvents::Aggregation.new({}).run }.to change{ UpcomingEvent.count }.from(5).to(3)
36+
end
37+
end
38+
end

0 commit comments

Comments
 (0)