mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-04 17:19:47 -04:00
Add completion rates to levels in campaign editor
This commit is contained in:
parent
9d55be8ab1
commit
fcf5346aa3
3 changed files with 119 additions and 1 deletions
app
server/analytics
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue