Update campaign editor analytics

Add date range picker to level view.
This commit is contained in:
Matt Lott 2015-01-21 16:03:50 -08:00
parent e49c74259b
commit 3f40364616
3 changed files with 137 additions and 101 deletions

View file

@ -5,8 +5,15 @@
a(href="/editor/level/#{level.get('slug')}", target="_blank") (edit)
p= level.get('description')
if analytics.startDay && analytics.endDay
.input-group.input-group-sm
input.form-control#input-startday(type='text', style='width:100px;', value=analytics.startDay)
input.form-control#input-endday(type='text', style='width:100px;', value=analytics.endDay)
button.btn.btn-default.btn-sm#reload-button(style='margin-left:10px;') Reload
h4 Completion Rates
if levelCompletions
if analytics.levelCompletions.loading
div Loading...
else
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
tr
@ -14,48 +21,43 @@
td Started
td Finished
td Completion %
if levelHelps && levelHelps.length === levelCompletions.length
if analytics.levelHelps.levels.length === analytics.levelCompletions.levels.length
td Helps Clicked
td Helps / Started
td Help Videos
td Videos / Started
tbody
- for (var i = 0; i < levelCompletions.length; i++)
- for (var i = 0; i < analytics.levelCompletions.levels.length; i++)
tr
td= levelCompletions[i].created
td= levelCompletions[i].started
td= levelCompletions[i].finished
td= levelCompletions[i].rate
if levelHelps && levelHelps.length === levelCompletions.length
td= levelHelps[i].alertHelps + levelHelps[i].paletteHelps
td= ((levelHelps[i].alertHelps + levelHelps[i].paletteHelps) / levelCompletions[i].started).toFixed(2)
td= levelHelps[i].videoStarts
td= (levelHelps[i].videoStarts / levelCompletions[i].started).toFixed(2)
else
div Loading...
td= analytics.levelCompletions.levels[i].created
td= analytics.levelCompletions.levels[i].started
td= analytics.levelCompletions.levels[i].finished
td= analytics.levelCompletions.levels[i].rate
if analytics.levelHelps.levels.length === analytics.levelCompletions.levels.length && analytics.levelCompletions.levels[i].created == analytics.levelHelps.levels[i].day
td= analytics.levelHelps.levels[i].alertHelps + analytics.levelHelps.levels[i].paletteHelps
td= ((analytics.levelHelps.levels[i].alertHelps + analytics.levelHelps.levels[i].paletteHelps) / analytics.levelCompletions.levels[i].started).toFixed(2)
td= analytics.levelHelps.levels[i].videoStarts
td= (analytics.levelHelps.levels[i].videoStarts / analytics.levelCompletions.levels[i].started).toFixed(2)
h4 Average Playtimes
if levelPlaytimes
if analytics.levelPlaytimes.loading
div Loading...
else
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
tr
td Date
td Average (s)
tbody
- for (var i = 0; i < levelPlaytimes.length; i++)
- for (var i = 0; i < analytics.levelPlaytimes.levels.length; i++)
tr
td= levelPlaytimes[i].created
td= levelPlaytimes[i].average.toFixed(2)
else
div Loading...
td= analytics.levelPlaytimes.levels[i].created
td= analytics.levelPlaytimes.levels[i].average.toFixed(2)
h4 Common Problems
if commonProblems
if commonProblems.startDay
if commonProblems.endDay
div(style='font-size:10pt') #{commonProblems.startDay} to #{commonProblems.endDay}
else
div(style='font-size:10pt') #{commonProblems.startDay} to today
if analytics.commonProblems.loading
div Loading...
else
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
tr
@ -64,18 +66,18 @@
td Error Hint
td Count
tbody
- for (var i = 0; i < commonProblems.length && i < 20; i++)
- for (var i = 0; i < analytics.commonProblems.levels.length && i < 20; i++)
tr
td= commonProblems[i].language
td= commonProblems[i].message
td= commonProblems[i].hint
td= commonProblems[i].count
else
div Loading...
td= analytics.commonProblems.levels[i].language
td= analytics.commonProblems.levels[i].message
td= analytics.commonProblems.levels[i].hint
td= analytics.commonProblems.levels[i].count
h4 Recent Sessions
if recentSessions
div(style='font-size:10pt') Latest #{recentSessions.length} sessions for this level
if analytics.recentSessions.loading
div Loading...
else
div(style='font-size:10pt') Latest #{analytics.recentSessions.levels.length} sessions for this level
div(style='font-size:10pt') Double-click row to open player and session
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
@ -87,19 +89,17 @@
td Complete
td Changed
tbody
- for (var i = 0; i < recentSessions.length; i++)
tr.recent-session(data-player-id=recentSessions[i].creator, data-session-id=recentSessions[i]._id)
td= recentSessions[i]._id
td= recentSessions[i].creatorName || recentSessions[i].creator
td= recentSessions[i].codeLanguage
td= recentSessions[i].playtime
if recentSessions[i].state && recentSessions[i].state.complete
td= recentSessions[i].state.complete
- for (var i = 0; i < analytics.recentSessions.levels.length; i++)
tr.recent-session(data-player-id=analytics.recentSessions.levels[i].creator, data-session-id=analytics.recentSessions.levels[i]._id)
td= analytics.recentSessions.levels[i]._id
td= analytics.recentSessions.levels[i].creatorName || analytics.recentSessions.levels[i].creator
td= analytics.recentSessions.levels[i].codeLanguage
td= analytics.recentSessions.levels[i].playtime
if analytics.recentSessions.levels[i].state && analytics.recentSessions.levels[i].state.complete
td= analytics.recentSessions.levels[i].state.complete
else
td false
td= recentSessions[i].changed
else
div Loading...
td= analytics.recentSessions.levels[i].changed
if level.get('tasks')
.tasks

