diff --git a/admin/app/admin/cohorts.rb b/admin/app/admin/cohorts.rb index 7e9d4a90c..e9bd73fea 100644 --- a/admin/app/admin/cohorts.rb +++ b/admin/app/admin/cohorts.rb @@ -12,7 +12,7 @@ ActiveAdmin.register Cohort, :as => 'Cohorts' do def scoped_collection objs = super - Cohort.alltime_cohorts! if 0 == Cohort.where(all_time: true).count + Cohort.alltime_cohorts! objs.where(all_time: true).order('group_start DESC') end @@ -23,46 +23,46 @@ ActiveAdmin.register Cohort, :as => 'Cohorts' do div(class: :cohort_col) { cc.group_start_str } end - column Cohort::TOTAL_LABELS[:registered_users] do |cc| + column Cohort::ALLTIME_LABELS[:registered_users] do |cc| div(class: :cohort_col) { cc.data_val(:registered_users) } end - column Cohort::TOTAL_LABELS[:first_downloaded_client_at] do |cc| + column Cohort::ALLTIME_LABELS[:first_downloaded_client_at] do |cc| div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at, true) } end - column Cohort::TOTAL_LABELS[:first_certified_gear_at] do |cc| + column Cohort::ALLTIME_LABELS[:first_certified_gear_at] do |cc| div(class: :cohort_col) { cc.data_val(:first_certified_gear_at) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_certified_gear_at, true) } end - column Cohort::TOTAL_LABELS[:music_sessions_user_history] do |cc| + column Cohort::ALLTIME_LABELS[:music_sessions_user_history] do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history, true) } end - column Cohort::TOTAL_LABELS[:jam_track_played] do |cc| - div(class: :cohort_col) { cc.data_val(:jam_track_played) } + column Cohort::ALLTIME_LABELS[:jam_tracks_played] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played) } end - column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_played, true) } end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played, true) } end - column Cohort::TOTAL_LABELS[:jam_track_rights] do |cc| + column Cohort::ALLTIME_LABELS[:jam_track_rights] do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights, true) } end - column Cohort::TOTAL_LABELS[:recorded_tracks] do |cc| + column Cohort::ALLTIME_LABELS[:recorded_tracks] do |cc| div(class: :cohort_col) { cc.data_val(:recorded_tracks) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:recorded_tracks, true) } end - column Cohort::TOTAL_LABELS[:friendships] do |cc| + column Cohort::ALLTIME_LABELS[:friendships] do |cc| div(class: :cohort_col) { cc.data_val(:friendships) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:friendships, true) } end - column Cohort::TOTAL_LABELS[:invited_users] do |cc| + column Cohort::ALLTIME_LABELS[:invited_users] do |cc| div(class: :cohort_col) { cc.data_val(:invited_users) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:invited_users, true) } end diff --git a/admin/app/admin/cohorts_monthly.rb b/admin/app/admin/cohorts_monthly.rb index 942da3191..d062a8317 100644 --- a/admin/app/admin/cohorts_monthly.rb +++ b/admin/app/admin/cohorts_monthly.rb @@ -8,91 +8,89 @@ ActiveAdmin.register Cohort, :as => 'Cohorts Monthly' do config.per_page = 50 filter(:monthly_start, as: :select, collection: Cohort.monthly_starts) - filter(:monthly_end, as: :select, collection: Cohort.monthly_ends) controller do def scoped_collection - objs = super args = params[:q] || {} - if !(args[:monthly_start_eq].nil? || args[:monthly_end_eq].nil?) - mstart, mend = Time.parse(args[:monthly_start_eq]), Time.parse(args[:monthly_end_eq]) - if mstart < mend - Cohort.monthly_cohorts(mstart, mend) # populate monthlys - objs = objs.where(monthly_start: mstart, monthly_end: mend) - end - end - objs.where(all_time: false).order('group_start DESC') + Cohort.monthly_cohorts!(Time.parse(args[:monthly_start_eq])) if ! args[:monthly_start_eq].nil? + super.where(all_time: false).order('group_start DESC') end end - index :title => "Monthly Cohorts" do - column 'Cohort' do |cc| cc.group_start_str end - column Cohort::MONTHLY_LABELS[:registered_users] do |cc| cc.data_val(:registered_users) end + index :title => proc { "Monthly Cohorts #{params[:q] ? '('+Time.parse(params[:q][:monthly_start_eq]).strftime('%Y-%m')+')' : ''}" } do + + column 'Cohort' do |cc| + div(class: :cohort_col) { cc.group_start_str } + end + + column Cohort::MONTHLY_LABELS[:registered_users] do |cc| + div(class: :cohort_col) { cc.data_val(:registered_users) } + end column Cohort::MONTHLY_LABELS[:first_downloaded_client_at] do |cc| - cc.data_val(:first_downloaded_client_at) + div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at) } end - column '%' do |cc| cc.data_val(:first_downloaded_client_at, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at, true) } end column Cohort::MONTHLY_LABELS[:first_certified_gear_at] do |cc| - cc.data_val(:first_certified_gear_at) + div(class: :cohort_col) { cc.data_val(:first_certified_gear_at) } end - column '%' do |cc| cc.data_val(:first_certified_gear_at, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_certified_gear_at, true) } end column Cohort::MONTHLY_LABELS[:music_sessions_user_history_1] do |cc| - cc.data_val(:music_sessions_user_history_1) + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_1) } end - column '%' do |cc| cc.data_val(:music_sessions_user_history_1, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_1, true) } end column Cohort::MONTHLY_LABELS[:music_sessions_user_history_2_5] do |cc| - cc.data_val(:music_sessions_user_history_2_5) + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_2_5) } end - column '%' do |cc| cc.data_val(:music_sessions_user_history_2_5, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_2_5, true) } end column Cohort::MONTHLY_LABELS[:music_sessions_user_history_6_] do |cc| - cc.data_val(:music_sessions_user_history_6_) + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_6_) } end - column '%' do |cc| cc.data_val(:music_sessions_user_history_6_, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_6_, true) } end - column Cohort::MONTHLY_LABELS[:jam_track_played_1] do |cc| - cc.data_val(:jam_track_played_1) + column Cohort::MONTHLY_LABELS[:jam_tracks_played_1] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played_1) } end - column '%' do |cc| cc.data_val(:jam_track_played_1, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_1, true) } end - column Cohort::MONTHLY_LABELS[:jam_track_played_2_5] do |cc| - cc.data_val(:jam_track_played_2_5) + column Cohort::MONTHLY_LABELS[:jam_tracks_played_2_5] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played_2_5) } end - column '%' do |cc| cc.data_val(:jam_track_played_2_5, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_2_5, true) } end - column Cohort::MONTHLY_LABELS[:jam_track_played_6_] do |cc| - cc.data_val(:jam_track_played_6_) + column Cohort::MONTHLY_LABELS[:jam_tracks_played_6_] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played_6_) } end - column '%' do |cc| cc.data_val(:jam_track_played_6_, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_6_, true) } end column Cohort::MONTHLY_LABELS[:jam_track_rights_redeemed] do |cc| - cc.data_val(:jam_track_rights_redeemed) + div(class: :cohort_col) { cc.data_val(:jam_track_rights_redeemed) } end - column '%' do |cc| cc.data_val(:jam_track_rights_redeemed, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights_redeemed, true) } end column Cohort::MONTHLY_LABELS[:jam_track_rights] do |cc| - cc.data_val(:jam_track_rights) + div(class: :cohort_col) { cc.data_val(:jam_track_rights) } end - column '%' do |cc| cc.data_val(:jam_track_rights, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights, true) } end column Cohort::MONTHLY_LABELS[:recorded_tracks] do |cc| - cc.data_val(:recorded_tracks) + div(class: :cohort_col) { cc.data_val(:recorded_tracks) } end - column '%' do |cc| cc.data_val(:recorded_tracks, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:recorded_tracks, true) } end column Cohort::MONTHLY_LABELS[:friendships] do |cc| - cc.data_val(:friendships) + div(class: :cohort_col) { cc.data_val(:friendships) } end - column '%' do |cc| cc.data_val(:friendships, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:friendships, true) } end column Cohort::MONTHLY_LABELS[:invited_users] do |cc| - cc.data_val(:invited_users) + div(class: :cohort_col) { cc.data_val(:invited_users) } end - column '%' do |cc| cc.data_val(:invited_users, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:invited_users, true) } end end end diff --git a/admin/app/models/cohort.rb b/admin/app/models/cohort.rb index 93173a619..29523a46d 100644 --- a/admin/app/models/cohort.rb +++ b/admin/app/models/cohort.rb @@ -3,36 +3,38 @@ require 'date' class Cohort < ActiveRecord::Base EARLIEST_DATE = Time.parse('2014-03-01') + TOTAL_COHORT_DATE = Time.at(0) - TOTAL_LABELS = { + ALLTIME_LABELS = { registered_users: 'Registered Users', - first_downloaded_client_at: 'Downloaded app', + first_downloaded_client_at: 'DL app', first_certified_gear_at: 'Certified Gear', music_sessions_user_history: 'Played Online', - jam_track_played: 'Played JamTrack', - jam_track_rights: 'Purchased JamTrack', + jam_tracks_played: 'Played JT', + jam_track_rights: 'Purchased JT', recorded_tracks: 'Made Recording', - friendships: 'Connected w/ Friend', + friendships: 'Friended', invited_users: 'Invite Others', } MONTHLY_LABELS = { registered_users: 'Registered Users', - first_downloaded_client_at: 'Downloaded app', + first_downloaded_client_at: 'DL app', first_certified_gear_at: 'Certified Gear', music_sessions_user_history_1: 'Played Online 1', music_sessions_user_history_2_5: 'Played Online 2-5', music_sessions_user_history_6_: 'Played Online 6+', - jam_track_played_1: 'Played Online 1', - jam_track_played_2_5: 'Played Online 2-5', - jam_track_played_6_: 'Played Online 6+', - jam_track_rights_redeemed: 'Redeemed JamTrack', - jam_track_rights: 'Purchased JamTrack', + jam_tracks_played_1: 'Played JT 1', + jam_tracks_played_2_5: 'Played JT 2-5', + jam_tracks_played_6_: 'Played JT 6+', + jam_track_rights_redeemed: 'Redeemed JT', + jam_track_rights: 'Purchased JT', recorded_tracks: 'Made Recording', - friendships: 'Connected w/ Friend', + friendships: 'Friended', invited_users: 'Invite Others', } + attr_accessible :all_time, :monthly_start serialize :data_set, JSON before_create do @@ -87,6 +89,7 @@ class Cohort < ActiveRecord::Base end def self.generate_all_time_cohorts + Cohort.delete_all("all_time = 't'") self.cohort_group_ranges.collect do |range| unless cc = Cohort.where(group_start: range.first).where(all_time: true).limit(1).first cc = Cohort.new @@ -121,7 +124,8 @@ class Cohort < ActiveRecord::Base SELECT played.user_id FROM (SELECT user_id, COUNT(*) cnt FROM music_sessions_user_history msuh1 WHERE - msuh1.created_at >= '#{start_date}' AND msuh1.created_at <= '#{end_date}' AND + msuh1.created_at >= '#{start_date}' AND + msuh1.created_at <= '#{end_date}' AND EXTRACT(EPOCH FROM (msuh1.session_removed_at - msuh1.created_at)) >= 900 AND (SELECT COUNT(*) FROM music_sessions_user_history msuh2 WHERE msuh1.music_session_id = msuh2.music_session_id @@ -132,8 +136,30 @@ WHERE #{where} SQL end + def _played_jamtrack_subquery(constraint) + where = if constraint.is_a?(Range) + "played.cnt >= #{constraint.first} AND played.cnt <= #{constraint.last}" + else + "played.cnt #{constraint}" + end + start_date = all_time ? self.group_start : self.monthly_start + end_date = all_time ? self.group_end : self.monthly_end + sql =<= '#{start_date}' AND + pp.created_at <= '#{end_date}' AND + pp.playable_type = 'JamRuby::JamTrack' + GROUP BY player_id + ) played +WHERE #{where} +SQL + end + def _subquery(assoc_key, num_user) assoc = User.reflections[assoc_key] + return 0 unless assoc start_date = all_time ? self.group_start : self.monthly_start end_date = all_time ? self.group_end : self.monthly_end sql =<= 6') - # count = self.class.cohort_users(self).where("users.id IN (#{sql})").count - # _put_data_set(:jam_track_played_6_, count, num_user) + sql = _played_jamtrack_subquery(' >= 6') + count = self.class.cohort_users(self).where("users.id IN (#{sql})").count + _put_data_set(:jam_tracks_played_6_, count, num_user) self.save! end @@ -247,8 +273,10 @@ SQL count = _subquery(assoc_key = :jam_track_rights, num_user) _put_data_set(assoc_key, count, num_user) - # count = _subquery(assoc_key = :jam_track_played, num_user) - # _put_data_set(assoc_key, count, num_user) + count = _subquery(assoc_key = :jam_tracks_played, num_user) do |subsql| + subsql += " AND tt.playable_type = 'JamRuby::JamTrack' " + end + _put_data_set(assoc_key, count, num_user) sql = _played_online_subquery(' >= 1') count = self.class.cohort_users(self).where("users.id IN (#{sql})").count @@ -261,20 +289,50 @@ SQL self.all_time ? _all_time! : _monthly! end - def self.alltime_cohorts! - Cohort.generate_all_time_cohorts.each do |cc| - cc._all_time! + def calculate_totals(cohorts) + self.group_start = self.group_end = TOTAL_COHORT_DATE + labels = all_time ? Cohort::ALLTIME_LABELS : Cohort::MONTHLY_LABELS + + self.data_set = labels.inject({}) do |hh, (kk,vv)| + hh[kk.to_s] = hh["#{kk}%"] = 0 + hh end + + labels = labels.keys.map(&:to_s) + cohorts.each do |cc| + labels.each do |key| + self.data_set[key] += cc.data_set[key].to_i + end + end + + user_total = self.data_set['registered_users'].to_f + labels.delete('registered_users') + labels.each do |key| + xx = (self.data_set[key].to_f / user_total) + self.data_set["#{key}%"] = 100.0 * xx.round(4) + end + + self.save! + cohorts << self end - def self.monthly_cohorts(monthly_start, monthly_end) - self.generate_monthly_cohorts(monthly_start, monthly_end).compact.each do |cc| + def self.alltime_cohorts! + cohorts = Cohort.generate_all_time_cohorts.each do |cc| + cc._all_time! + end + Cohort.new(all_time: true).calculate_totals(cohorts) + end + + def self.monthly_cohorts!(monthly_start, monthly_end=nil) + monthly_end ||= monthly_start + 1.month - 1.second + cohorts = self.generate_monthly_cohorts(monthly_start, monthly_end).compact.each do |cc| cc._monthly! end + Cohort.new(all_time: false, monthly_start: monthly_start).calculate_totals(cohorts) end def group_start_str - self.group_start.strftime('%Y-%m') + is_total_cohort? ? 'Total' : self.group_start.strftime('%Y-%m') end def group_end_str @@ -296,10 +354,8 @@ SQL end end - def self.monthly_ends - self.cohort_group_ranges.collect do |rr| - rr.last.to_s - end + def is_total_cohort? + self.group_start == TOTAL_COHORT_DATE end end