Add completion % to campaign analytics

This commit is contained in:
Matt Lott 2015-01-05 10:28:56 -08:00
parent 15be292573
commit 61180c640d
4 changed files with 28 additions and 24 deletions

View file

@ -28,5 +28,8 @@
top: 1% top: 1%
padding: 3px 8px padding: 3px 8px
#analytics-modal .modal-content #analytics-modal
background-color: white .modal-dialog
width: 75%
.modal-content
background-color: white

View file

@ -65,6 +65,7 @@ block outer_content
td Finished td Finished
td Dropped td Dropped
td Drop % td Drop %
td Completion %
tbody tbody
- for (var i = 0; i < campaignDropOffs.levels.length; i++) - for (var i = 0; i < campaignDropOffs.levels.length; i++)
tr tr
@ -75,6 +76,7 @@ block outer_content
td= campaignDropOffs.levels[i].finished td= campaignDropOffs.levels[i].finished
td= campaignDropOffs.levels[i].finishDropped td= campaignDropOffs.levels[i].finishDropped
td= campaignDropOffs.levels[i].finishDropRate td= campaignDropOffs.levels[i].finishDropRate
td= campaignDropOffs.levels[i].completionRate
else else
button.btn.btn-default.disabled#analytics-button Analytics Loading... button.btn.btn-default.disabled#analytics-button Analytics Loading...

View file

@ -47,7 +47,7 @@ module.exports = class CampaignEditorView extends RootView
@listenToOnce @levels, 'sync', @onFundamentalLoaded @listenToOnce @levels, 'sync', @onFundamentalLoaded
@listenToOnce @achievements, 'sync', @onFundamentalLoaded @listenToOnce @achievements, 'sync', @onFundamentalLoaded
_.delay @getCampaignDropOffs, 1000 _.delay @getCampaignCompletions, 1000
loadThangTypeNames: -> loadThangTypeNames: ->
# Load the names of the ThangTypes that this level's Treema nodes might want to display. # Load the names of the ThangTypes that this level's Treema nodes might want to display.
@ -240,7 +240,7 @@ module.exports = class CampaignEditorView extends RootView
if achievement.hasLocalChanges() if achievement.hasLocalChanges()
@toSave.add achievement @toSave.add achievement
getCampaignDropOffs: => getCampaignCompletions: =>
# Fetch last 7 days of campaign drop-off rates # Fetch last 7 days of campaign drop-off rates
startDay = new Date() startDay = new Date()
@ -254,14 +254,15 @@ module.exports = class CampaignEditorView extends RootView
mapFn = (item) -> mapFn = (item) ->
item.startDropRate = (item.startDropped / item.started * 100).toFixed(2) item.startDropRate = (item.startDropped / item.started * 100).toFixed(2)
item.finishDropRate = (item.finishDropped / item.finished * 100).toFixed(2) item.finishDropRate = (item.finishDropped / item.finished * 100).toFixed(2)
item.completionRate = (item.finished / item.started * 100).toFixed(2)
item item
@campaignDropOffs.levels = _.map @campaignDropOffs.levels, mapFn, @ @campaignDropOffs.levels = _.map @campaignDropOffs.levels, mapFn, @
@campaignDropOffs.startDay = startDay @campaignDropOffs.startDay = startDay
@render() @render()
# TODO: Why do we need this url dash? # TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'campaign_drop_offs', { request = @supermodel.addRequestResource 'campaign_completions', {
url: '/db/analytics_log_event/-/campaign_drop_offs' url: '/db/analytics_log_event/-/campaign_completions'
data: {startDay: startDay, slug: @campaignHandle} data: {startDay: startDay, slug: @campaignHandle}
method: 'POST' method: 'POST'
success: success success: success

View file

