Merged in VRFS-5128_report_for_ad_campaigns (pull request #14)
VRFS-5128 report for ad campaigns * Ad campaign report add new admin report for monitor and analyze the results from a variety of paid campaigns * reload report after updating values using best_in_place * add users first_subscribed_plan_code use this new column in ad campaigns report to filter by subscription plan * set first_subscribed_plan_code in recurly subscription creation * set correct value for first_subscribed_plan_code
This commit is contained in:
parent
253d82a32c
commit
c2fe4ffb98
|
|
@ -0,0 +1,154 @@
|
||||||
|
module AdCampaignsHelper
|
||||||
|
def self.spacer(val, total)
|
||||||
|
percentage = ((val * 100) / total.to_f).round(1).to_s
|
||||||
|
('%-5.5s' % percentage).gsub(' ', ' ') + '% - ' + val.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.cac(campaign)
|
||||||
|
if campaign.subscribed && campaign.subscribed > 0
|
||||||
|
(campaign.spend/campaign.subscribed.to_f).round(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.cac_divide_by_ltv(campaign)
|
||||||
|
customer_ltv = GenericState.singleton.customer_ltv
|
||||||
|
if cac(campaign) && customer_ltv && customer_ltv > 0
|
||||||
|
return (cac(campaign)/customer_ltv.to_f).round(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.format_number(num)
|
||||||
|
if num
|
||||||
|
num.to_s.reverse.scan(/\d{3}|.+/).join(",").reverse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
ActiveAdmin.register JamRuby::AdCampaign, as: 'AdCampaign' do
|
||||||
|
menu :label => 'Ad Campaigns', :parent => 'Reports'
|
||||||
|
before_filter :skip_sidebar!, :only => :index
|
||||||
|
config.batch_actions = false
|
||||||
|
|
||||||
|
index do
|
||||||
|
div do
|
||||||
|
render 'customer_ltv'
|
||||||
|
end
|
||||||
|
|
||||||
|
column "Campaign" do |campaign|
|
||||||
|
campaign.origin_utm_campaign
|
||||||
|
end
|
||||||
|
column "Medium" do |campaign|
|
||||||
|
campaign.origin_utm_medium
|
||||||
|
end
|
||||||
|
column "End Date" do |campaign|
|
||||||
|
best_in_place campaign, :end_date, as: :date, url: inplace_update_admin_ad_campaigns_path(campaign: campaign.origin_utm_campaign, medium: campaign.origin_utm_medium), param: 'ad_campaign', classes: 'ac_bip'
|
||||||
|
end
|
||||||
|
column "Hard Date" do |campaign|
|
||||||
|
(campaign.end_date + 45.days).strftime('%Y-%m-%d') if campaign.end_date.present?
|
||||||
|
end
|
||||||
|
column "Subscribed" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.subscribed, campaign.joined))
|
||||||
|
end
|
||||||
|
column "Spend" do |campaign|
|
||||||
|
best_in_place campaign, :spend, as: :input, url: inplace_update_admin_ad_campaigns_path(campaign: campaign.origin_utm_campaign, medium: campaign.origin_utm_medium), param: 'ad_campaign', display_with: Proc.new{|spend| number_to_currency(spend) }, classes: 'ac_bip'
|
||||||
|
end
|
||||||
|
column "CAC" do |campaign|
|
||||||
|
number_to_currency(AdCampaignsHelper.cac(campaign)) if AdCampaignsHelper.cac(campaign) && AdCampaignsHelper.cac(campaign) > 0
|
||||||
|
end
|
||||||
|
column "LTV/CAC" do |campaign|
|
||||||
|
AdCampaignsHelper.cac_divide_by_ltv(campaign)
|
||||||
|
end
|
||||||
|
column "Referred" do |campaign|
|
||||||
|
best_in_place campaign, :referred, as: :input, url: inplace_update_admin_ad_campaigns_path(campaign: campaign.origin_utm_campaign, medium: campaign.origin_utm_medium), param: 'ad_campaign', display_with: Proc.new{|referred| AdCampaignsHelper.format_number(referred) }, classes: 'ac_bip'
|
||||||
|
end
|
||||||
|
column "Signed Up" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.joined, campaign.referred)) if campaign.referred && campaign.referred > 0
|
||||||
|
end
|
||||||
|
column "Downloaded" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.downloaded, campaign.joined))
|
||||||
|
end
|
||||||
|
column "Ran Client" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.ran_client, campaign.joined))
|
||||||
|
end
|
||||||
|
column "FTUE" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.ftue, campaign.joined))
|
||||||
|
end
|
||||||
|
column "Any Session" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.any_session, campaign.joined))
|
||||||
|
end
|
||||||
|
column "2+ Session" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.real_session, campaign.joined))
|
||||||
|
end
|
||||||
|
column "Good Session" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.good_session, campaign.joined))
|
||||||
|
end
|
||||||
|
column "Invited" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.invited, campaign.joined))
|
||||||
|
end
|
||||||
|
column "Friended" do |campaign|
|
||||||
|
raw(AdCampaignsHelper.spacer(campaign.friended, campaign.joined))
|
||||||
|
end
|
||||||
|
column "Platinum" do |campaign|
|
||||||
|
campaign.platinum
|
||||||
|
end
|
||||||
|
column "Gold" do |campaign|
|
||||||
|
campaign.gold
|
||||||
|
end
|
||||||
|
column "Silver" do |campaign|
|
||||||
|
campaign.silver
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
controller do
|
||||||
|
def scoped_collection
|
||||||
|
User.select("users.origin_utm_campaign,
|
||||||
|
users.origin_utm_medium, COUNT(users.id) AS joined,
|
||||||
|
COUNT(users.first_downloaded_client_at) AS downloaded,
|
||||||
|
COUNT(users.first_subscribed_at) AS subscribed,
|
||||||
|
COUNT(users.first_ran_client_at) AS ran_client,
|
||||||
|
COUNT(users.first_certified_gear_at) AS ftue,
|
||||||
|
COUNT(users.first_music_session_at) AS any_session,
|
||||||
|
COUNT(users.first_real_music_session_at) AS real_session,
|
||||||
|
COUNT(users.first_good_music_session_at) AS good_session,
|
||||||
|
COUNT(users.first_invited_at) AS invited,
|
||||||
|
COUNT(users.first_friended_at) AS friended,
|
||||||
|
COUNT(CASE WHEN users.first_subscribed_plan_code = 'jamsubplatinum' OR users.first_subscribed_plan_code = 'jamsubplatinumyearly' THEN users.first_subscribed_plan_code END) AS platinum,
|
||||||
|
COUNT(CASE WHEN users.first_subscribed_plan_code = 'jamsubgold' OR users.first_subscribed_plan_code = 'jamsubgoldyearly' THEN users.first_subscribed_plan_code END) AS gold,
|
||||||
|
COUNT(CASE WHEN users.first_subscribed_plan_code = 'jamsubsilver' OR users.first_subscribed_plan_code = 'jamsubsilveryearly' THEN users.first_subscribed_plan_code END) AS silver,
|
||||||
|
COUNT(CASE WHEN users.subscription_plan_code = 'jamsubgold' THEN users.subscription_plan_code END) AS gold,
|
||||||
|
COUNT(CASE WHEN users.subscription_plan_code = 'jamsubsilver' THEN users.subscription_plan_code END) AS silver,
|
||||||
|
ad_campaigns.id,
|
||||||
|
COALESCE(MAX(ad_campaigns.referred), NULL) as referred,
|
||||||
|
COALESCE(MAX(ad_campaigns.end_date), NULL) AS end_date,
|
||||||
|
COALESCE(MAX(ad_campaigns.spend), 0) AS spend").joins("
|
||||||
|
LEFT JOIN ad_campaigns ON users.origin_utm_campaign = ad_campaigns.campaign
|
||||||
|
AND users.origin_utm_medium = ad_campaigns.medium").where("
|
||||||
|
users.origin_utm_campaign IS NOT NULL AND users.origin_utm_medium IS NOT NULL").group("
|
||||||
|
ad_campaigns.id, users.origin_utm_campaign, users.origin_utm_medium").order("
|
||||||
|
users.origin_utm_campaign DESC")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def permitted_params
|
||||||
|
params.permit :campaign, :medium, :_method, ad_campaign: [:spend, :referred, :end_date]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
collection_action :inplace_update, method: :put do
|
||||||
|
campaign = permitted_params[:campaign]
|
||||||
|
medium = permitted_params[:medium]
|
||||||
|
@ad_campaign = JamRuby::AdCampaign.where(campaign: campaign, medium: medium).first_or_create
|
||||||
|
respond_to do |format|
|
||||||
|
if @ad_campaign.update_attributes(permitted_params[:ad_campaign])
|
||||||
|
format.json { head :ok }
|
||||||
|
else
|
||||||
|
format.json{ render :json => @ad_campaign.errors.full_messages, :status => :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# module AdCampaignsHelper
|
||||||
|
# def campaign_brought_in_users(campaign, medium)
|
||||||
|
# User.where(origin_utm_campaign: campaign, origin_utm_medium: medium)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# ActiveAdmin.register JamRuby::AdCampaign do
|
||||||
|
# permit_params :campaign, :medium, :spend
|
||||||
|
# end
|
||||||
|
|
||||||
|
# ActiveAdmin.register_page "Ad campaigns" do
|
||||||
|
# menu parent: 'Reports'
|
||||||
|
# content :title => "Paid Advertising Report" do
|
||||||
|
# table_for User.select("users.origin_utm_campaign, users.origin_utm_medium, COALESCE(MAX(ad_campaigns.end_date), NULL) AS end_date, COALESCE(MAX(ad_campaigns.spend), NULL) AS spend").joins("LEFT JOIN ad_campaigns ON users.origin_utm_campaign = ad_campaigns.campaign AND users.origin_utm_medium = ad_campaigns.medium").group("ad_campaigns.id, users.origin_utm_campaign, users.origin_utm_medium") do
|
||||||
|
# column "Campaign" do |campaign|
|
||||||
|
# campaign.origin_utm_campaign
|
||||||
|
# end
|
||||||
|
# column "Medium" do |campaign|
|
||||||
|
# campaign.origin_utm_medium
|
||||||
|
# end
|
||||||
|
# column "End Date" do |campaign|
|
||||||
|
# campaign.end_date
|
||||||
|
# end
|
||||||
|
# column "Hard Date" do |campaign|
|
||||||
|
# campaign.end_date + 45.days if campaign.end_date.present?
|
||||||
|
# end
|
||||||
|
# column "Subscribed" do |campaign|
|
||||||
|
# end
|
||||||
|
# column "Spend" do |campaign|
|
||||||
|
# best_in_place campaign, :spend, as: :input, url: admin_ad_campaigns_update_path(campaign: campaign.origin_utm_campaign, medium: campaign.origin_utm_medium), param: 'ad_campaign'
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# page_action :update, method: :put do
|
||||||
|
# campaign = params[:campaign]
|
||||||
|
# medium = params[:medium]
|
||||||
|
# ad_campaign = AdCampaign.where(campaign: campaign, medium: medium).first_or_initialize
|
||||||
|
# ad_campaign.attributes = params["ad_campaign"]
|
||||||
|
# ad_campaign.save!
|
||||||
|
# respond_with_bip(ad_campaign)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
@ -3,7 +3,7 @@ ActiveAdmin.register JamRuby::GenericState, :as => 'GenericState' do
|
||||||
|
|
||||||
config.clear_action_items!
|
config.clear_action_items!
|
||||||
filter :env
|
filter :env
|
||||||
permit_params :top_message, :event_page_top_logo_url, :connection_policy
|
permit_params :top_message, :event_page_top_logo_url, :customer_ltv, :connection_policy
|
||||||
|
|
||||||
actions :all, :except => [:destroy]
|
actions :all, :except => [:destroy]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,11 @@
|
||||||
// //= require autocomplete-rails
|
// //= require autocomplete-rails
|
||||||
//= require base
|
//= require base
|
||||||
//= require_tree .
|
//= require_tree .
|
||||||
|
//= require best_in_place.jquery-ui
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
jQuery(".best_in_place").best_in_place()
|
jQuery(".best_in_place").best_in_place();
|
||||||
|
$.datepicker.setDefaults({
|
||||||
|
dateFormat: 'yy-mm-dd',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
$(document).ready(function() {
|
||||||
|
jQuery(".ac_bip").bind("ajax:success", function(){ window.location.reload(); });
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<%= label_tag :customer_ltv, 'Customer LTV : $' %>
|
||||||
|
<%= best_in_place GenericState.singleton, :customer_ltv, :as => :input, url: "/admin/generic_states/#{GenericState.singleton.id}", place_holder: "---", :ok_button => 'Save', :cancel_button => 'Cancel', classes: 'ac_bip' %>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
class CreateAdCampaigns < ActiveRecord::Migration
|
||||||
|
|
||||||
|
def self.up
|
||||||
|
execute( <<-SQL
|
||||||
|
CREATE TABLE public.ad_campaigns (
|
||||||
|
id character varying(64) DEFAULT public.uuid_generate_v4() NOT NULL,
|
||||||
|
campaign character varying(256),
|
||||||
|
medium character varying(128),
|
||||||
|
spend integer default 0,
|
||||||
|
end_date date,
|
||||||
|
cac NUMERIC (8,2),
|
||||||
|
referred integer,
|
||||||
|
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
expired_at timestamp without time zone);
|
||||||
|
SQL
|
||||||
|
)
|
||||||
|
execute("CREATE INDEX index_ad_campaigns_campaign_medium ON public.ad_campaigns USING btree (campaign, medium);")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
execute("DROP INDEX index_ad_campaigns_campaign_medium")
|
||||||
|
execute("DROP TABLE public.ad_campaigns")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
class AddLtvToGenericState < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
execute "ALTER TABLE generic_state ADD COLUMN customer_ltv INTEGER"
|
||||||
|
end
|
||||||
|
def self.down
|
||||||
|
execute "ALTER TABLE generic_state DROP COLUMN customer_ltv"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AddIndexUsersCampaignMedium < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
execute("CREATE INDEX index_users_origin_utm_campaign_origin_utm_medium ON public.users USING btree (origin_utm_campaign, origin_utm_medium);")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
execute("DROP INDEX index_users_origin_utm_campaign_origin_utm_medium;")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AddFirstSubscribedPlanCodeToUsers < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
execute("ALTER TABLE users ADD COLUMN first_subscribed_plan_code VARCHAR(100);")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
execute("ALTER TABLE users DROP COLUMN first_subscribed_plan_code;")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -337,6 +337,7 @@ require "jam_ruby/models/mobile_recording"
|
||||||
require "jam_ruby/app/uploaders/mobile_recording_uploader"
|
require "jam_ruby/app/uploaders/mobile_recording_uploader"
|
||||||
require "jam_ruby/models/mobile_recording_upload"
|
require "jam_ruby/models/mobile_recording_upload"
|
||||||
require "jam_ruby/models/temp_token"
|
require "jam_ruby/models/temp_token"
|
||||||
|
require "jam_ruby/models/ad_campaign"
|
||||||
|
|
||||||
|
|
||||||
include Jampb
|
include Jampb
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
module JamRuby
|
||||||
|
class AdCampaign < ActiveRecord::Base
|
||||||
|
self.primary_key = 'id'
|
||||||
|
self.table_name = 'ad_campaigns'
|
||||||
|
|
||||||
|
attr_accessible :campaign, :medium, :spend, :end_date, :referred
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -3,7 +3,7 @@ module JamRuby
|
||||||
class GenericState < ActiveRecord::Base
|
class GenericState < ActiveRecord::Base
|
||||||
|
|
||||||
|
|
||||||
attr_accessible :top_message, :event_page_top_logo_url, :connection_policy, as: :admin
|
attr_accessible :top_message, :event_page_top_logo_url, :connection_policy, :customer_ltv, as: :admin
|
||||||
|
|
||||||
self.table_name = 'generic_state'
|
self.table_name = 'generic_state'
|
||||||
|
|
||||||
|
|
@ -47,6 +47,10 @@ module JamRuby
|
||||||
GenericState.singleton.recurly_transactions_last_sync_at
|
GenericState.singleton.recurly_transactions_last_sync_at
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.customer_ltv
|
||||||
|
GenericState.singleton.customer_ltv
|
||||||
|
end
|
||||||
|
|
||||||
def self.connection_policy
|
def self.connection_policy
|
||||||
GenericState.connection_policy
|
GenericState.connection_policy
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,7 @@ module JamRuby
|
||||||
subscription = create_subscription(current_user, plan_code, account, current_user.subscription_trial_ended? ? nil : current_user.subscription_trial_ends_at)
|
subscription = create_subscription(current_user, plan_code, account, current_user.subscription_trial_ended? ? nil : current_user.subscription_trial_ends_at)
|
||||||
current_user.recurly_subscription_id = subscription.uuid
|
current_user.recurly_subscription_id = subscription.uuid
|
||||||
current_user.first_subscribed_at = Time.now if current_user.first_subscribed_at.nil?
|
current_user.first_subscribed_at = Time.now if current_user.first_subscribed_at.nil?
|
||||||
|
current_user.first_subscribed_plan_code = plan_code if current_user.first_subscribed_plan_code.nil?
|
||||||
if current_user.subscription_trial_ended?
|
if current_user.subscription_trial_ended?
|
||||||
current_user.subscription_plan_code = get_highest_plan(subscription)
|
current_user.subscription_plan_code = get_highest_plan(subscription)
|
||||||
current_user.subscription_plan_code_set_at = DateTime.now
|
current_user.subscription_plan_code_set_at = DateTime.now
|
||||||
|
|
@ -464,6 +465,7 @@ module JamRuby
|
||||||
if subscription && user.recurly_subscription_id.nil?
|
if subscription && user.recurly_subscription_id.nil?
|
||||||
puts "Repairing subscription ID on account"
|
puts "Repairing subscription ID on account"
|
||||||
user.update_attribute(:recurly_subscription_id, subscription.uuid)
|
user.update_attribute(:recurly_subscription_id, subscription.uuid)
|
||||||
|
user.update_attribute(:first_subscribed_plan_code, subscription.plan.plan_code)
|
||||||
user.recurly_subscription_id = subscription.uuid
|
user.recurly_subscription_id = subscription.uuid
|
||||||
user.first_subscribed_at = Time.now if user.first_subscribed_at.nil?
|
user.first_subscribed_at = Time.now if user.first_subscribed_at.nil?
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue