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!
|
||||
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]
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@
|
|||
// //= require autocomplete-rails
|
||||
//= require base
|
||||
//= require_tree .
|
||||
|
||||
//= require best_in_place.jquery-ui
|
||||
|
||||
$(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/models/mobile_recording_upload"
|
||||
require "jam_ruby/models/temp_token"
|
||||
require "jam_ruby/models/ad_campaign"
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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'
|
||||
|
||||
|
|
@ -47,6 +47,10 @@ module JamRuby
|
|||
GenericState.singleton.recurly_transactions_last_sync_at
|
||||
end
|
||||
|
||||
def self.customer_ltv
|
||||
GenericState.singleton.customer_ltv
|
||||
end
|
||||
|
||||
def self.connection_policy
|
||||
GenericState.connection_policy
|
||||
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)
|
||||
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_plan_code = plan_code if current_user.first_subscribed_plan_code.nil?
|
||||
if current_user.subscription_trial_ended?
|
||||
current_user.subscription_plan_code = get_highest_plan(subscription)
|
||||
current_user.subscription_plan_code_set_at = DateTime.now
|
||||
|
|
@ -464,6 +465,7 @@ module JamRuby
|
|||
if subscription && user.recurly_subscription_id.nil?
|
||||
puts "Repairing subscription ID on account"
|
||||
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.first_subscribed_at = Time.now if user.first_subscribed_at.nil?
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue