Add completion rates to levels in campaign editor

This commit is contained in:
Matt Lott 2014-12-31 11:49:17 -08:00
parent 9d55be8ab1
commit fcf5346aa3
3 changed files with 119 additions and 1 deletions
app
templates/editor/campaign
views/editor/campaign
server/analytics

View file

@ -5,7 +5,24 @@
a(href="/editor/level/#{level.get('slug')}", target="_blank") (edit)
p= level.get('description')
h2 TODO: actually put useful stuff in here
h4 Completion Rates
if levelCompletions
table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
tr
td Date
td Started
td Finished
td Completion %
tbody
- for (var i = 0; i < levelCompletions.length; i++)
tr
td= levelCompletions[i].created
td= levelCompletions[i].started
td= levelCompletions[i].finished
td= levelCompletions[i].rate
else
div Loading...
if level.get('tasks')
.tasks

View file

@ -14,11 +14,39 @@ module.exports = class CampaignLevelView extends CocoView
@fullLevel.fetch()
@listenToOnce @fullLevel, 'sync', => @render?()
@levelSlug = @level.get('slug')
@getLevelCompletions()
getRenderData: ->
c = super()
c.level = if @fullLevel.loaded then @fullLevel else @level
c.levelCompletions = @levelCompletions
c
onClickClose: ->
@$el.addClass('hidden')
@trigger 'hidden'
getLevelCompletions: ->
# Fetch last 7 days of level completion counts
success = (data) =>
return 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()
startDay = new Date()
startDay.setDate(startDay.getUTCDate() - 6)
startDay = startDay.getUTCFullYear() + '-' + (startDay.getUTCMonth() + 1) + '-' + startDay.getUTCDate()
# TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'level_completions', {
url: '/db/analytics_log_event/-/level_completions'
data: {startDay: startDay, slugs: [@levelSlug]}
method: 'POST'
success: success
}, 0
request.load()

View file

@ -1,5 +1,6 @@
AnalyticsLogEvent = require './AnalyticsLogEvent'
Handler = require '../commons/Handler'
log = require 'winston'
class AnalyticsLogEventHandler extends Handler
modelClass: AnalyticsLogEvent
@ -17,4 +18,76 @@ class AnalyticsLogEventHandler extends Handler
instance.set('user', req.user._id)
instance
getByRelationship: (req, res, args...) ->
return @getLevelCompletionsBySlugs(req, res) if args[1] is 'level_completions'
super(arguments...)
getLevelCompletionsBySlugs: (req, res) ->
# Returns an array of per-day level starts and finishes
# Parameters:
# slugs - array of level slugs
# startDay - Inclusive, optional, e.g. '2014-12-14'
# endDay - Exclusive, optional, e.g. '2014-12-16'
# TODO: An uncached call takes about 15s
levelSlugs = req.query.slugs or req.body.slugs
startDay = req.query.startDay or req.body.startDay
endDay = req.query.endDay or req.body.endDay
return @sendSuccess res, [] unless levelSlugs?
# Cache results for 1 day
@levelCompletionsCache ?= {}
@levelCompletionsCachedSince ?= new Date()
if (new Date()) - @levelCompletionsCachedSince > 86400 * 1000 # Dumb cache expiration
@levelCompletionsCache = {}
@levelCompletionsCacheSince = new Date()
cacheKey = levelSlugs.join(',')
cacheKey += 's' + startDay if startDay?
cacheKey += 'e' + endDay if endDay?
return @sendSuccess res, levelCompletions if levelCompletions = @levelCompletionsCache[cacheKey]
# Build query
match = {$match: {$and: [{$or: [{"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}]}}
match["$match"]["$and"].push created: {$gte: new Date(startDay + "T00:00:00.000Z")} if startDay?
match["$match"]["$and"].push created: {$lt: new Date(endDay + "T00:00:00.000Z")} if endDay?
project = {"$project": {"_id": 0, "event": 1, "level": {$ifNull: ["$properties.level", "$properties.levelID"]}, "created": {"$concat": [{"$substr": ["$created", 0, 4]}, "-", {"$substr": ["$created", 5, 2]}, "-", {"$substr" : ["$created", 8, 2]}]}}}
group = {"$group": {"_id": {"event": "$event", "created": "$created", "level": "$level"}, "count": {"$sum": 1}}}
query = AnalyticsLogEvent.aggregate match, project, group
query.exec (err, data) =>
if err? then return @sendDatabaseError res, err
# Build per-level-day started and finished counts
levelDateMap = {}
for item in data
created = item._id.created
event = item._id.event
level = item._id.level
continue unless level?
# 'Started Level' event uses level slug, 'Saw Victory' event uses level name with caps and spaces.
level = level.toLowerCase().replace new RegExp(' ', 'g'), '-' if event is 'Saw Victory'
continue unless level in levelSlugs
levelDateMap[level] ?= {}
levelDateMap[level][created] ?= {}
levelDateMap[level][created] ?= {}
if event is 'Saw Victory'
levelDateMap[level][created]['finished'] = item.count
else
levelDateMap[level][created]['started'] = item.count
# Build list of level completions
completions = []
for level of levelDateMap
for created, item of levelDateMap[level]
completions.push
level: level
created: created
started: item.started
finished: item.finished
@levelCompletionsCache[cacheKey] = completions
@sendSuccess res, completions
module.exports = new AnalyticsLogEventHandler()