View file

@ -25,9 +25,9 @@ module.exports = class CampaignAnalyticsModal extends ModalView
getRenderData: ->
c = super()
c.campaignCompletions = @campaignCompletions
c.showLeftGame = @showLeftGame
c.showSubscriptions = @showSubscriptions
c.campaignCompletions = @campaignCompletions
c
afterRender: ->
@ -124,13 +124,13 @@ module.exports = class CampaignAnalyticsModal extends ModalView
# Chain these together so we can calculate relative metrics (e.g. left game per second)
@getCampaignLevelCompletions startDay, endDay, () =>
@render()
@render?()
@getCompaignLevelDrops startDay, endDay, () =>
@render()
@render?()
@getCampaignAveragePlaytimes startDayDashed, endDayDashed, () =>
@render()
@render?()
@getCampaignLevelSubscriptions startDay, endDay, () =>
@render()
@render?()
getCampaignAveragePlaytimes: (startDay, endDay, doneCallback) =>
# Fetch level average playtimes

View file

@ -11,6 +11,7 @@ module.exports = class CampaignLevelView extends CocoView
events:
'click .close': 'onClickClose'
'click #reload-button': 'onClickReloadButton'
'dblclick .recent-session': 'onDblClickRecentSession'
constructor: (options, @level) ->
@ -20,26 +21,28 @@ module.exports = class CampaignLevelView extends CocoView
@listenToOnce @fullLevel, 'sync', => @render?()
@levelSlug = @level.get('slug')
@getCommonLevelProblems()
@getLevelCompletions()
@getLevelHelps()
@getLevelPlaytimes()
@getRecentSessions()
@getAnalytics()
getRenderData: ->
c = super()
c.level = if @fullLevel.loaded then @fullLevel else @level
c.commonProblems = @commonProblems
c.levelCompletions = @levelCompletions
c.levelHelps = @levelHelps
c.levelPlaytimes = @levelPlaytimes
c.recentSessions = @recentSessions
c.analytics = @analytics
c
afterRender: ->
super()
$("#input-startday").datepicker dateFormat: "yy-mm-dd"
$("#input-endday").datepicker dateFormat: "yy-mm-dd"
onClickClose: ->
@$el.addClass('hidden')
@trigger 'hidden'
onClickReloadButton: () =>
startDay = $('#input-startday').val()
endDay = $('#input-endday').val()
@getAnalytics startDay, endDay
onDblClickRecentSession: (e) ->
# Admin view of players' code
return unless me.isAdmin()
@ -48,91 +51,124 @@ module.exports = class CampaignLevelView extends CocoView
session = new LevelSession _id: row.data 'session-id'
@openModalView new ModelModal models: [session, player]
getCommonLevelProblems: ->
# Fetch last 30 days of common level problems
startDay = utils.getUTCDay -29
startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
getAnalytics: (startDay, endDay) =>
if startDay?
startDayDashed = startDay
startDay = startDay.replace(/-/g, '')
else
startDay = utils.getUTCDay -14
startDayDashed = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
if endDay?
endDayDashed = endDay
endDay = endDay.replace(/-/g, '')
else
endDay = utils.getUTCDay -1
endDayDashed = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}"
success = (data) =>
return if @destroyed
@commonProblems = data
@commonProblems.startDay = startDay
@analytics =
startDay: startDayDashed
endDay: endDayDashed
commonProblems:
levels: []
loading: true
levelCompletions:
levels: []
loading: true
levelHelps:
levels: []
loading: true
levelPlaytimes:
levels: []
loading: true
recentSessions:
levels: []
loading: true
@render()
@getCommonLevelProblems startDayDashed, endDayDashed, () =>
@analytics.commonProblems.loading = false
@render()
@getLevelCompletions startDay, endDay, () =>
@analytics.levelCompletions.loading = false
@render()
@getLevelHelps startDay, endDay, () =>
@analytics.levelHelps.loading = false
@render()
@getLevelPlaytimes startDayDashed, endDayDashed, () =>
@analytics.levelPlaytimes.loading = false
@render()
@getRecentSessions () =>
@analytics.recentSessions.loading = false
@render()
getCommonLevelProblems: (startDay, endDay, doneCallback) ->
success = (data) =>
return doneCallback() if @destroyed
@analytics.commonProblems.levels = data
doneCallback()
# TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'common_problems', {
url: '/db/user_code_problem/-/common_problems'
data: {startDay: startDay, slug: @levelSlug}
data: {startDay: startDay, endDay: endDay, slug: @levelSlug}
method: 'POST'
success: success
}, 0
request.load()
getLevelCompletions: ->
# Fetch last 14 days of level completion counts
getLevelCompletions: (startDay, endDay, doneCallback) ->
success = (data) =>
return if @destroyed
return doneCallback() if @destroyed
data.sort (a, b) -> if a.created < b.created then 1 else -1
mapFn = (item) ->
item.rate = (item.finished / item.started * 100).toFixed(2)
item
@levelCompletions = _.map data, mapFn, @
@render()
@analytics.levelCompletions.levels = _.map data, mapFn, @
doneCallback()
startDay = utils.getUTCDay -14
# TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'level_completions', {
url: '/db/analytics_perday/-/level_completions'
data: {startDay: startDay, slug: @levelSlug}
data: {startDay: startDay, endDay: endDay, slug: @levelSlug}
method: 'POST'
success: success
}, 0
request.load()
getLevelHelps: ->
# Fetch last 14 days of level completion counts
getLevelHelps: (startDay, endDay, doneCallback) ->
success = (data) =>
return if @destroyed
@levelHelps = data.sort (a, b) -> if a.created < b.created then 1 else -1
@render()
startDay = utils.getUTCDay -14
return doneCallback() if @destroyed
@analytics.levelHelps.levels = data.sort (a, b) -> if a.day < b.day then 1 else -1
doneCallback()
request = @supermodel.addRequestResource 'level_helps', {
url: '/db/analytics_perday/-/level_helps'
data: {startDay: startDay, slugs: [@levelSlug]}
data: {startDay: startDay, endDay: endDay, slugs: [@levelSlug]}
method: 'POST'
success: success
}, 0
request.load()
getLevelPlaytimes: ->
# Fetch last 14 days of level average playtimes
getLevelPlaytimes: (startDay, endDay, doneCallback) ->
success = (data) =>
return if @destroyed
@levelPlaytimes = data.sort (a, b) -> if a.created < b.created then 1 else -1
@render()
return doneCallback() if @destroyed
@analytics.levelPlaytimes.levels = data.sort (a, b) -> if a.created < b.created then 1 else -1
doneCallback()
startDay = utils.getUTCDay -13
startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
# TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'playtime_averages', {
url: '/db/level/-/playtime_averages'
data: {startDay: startDay, slugs: [@levelSlug]}
data: {startDay: startDay, endDay: endDay, slugs: [@levelSlug]}
method: 'POST'
success: success
}, 0
request.load()
getRecentSessions: ->
getRecentSessions: (doneCallback) ->
limit = 100
success = (data) =>
return if @destroyed
@recentSessions = data
@render()
return doneCallback() if @destroyed
@analytics.recentSessions.levels = data
doneCallback()
# TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'level_sessions_recent', {