cohort reportin
This commit is contained in:
parent
9db218d518
commit
17322eab1d
|
|
@ -43,6 +43,7 @@ gem 'jquery-rails' # , '2.3.0' # pinned because jquery-ui-rails was split from j
|
|||
gem 'jquery-ui-rails', '4.2.1'
|
||||
gem 'rails3-jquery-autocomplete'
|
||||
gem 'activeadmin' #, github: 'activeadmin', branch: '0-6-stable'
|
||||
#gem 'activeadmin', github: 'activeadmin
|
||||
gem 'mime-types', '1.25'
|
||||
gem 'meta_search'
|
||||
gem 'fog'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
ActiveAdmin.register_page "CampaignSpend" do
|
||||
menu :parent => 'JamClass'
|
||||
|
||||
page_action :create_spend, :method => :post do
|
||||
|
||||
campaign = params[:jam_ruby_campaign_spend][:campaign]
|
||||
year = params[:jam_ruby_campaign_spend][:year]
|
||||
month = params[:jam_ruby_campaign_spend][:month]
|
||||
spend = params[:jam_ruby_campaign_spend][:spend]
|
||||
|
||||
if campaign.blank?
|
||||
redirect_to admin_campaignspend_path, :notice => "No campaign defined! Nothing done."
|
||||
return
|
||||
elsif spend.blank?
|
||||
redirect_to admin_campaignspend_path, :notice => "No spend defined! Nothing done."
|
||||
return
|
||||
elsif year.blank? || month.blank?
|
||||
spend = spend.to_f
|
||||
# get all cohorts for a given campaign
|
||||
campaign_cohorts = JamClassReport.where(campaign: campaign).where("cohort IS NOT NULL")
|
||||
year_months = []
|
||||
campaign_cohorts.each do |cohort|
|
||||
year_month = {year: cohort.cohort.year, month: cohort.cohort.month}
|
||||
year_months << year_month
|
||||
end
|
||||
|
||||
if campaign_cohorts.length > 0
|
||||
per_month = spend / campaign_cohorts.length
|
||||
year_months.each do |year_month|
|
||||
campaign_spend = CampaignSpend.where(campaign: campaign).where(year: year_month[:year]).where(month: year_month[:month]).first
|
||||
|
||||
if campaign_spend.nil?
|
||||
campaign_spend = CampaignSpend.new
|
||||
end
|
||||
campaign_spend.campaign = campaign
|
||||
campaign_spend.month = year_month[:month]
|
||||
campaign_spend.year = year_month[:year]
|
||||
campaign_spend.spend = per_month
|
||||
|
||||
campaign_spend.save!
|
||||
|
||||
end
|
||||
else
|
||||
redirect_to admin_campaignspend_path, :notice => "No data found for campaign: #{campaign}"
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
redirect_to admin_campaignspend_path, :notice => "Campaign #{campaign} updated with a per month value of $#{per_month} (#{year_months.length} months worth of data found)"
|
||||
else
|
||||
campaign_spend = CampaignSpend.where(campaign: campaign).where(year: year).where(month: month).first
|
||||
|
||||
if campaign_spend.nil?
|
||||
campaign_spend = CampaignSpend.new
|
||||
end
|
||||
campaign_spend.campaign = campaign
|
||||
campaign_spend.month = month
|
||||
campaign_spend.year = year
|
||||
campaign_spend.spend = spend
|
||||
|
||||
campaign_spend.save!
|
||||
|
||||
redirect_to admin_campaignspend_path, :notice => "Campaign spend updated: #{campaign}:#{year}-#{month} = $#{spend}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
content do
|
||||
|
||||
para do
|
||||
link_to "JamClass Report", admin_jamclassreports_path
|
||||
end
|
||||
para do
|
||||
semantic_form_for CampaignSpend.new, :url => admin_campaignspend_create_spend_path, :builder => ActiveAdmin::FormBuilder do |f|
|
||||
f.inputs "Campaign Spend" do
|
||||
f.input :spend, :required => true, hint: "If you leave year or month blank, the system will divide up the specified spend amount here across all months seen for this campaign."
|
||||
f.input :campaign, :as => :select, hint: "If this appears empty or incomplete, visit the JamClass Report page (link above) and come back.", :required => true, :collection => JamClassReport.select('campaign').group('campaign').map(&:campaign)
|
||||
f.input :year, :as => :select, :hint => "Year of campaign spend (optional)", :collection => [Date.today.year, Date.today.year - 1]
|
||||
f.input :month, :as => :select, :hint => "Month of campaign (optional)", :collection => (1..12).map { |m| [Date::MONTHNAMES[m], m] }
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
ActiveAdmin.register_page "JamClassReports", as: "JamClass Cohort Report" do
|
||||
menu :parent => 'JamClass'
|
||||
|
||||
content :title => "JamClass Report" do
|
||||
para do
|
||||
link_to "Campaign Spend", admin_campaignspend_path
|
||||
end
|
||||
para do
|
||||
table_for JamClassReport.analyse do
|
||||
|
||||
column "Campaign" do |r|
|
||||
if r.campaign.nil?
|
||||
"N/A"
|
||||
else
|
||||
r.campaign
|
||||
end
|
||||
end
|
||||
column "Cohort" do |r|
|
||||
if r.cohort.nil?
|
||||
"Total"
|
||||
else
|
||||
"#{Date::ABBR_MONTHNAMES[r.cohort.month]} #{r.cohort.year}"
|
||||
end
|
||||
end
|
||||
column "Spend" do |r|
|
||||
if r.spend.nil?
|
||||
"N/A"
|
||||
else
|
||||
r.spend
|
||||
end
|
||||
end
|
||||
column "Registrations", :registrations
|
||||
column "TD Customers", :td_customers
|
||||
column "JamClass Revenues", :jamclass_rev
|
||||
column "TD4", :td4
|
||||
column "TD2", :td2
|
||||
column "TD1", :td1
|
||||
column "Spend/TD" do |r|
|
||||
if r.spend_td.nil?
|
||||
"N/A"
|
||||
else
|
||||
r.spend_td
|
||||
end
|
||||
end
|
||||
column "% 0 BC" do |r|
|
||||
(r.purchases0 * 100).round
|
||||
end
|
||||
column "% 1 BC" do |r|
|
||||
(r.purchases1 * 100).round
|
||||
end
|
||||
column "% 2 BC" do |r|
|
||||
(r.purchases2 * 100).round
|
||||
end
|
||||
column "% 3 BC" do |r|
|
||||
(r.purchases3 * 100).round
|
||||
end
|
||||
column "% 4+ BC" do |r|
|
||||
(r.purchases_rest * 100).round
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -358,3 +358,4 @@ audio_in_music_notations.sql
|
|||
lesson_time_tracking.sql
|
||||
packaged_test_drive.sql
|
||||
packaged_test_drive2.sql
|
||||
jamclass_report.sql
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
CREATE TABLE campaign_spends (
|
||||
id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
|
||||
campaign VARCHAR NOT NULL,
|
||||
spend NUMERIC(8,2) NOT NULL,
|
||||
month INTEGER NOT NULL,
|
||||
year INTEGER NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE jam_class_reports (
|
||||
cohort DATE,
|
||||
campaign VARCHAR,
|
||||
spend NUMERIC (8,2),
|
||||
registrations INTEGER,
|
||||
td_customers INTEGER,
|
||||
jamclass_rev NUMERIC (8,2),
|
||||
td4 INTEGER,
|
||||
td2 INTEGER,
|
||||
td1 INTEGER,
|
||||
spend_td NUMERIC (8,2),
|
||||
purchases0 NUMERIC (8,2),
|
||||
purchases1 NUMERIC (8,2),
|
||||
purchases2 NUMERIC (8,2),
|
||||
purchases3 NUMERIC (8,2),
|
||||
purchases_rest NUMERIC (8,2),
|
||||
purchases0_count INTEGER,
|
||||
purchases1_count INTEGER,
|
||||
purchases2_count INTEGER,
|
||||
purchases3_count INTEGER,
|
||||
purchases_rest_count INTEGER,
|
||||
purchases_count INTEGER
|
||||
);
|
||||
|
|
@ -303,6 +303,8 @@ require "jam_ruby/models/teacher_instrument"
|
|||
require "jam_ruby/models/teacher_subject"
|
||||
require "jam_ruby/models/teacher_language"
|
||||
require "jam_ruby/models/teacher_genre"
|
||||
require "jam_ruby/models/jam_class_report"
|
||||
require "jam_ruby/models/campaign_spend"
|
||||
include Jampb
|
||||
|
||||
module JamRuby
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
module JamRuby
|
||||
class CampaignSpend < ActiveRecord::Base
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
# CREATE FUNCTION jamclass_report RETURNS TABLE (campaign VARCHAR, spend numeric(8,2), registrations INTEGER, td_customers INTEGER, jamclass_rev NUMERIC(8,2), td4 INTEGER, td2 INTEGER, td1 INTEGER, spend_td NUMERIC(8,2), purchases_0 INTEGER, purchases_1 INTEGER, purchases_2 INTEGER, purchases_3 INTEGER, purchases_rest INTEGER) VOLATILE AS $$
|
||||
|
||||
module JamRuby
|
||||
class JamClassReport < ActiveRecord::Base
|
||||
|
||||
def self.update_spend
|
||||
|
||||
end
|
||||
def self.analyse(campaign_filter = nil)
|
||||
|
||||
User.transaction do
|
||||
user_purchase = "CREATE TEMPORARY TABLE user_jamclass_purchases (user_id VARCHAR(64) NOT NULL, purchases INTEGER DEFAULT 0) ON COMMIT DROP"
|
||||
user_inserts = "INSERT INTO user_jamclass_purchases (user_id, purchases) (SELECT id, COUNT(user_jamclass_purchases.user_id) FROM users LEFT OUTER JOIN user_jamclass_purchases ON users.id = user_jamclass_purchases.user_id GROUP BY users.id)"
|
||||
User.connection.execute(user_purchase)
|
||||
User.connection.execute(user_inserts)
|
||||
|
||||
jamclass_revenue = "(SELECT SUM(price) * 0.25 FROM lesson_package_purchases WHERE lesson_package_purchases.lesson_package_type_id = 'single') + (SELECT SUM(6) FROM lesson_package_purchases WHERE lesson_package_purchases.lesson_package_type_id = 'test-drive-1') + (SELECT SUM(10) FROM lesson_package_purchases WHERE lesson_package_purchases.lesson_package_type_id = 'test-drive-2') + (SELECT SUM(10) FROM lesson_package_purchases WHERE lesson_package_purchases.lesson_package_type_id = 'test-drive')"
|
||||
td_users = "COUNT(td_purchases.id)"
|
||||
td4 = "COUNT(td4_purchases.id)"
|
||||
td2 = "COUNT(td2_purchases.id)"
|
||||
td1 = "COUNT(td1_purchases.id)"
|
||||
spend_td = "SELECT (CASE WHEN COUNT(td_purchases.id) = 0 THEN NULL ELSE avg(campaign_spends.spend) / COUNT(td_purchases.id) END)"
|
||||
purchases0 = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 0 THEN 1 ELSE NULL END) / COUNT(user_jamclass_purchases.purchases)"
|
||||
purchases1 = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 1 THEN 1 ELSE NULL END) / COUNT(user_jamclass_purchases.purchases)"
|
||||
purchases2 = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 2 THEN 1 ELSE NULL END) / COUNT(user_jamclass_purchases.purchases)"
|
||||
purchases3 = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 3 THEN 1 ELSE NULL END) / COUNT(user_jamclass_purchases.purchases)"
|
||||
purchases_rest = "COUNT(CASE WHEN user_jamclass_purchases.purchases >= 3 THEN 1 ELSE NULL END) / COUNT(user_jamclass_purchases.purchases)"
|
||||
purchases0_count = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 0 THEN 1 ELSE NULL END)"
|
||||
purchases1_count = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 1 THEN 1 ELSE NULL END)"
|
||||
purchases2_count = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 2 THEN 1 ELSE NULL END)"
|
||||
purchases3_count = "COUNT(CASE WHEN user_jamclass_purchases.purchases = 3 THEN 1 ELSE NULL END)"
|
||||
purchases_rest_count = "COUNT(CASE WHEN user_jamclass_purchases.purchases >= 3 THEN 1 ELSE NULL END)"
|
||||
purchases_count = "COUNT(user_jamclass_purchases.purchases)"
|
||||
query = User.select("date_trunc( 'month', users.created_at ) as cohort, origin_utm_campaign AS campaign, avg(campaign_spends.spend) as spend, count(users.id) AS registrations, (#{td_users}) as td_customers, (#{jamclass_revenue}) as jamclass_rev, (#{td4}) AS td4, (#{td2}) AS td2, (#{td1}) AS td1, (#{spend_td}) as spend_td, (#{purchases0}) as purchases0, (#{purchases1}) as purchases1, (#{purchases2}) as purchases2, (#{purchases3}) as purchases3, (#{purchases_rest}) as purchases_rest, (#{purchases0_count}) as purchases0_count, (#{purchases1_count}) as purchases1_count, (#{purchases2_count}) as purchases2_count, (#{purchases3_count}) as purchases3_count, (#{purchases_rest_count}) as purchases_rest_count, (#{purchases_count}) as purchases_count")
|
||||
.joins(%Q{
|
||||
LEFT OUTER JOIN
|
||||
campaign_spends
|
||||
ON
|
||||
campaign_spends.month = date_part('month', users.created_at) AND year = date_part('year', users.created_at) AND campaign_spends.campaign = users.origin_utm_campaign
|
||||
})
|
||||
.joins(%Q{
|
||||
LEFT OUTER JOIN
|
||||
lesson_package_purchases
|
||||
ON
|
||||
lesson_package_purchases.user_id = users.id
|
||||
})
|
||||
.joins(%Q{
|
||||
LEFT OUTER JOIN
|
||||
lesson_package_purchases AS td4_purchases
|
||||
ON
|
||||
lesson_package_purchases.user_id = users.id AND lesson_package_purchases.id = 'test-drive'
|
||||
})
|
||||
.joins(%Q{
|
||||
LEFT OUTER JOIN
|
||||
lesson_package_purchases AS td2_purchases
|
||||
ON
|
||||
lesson_package_purchases.user_id = users.id AND lesson_package_purchases.id = 'test-drive-2'
|
||||
})
|
||||
.joins(%Q{
|
||||
LEFT OUTER JOIN
|
||||
lesson_package_purchases AS td1_purchases
|
||||
ON
|
||||
lesson_package_purchases.user_id = users.id AND lesson_package_purchases.id = 'test-drive-1'
|
||||
})
|
||||
.joins(%Q{
|
||||
LEFT OUTER JOIN
|
||||
lesson_package_purchases AS td_purchases
|
||||
ON
|
||||
lesson_package_purchases.user_id = users.id AND lesson_package_purchases.id in ('test-drive', 'test-drive-2', 'test-drive-1')
|
||||
})
|
||||
.joins(%Q{
|
||||
INNER JOIN
|
||||
user_jamclass_purchases AS user_jamclass_purchases
|
||||
ON
|
||||
user_jamclass_purchases.user_id = users.id
|
||||
})
|
||||
.group('users.origin_utm_campaign, cohort')
|
||||
|
||||
|
||||
user_inserts = "INSERT INTO jam_class_reports (cohort, campaign, spend, registrations, td_customers, jamclass_rev, td4, td2, td1, spend_td, purchases0, purchases1, purchases2, purchases3, purchases_rest, purchases0_count, purchases1_count, purchases2_count, purchases3_count, purchases_rest_count, purchases_count) (#{query.to_sql})"
|
||||
User.connection.execute("DELETE FROM jam_class_reports")
|
||||
User.connection.execute(user_inserts)
|
||||
purchases0 = "SUM(jam_class_reports.purchases0_count) / SUM(jam_class_reports.purchases_count)"
|
||||
purchases1 = "SUM(jam_class_reports.purchases1_count) / SUM(jam_class_reports.purchases_count)"
|
||||
purchases2 = "SUM(jam_class_reports.purchases2_count) / SUM(jam_class_reports.purchases_count)"
|
||||
purchases3 = "SUM(jam_class_reports.purchases3_count) / SUM(jam_class_reports.purchases_count)"
|
||||
purchases_rest = "SUM(jam_class_reports.purchases_rest_count) / SUM(jam_class_reports.purchases_count)"
|
||||
|
||||
group_inserts = "INSERT INTO jam_class_reports (cohort, campaign, spend, registrations, td_customers, jamclass_rev, td4, td2, td1, spend_td, purchases0, purchases1, purchases2, purchases3, purchases_rest)
|
||||
(SELECT NULL, jam_class_reports.campaign, SUM(spend), SUM(registrations), SUM(td_customers), SUM(jamclass_rev), SUM(td4), SUM(td2), SUM(td1), CASE WHEN SUM(td4) + SUM (td2) + SUM(td1) = 0 THEN NULL ELSE (SUM(spend) / (SUM(td4) + SUM (td2) + SUM(td1))) END,
|
||||
#{purchases0}, #{purchases1}, #{purchases2}, #{purchases3}, #{purchases_rest} FROM jam_class_reports
|
||||
GROUP BY campaign)"
|
||||
User.connection.execute(group_inserts)
|
||||
reports = JamClassReport.order('campaign, cohort DESC NULLS LAST')
|
||||
if campaign_filter
|
||||
reports = reports.where(campaign: campaign_filter)
|
||||
end
|
||||
|
||||
reports
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe JamClassReport do
|
||||
|
||||
it "wee bit of data" do
|
||||
user = FactoryGirl.create(:user, origin_utm_campaign: 'legacy')
|
||||
|
||||
query = JamClassReport.analyse
|
||||
query.length.should eql 2
|
||||
r1 = query[0]
|
||||
|
||||
r1.cohort.should eql Date.new(user.created_at.year, user.created_at.month, 1)
|
||||
r1.registrations.should eql 1
|
||||
r1.campaign.should eql 'legacy'
|
||||
r1.spend.should be_nil
|
||||
r1.registrations.should eql 1
|
||||
r1.td_customers.should eql 0
|
||||
r1.jamclass_rev.should be_nil
|
||||
r1.td4.should eql 0
|
||||
r1.td2.should eql 0
|
||||
r1.td1.should eql 0
|
||||
r1.spend_td.should be_nil
|
||||
r1.purchases0.should eql 1
|
||||
r1.purchases1.should eql 0
|
||||
r1.purchases2.should eql 0
|
||||
r1.purchases3.should eql 0
|
||||
r1.purchases_rest.should eql 0
|
||||
|
||||
r2 = query[1]
|
||||
r2.cohort.should be_nil
|
||||
r2.registrations.should eql 1
|
||||
r2.campaign.should eql 'legacy'
|
||||
r2.spend.should be_nil
|
||||
r2.registrations.should eql 1
|
||||
r2.td_customers.should eql 0
|
||||
r2.jamclass_rev.should be_nil
|
||||
r2.td4.should eql 0
|
||||
r2.td2.should eql 0
|
||||
r2.td1.should eql 0
|
||||
r2.spend_td.should be_nil
|
||||
r2.purchases0.should eql 1
|
||||
r2.purchases1.should eql 0
|
||||
r2.purchases2.should eql 0
|
||||
r2.purchases3.should eql 0
|
||||
r2.purchases_rest.should eql 0
|
||||
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue