From 2b11cb7abbb19a9898b61f06b275ce384d1ed257 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 9 Mar 2014 13:38:46 +0000 Subject: [PATCH] VRFS-925 ga event processing --- db/manifest | 1 + db/up/bands_did_session.sql | 2 + ruby/lib/jam_ruby/models/band.rb | 6 + ruby/lib/jam_ruby/models/connection.rb | 12 ++ ruby/lib/jam_ruby/models/music_session.rb | 14 +++ ruby/lib/jam_ruby/models/recording.rb | 6 + .../jam_ruby/resque/google_analytics_event.rb | 115 ++++++++++++++---- .../resque/google_analytics_event_spec.rb | 49 ++++++++ ruby/spec/spec_helper.rb | 27 ++++ 9 files changed, 207 insertions(+), 25 deletions(-) create mode 100644 db/up/bands_did_session.sql create mode 100644 ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb diff --git a/db/manifest b/db/manifest index 47d143af9..77b44e5ca 100755 --- a/db/manifest +++ b/db/manifest @@ -130,3 +130,4 @@ scores_better_test_data.sql connection_client_type.sql add_countries_regions_and_cities.sql plays_refactor.sql +bands_did_session.sql diff --git a/db/up/bands_did_session.sql b/db/up/bands_did_session.sql new file mode 100644 index 000000000..62bb9222c --- /dev/null +++ b/db/up/bands_did_session.sql @@ -0,0 +1,2 @@ +ALTER TABLE bands ADD COLUMN did_real_session boolean default false; + diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index ea14cabe5..7a5822581 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -258,6 +258,12 @@ module JamRuby name end + def in_real_session?(session) + b_members = self.users.sort_by(&:id).map(&:id) + s_members = session.users.sort_by(&:id).map(&:id) + (b_members - s_members).blank? + end + private def require_at_least_one_genre diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb index c0a6c124b..a4ffa60b2 100644 --- a/ruby/lib/jam_ruby/models/connection.rb +++ b/ruby/lib/jam_ruby/models/connection.rb @@ -18,6 +18,7 @@ module JamRuby validate :can_join_music_session, :if => :joining_session? after_save :require_at_least_one_track_when_in_session, :if => :joining_session? after_create :did_create + before_save :report_add_participant include AASM IDLE_STATE = :idle @@ -127,6 +128,17 @@ module JamRuby self.user.update_lat_lng(self.ip_address) if self.user && self.ip_address end + def report_add_participant + if self.music_session_id_changed? && + self.music_session.present? && + self.connected? && + self.as_musician? && + 0 < (count = self.music_session.connected_participant_count) + GoogleAnalyticsEvent.report_session_participant(count) + end + true + end + private def require_at_least_one_track_when_in_session if tracks.count == 0 diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 70eb50e7f..c44ff8387 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -24,6 +24,8 @@ module JamRuby has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" + after_create :started_session + validate :require_at_least_one_genre, :limit_max_genres after_save :sync_music_session_history @@ -400,6 +402,18 @@ module JamRuby self.save!(:validate => false) end + def connected_participant_count + Connection.where(:music_session_id => self.id, + :aasm_state => Connection::CONNECT_STATE.to_s, + :as_musician => true) + .count + end + + def started_session + GoogleAnalyticsEvent.track_session_duration(self) + GoogleAnalyticsEvent.track_band_real_session(self) + end + private def require_at_least_one_genre diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 65ab57968..cb4d11d0d 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -28,6 +28,7 @@ module JamRuby before_save :sanitize_active_admin before_create :add_to_feed + after_create :check_first_band_recording def add_to_feed feed = Feed.new @@ -39,6 +40,11 @@ module JamRuby self.band_id = nil if self.band_id == '' end + def check_first_band_recording + GoogleAnalyticsEvent.report_band_recording(self.band) + true + end + def comment_count self.comments.size end diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index 30c08084b..bca201c3e 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -1,43 +1,108 @@ -class GoogleAnalyticsEvent +require 'resque' - @queue = 'google_analytics_event' +module JamRuby + class GoogleAnalyticsEvent - @@log = Logging.logger[GoogleAnalyticsEvent] + @queue = :google_analytics_event - def self.perform(category, action) + CAT_SESS_SIZE = 'SessionSize' + ACTION_SESS_SIZE = 'Size' + CAT_SESS_DUR = 'SessionDuration' + ACTION_SESS_DUR = 'Duration' + CAT_BAND = 'Band' + ACTION_BAND_SESS = 'Session' + ACTION_BAND_REC = 'Recording' - @@log.info("starting (#{category}, #{action})") + @@log = Logging.logger[GoogleAnalyticsEvent] - run(category, action) + SESSION_INTERVALS = [1, 5, 10, 15, 30, 45, 60, 90, 120, 180] # minutes + QUEUE_SESSION_TRACKER = :session_tracker - @@log.info("done (#{category}, #{action})") + class SessionDurationTracker + @queue = QUEUE_SESSION_TRACKER - end - - def self.enqueue(category, event) - begin - Resque.enqueue(AudioMixer, category, event) - true - rescue - # implies redis is down. but since there is no retry logic with this, we should at least log a warn in case we've configured something wrong - @@log.warn("unable to enqueue") - false + def self.perform(session_id, interval_idx) + return unless session = MusicSession.find(session_id) + interval_idx += 1 + GoogleAnalyticsEvent.enqueue(CAT_SESS_DUR, ACTION_SESS_DUR, interval_idx) + + if SESSION_INTERVALS.count-1 > interval_idx + next_time = session.created_at + SESSION_INTERVALS[interval_idx].minutes + Resque.enqueue_at(next_time, + self, + :session_id => session_id, + :interval_idx => interval_idx) + end + end end - end - def self.run(category, action) + class BandSessionTracker + @queue = QUEUE_SESSION_TRACKER - raise "no google analytics tracking ID" unless APP_CONFIG.ga_ua + def self.perform(session_id) + return unless session = MusicSession.find(session_id) + band = session.band + if band.in_real_session(session)? + GoogleAnalyticsEvent.enqueue(CAT_BAND, ACTION_BAND_SESS) + band.update_attributes!(did_real_session: true) + end if band + end + end - params = { + def self.track_session_duration(session) + Resque.enqueue_at(SESSION_INTERVALS[0].minute.from_now, + SessionDurationTracker, + :session_id => session.id, + :interval_idx => 0) + end + + BAND_SESSION_MIN_DURATION = 15 # minutes + + def self.track_band_real_session(session) + if session.band && !session.band.did_real_session? + Resque.enqueue_at(BAND_SESSION_MIN_DURATION.minutes.from_now, + BandSessionTracker, + :session_id => session.id) + end + end + + def self.report_band_recording(band) + if band && 1 == Recording.where(:band_id => band.id).count + self.enqueue(CAT_BAND, ACTION_BAND_REC) + end + end + + def self.report_session_participant(participant_count) + self.enqueue(CAT_SESS_SIZE, ACTION_SESS_SIZE, participant_count) + end + + def self.enqueue(category, event, data=nil) + begin + Resque.enqueue(GoogleAnalyticsEvent, category, event, data) + true + rescue + # implies redis is down. but since there is no retry logic with this, we should at least log a warn in case we've configured something wrong + @@log.warn("unable to enqueue") + false + end + end + + def self.perform(category, action, data) + @@log.info("starting (#{category}, #{action})") + raise "no google analytics tracking ID" unless APP_CONFIG.ga_ua + params = { v: APP_CONFIG.ga_ua_version, tid: APP_CONFIG.ga_ua, cid: APP_CONFIG.ga_anonymous_client_id, t: "event", ec: category, - ea: action - } + ea: action, + el: 'data', + ev: data.to_s + } + RestClient.post(APP_CONFIG.ga_endpoint, params: params, timeout: 8, open_timeout: 8) + @@log.info("done (#{category}, #{action})") + end - RestClient.post(APP_CONFIG.ga_endpoint, params: params, timeout: 8, open_timeout: 8) end -end \ No newline at end of file +end diff --git a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb new file mode 100644 index 000000000..5c9c113f0 --- /dev/null +++ b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests +describe GoogleAnalyticsEvent do + + let(:ga) { GoogleAnalyticsEvent.new } + + describe "report session participant" do + it "queues job when session participant joins" do + pending + end + end + + describe "track session duration" do + + it "queues jobs for session duration intervals" do + pending + end + + it "stop queueing jobs if session is deleted" do + pending + end + end + + describe "track band real session" do + it "queues jobs for band real session tracking" do + pending + end + + it "stop queueing jobs if band session is deleted" do + pending + end + + it "updates band real session attributes" do + pending + end + end + + describe "report band recording" do + it "queues job when first band recording is made" do + pending + end + + it "does not queues job when band has a recording already" do + pending + end + end + +end diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 46034742c..0a0b444ce 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -64,6 +64,7 @@ Spork.prefork do # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| + config.color_enabled = true config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true @@ -101,6 +102,32 @@ Spork.prefork do # the seed, which is printed after each run. # --seed 1234 config.order = 'random' + + REDIS_PID = "#{Rails.root}/tmp/pids/redis-test.pid" + REDIS_CACHE_PATH = "#{Rails.root}/tmp/cache/" + config.before(:suite) do + redis_options = { + "daemonize" => 'yes', + "pidfile" => REDIS_PID, + "port" => 9736, + "timeout" => 300, + "save 900" => 1, + "save 300" => 1, + "save 60" => 10000, + "dbfilename" => "dump.rdb", + "dir" => REDIS_CACHE_PATH, + "loglevel" => "debug", + "logfile" => "stdout", + "databases" => 16 + }.map { |k, v| "#{k} #{v}" }.join("\n") + `echo '#{redis_options}' | redis-server -` + end + config.after(:suite) do + %x{ + cat #{REDIS_PID} | xargs kill -QUIT + rm -f #{REDIS_CACHE_PATH}dump.rdb + } + end end end