@ -21,11 +21,11 @@ class AnalyticsLogEventHandler extends Handler
instance instance
getByRelationship: (req, res, args...) -> getByRelationship: (req, res, args...) ->
return @getLevelCompletionsBySlugs(req, res) if args[1] is 'level_completions' return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions'
return @getCampaignDropOffs(req, res) if args[1] is 'campaign_drop_offs' return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions'
super(arguments...) super(arguments...)
getLevelCompletionsBySlugs: (req, res) -> getLevelCompletionsBySlug: (req, res) ->
# Returns an array of per-day level starts and finishes # Returns an array of per-day level starts and finishes
# Parameters: # Parameters:
# slug - level slug # slug - level slug
@ -97,8 +97,8 @@ class AnalyticsLogEventHandler extends Handler
@levelCompletionsCache[cacheKey] = completions[level] @levelCompletionsCache[cacheKey] = completions[level]
@sendSuccess res, completions[levelSlug] @sendSuccess res, completions[levelSlug]
getCampaignDropOffs: (req, res) -> getCampaignCompletionsBySlug: (req, res) ->
# Returns a dictionary of per-campaign level start and finish drop-offs # Returns a dictionary of per-campaign level starts, finishes, and drop-offs
# Drop-off: last started or finished level event # Drop-off: last started or finished level event
# Parameters: # Parameters:
# slugs - array of campaign slugs # slugs - array of campaign slugs
@ -128,7 +128,7 @@ class AnalyticsLogEventHandler extends Handler
cacheKey += 'e' + endDay if endDay? cacheKey += 'e' + endDay if endDay?
return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignDropOffsCache[cacheKey] return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignDropOffsCache[cacheKey]
calculateDropOffs = (campaigns) => getCompletions = (campaigns) =>
# Calculate campaign drop off rates # Calculate campaign drop off rates
# Input: # Input:
# campaigns - per-campaign dictionary of ordered level slugs # campaigns - per-campaign dictionary of ordered level slugs
@ -180,7 +180,7 @@ class AnalyticsLogEventHandler extends Handler
levelProgression[level].finishDropped++ if i is userProgression[user].length - 1 levelProgression[level].finishDropped++ if i is userProgression[user].length - 1
# Put in campaign order # Put in campaign order
campaignRates = {} completions = {}
for level of levelProgression for level of levelProgression
for campaign of campaigns for campaign of campaigns
if level in campaigns[campaign] if level in campaigns[campaign]
@ -188,14 +188,14 @@ class AnalyticsLogEventHandler extends Handler
startDropped = levelProgression[level].startDropped startDropped = levelProgression[level].startDropped
finished = levelProgression[level].finished finished = levelProgression[level].finished
finishDropped = levelProgression[level].finishDropped finishDropped = levelProgression[level].finishDropped
campaignRates[campaign] ?= completions[campaign] ?=
levels: [] levels: []
# overall: # overall:
# started: 0, # started: 0,
# startDropped: 0, # startDropped: 0,
# finished: 0, # finished: 0,
# finishDropped: 0 # finishDropped: 0
campaignRates[campaign].levels.push completions[campaign].levels.push
level: level level: level
started: started started: started
startDropped: startDropped startDropped: startDropped
@ -204,19 +204,19 @@ class AnalyticsLogEventHandler extends Handler
break break
# Sort level data by campaign order # Sort level data by campaign order
for campaign of campaignRates for campaign of completions
campaignRates[campaign].levels.sort (a, b) -> completions[campaign].levels.sort (a, b) ->
if campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level) then return -1 else 1 if campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level) then return -1 else 1
# Return all campaign data for simplicity # Return all campaign data for simplicity
# Cache other individual campaigns too, since we have them # Cache other individual campaigns too, since we have them
@campaignDropOffsCache[cacheKey] = campaignRates @campaignDropOffsCache[cacheKey] = completions
for campaign of campaignRates for campaign of completions
cacheKey = campaign cacheKey = campaign
cacheKey += 's' + startDay if startDay? cacheKey += 's' + startDay if startDay?
cacheKey += 'e' + endDay if endDay? cacheKey += 'e' + endDay if endDay?
@campaignDropOffsCache[cacheKey] = campaignRates @campaignDropOffsCache[cacheKey] = completions
@sendSuccess res, campaignRates @sendSuccess res, completions
getLevelData = (campaigns, campaignLevelIDs) => getLevelData = (campaigns, campaignLevelIDs) =>
# Get level data and replace levelIDs with level slugs in campaigns # Get level data and replace levelIDs with level slugs in campaigns
@ -239,10 +239,8 @@ class AnalyticsLogEventHandler extends Handler
for campaign of campaigns for campaign of campaigns
mapFn = (item) -> levelSlugMap[item] mapFn = (item) -> levelSlugMap[item]
campaigns[campaign] = _.map campaigns[campaign], mapFn, @ campaigns[campaign] = _.map campaigns[campaign], mapFn, @
# Forest campaign levels are reversed for some reason
campaigns[campaign].reverse() if campaign is 'forest'
calculateDropOffs campaigns getCompletions campaigns
getCampaignData = () => getCampaignData = () =>
# Get campaign data # Get campaign data