Add common user code problems to campaign editor

In level view, showing data from last 30 days, with top 20 issues by
count.
This commit is contained in:
Matt Lott 2015-01-05 13:42:14 -08:00
parent 0df9de551c
commit b4a0fe146e
3 changed files with 106 additions and 3 deletions

View file

@ -38,9 +38,31 @@
td= levelPlaytimes[i].average.toFixed(2) td= levelPlaytimes[i].average.toFixed(2)
else else
div Loading... div Loading...
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
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
tr
td Language
td Error Message
td Error Hint
td Count
tbody
- for (var i = 0; i < commonProblems.length && i < 20; i++)
tr
td= commonProblems[i].language
td= commonProblems[i].message
td= commonProblems[i].hint
td= commonProblems[i].count
else
div Loading...
if level.get('tasks') if level.get('tasks')
.tasks .tasks
h3 Tasks (read only) h3 Tasks (read only)

View file

@ -15,12 +15,14 @@ module.exports = class CampaignLevelView extends CocoView
@listenToOnce @fullLevel, 'sync', => @render?() @listenToOnce @fullLevel, 'sync', => @render?()
@levelSlug = @level.get('slug') @levelSlug = @level.get('slug')
@getCommonLevelProblems()
@getLevelCompletions() @getLevelCompletions()
@getLevelPlaytimes() @getLevelPlaytimes()
getRenderData: -> getRenderData: ->
c = super() c = super()
c.level = if @fullLevel.loaded then @fullLevel else @level c.level = if @fullLevel.loaded then @fullLevel else @level
c.commonProblems = @commonProblems
c.levelCompletions = @levelCompletions c.levelCompletions = @levelCompletions
c.levelPlaytimes = @levelPlaytimes c.levelPlaytimes = @levelPlaytimes
c c
@ -29,6 +31,27 @@ module.exports = class CampaignLevelView extends CocoView
@$el.addClass('hidden') @$el.addClass('hidden')
@trigger 'hidden' @trigger 'hidden'
getCommonLevelProblems: ->
# Fetch last 30 days of common level problems
startDay = new Date()
startDay.setDate(startDay.getUTCDate() - 29)
startDay = startDay.getUTCFullYear() + '-' + (startDay.getUTCMonth() + 1) + '-' + startDay.getUTCDate()
success = (data) =>
return if @destroyed
@commonProblems = data
@commonProblems.startDay = startDay
@render()
# 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}
method: 'POST'
success: success
}, 0
request.load()
getLevelCompletions: -> getLevelCompletions: ->
# Fetch last 7 days of level completion counts # Fetch last 7 days of level completion counts
success = (data) => success = (data) =>

View file

@ -23,4 +23,62 @@ class UserCodeProblemHandler extends Handler
ucp.set('creator', req.user._id) ucp.set('creator', req.user._id)
ucp ucp
getByRelationship: (req, res, args...) ->
return @getCommonLevelProblemsBySlug(req, res) if args[1] is 'common_problems'
super(arguments...)
getCommonLevelProblemsBySlug: (req, res) ->
# Returns an ordered array of common user code problems with: language, message, hint, count
# Parameters:
# slug - level slug
# startDay - Inclusive, optional, e.g. '2014-12-14'
# endDay - Exclusive, optional, e.g. '2014-12-16'
levelSlug = req.query.slug or req.body.slug
startDay = req.query.startDay or req.body.startDay
endDay = req.query.endDay or req.body.endDay
return @sendSuccess res, [] unless levelSlug?
# Cache results for 1 day
@commonLevelProblemsCache ?= {}
@commonLevelProblemsCachedSince ?= new Date()
if (new Date()) - @commonLevelProblemsCachedSince > 86400 * 1000 # Dumb cache expiration
@commonLevelProblemsCache = {}
@commonLevelProblemsCachedSince = new Date()
cacheKey = levelSlug
cacheKey += 's' + startDay if startDay?
cacheKey += 'e' + endDay if endDay?
return @sendSuccess res, commonProblems if commonProblems = @commonLevelProblemsCache[cacheKey]
# Build query
match = if startDay? or endDay? then {$match: {$and: []}} else {$match: {}}
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?
group = {"$group": {"_id": {"errMessage": "$errMessageNoLineInfo", "errHint": "$errHint", "language": "$language", "levelID": "$levelID"}, "count": {"$sum": 1}}}
sort = { $sort : { "_id.levelID": 1, count : -1, "_id.language": 1 } }
query = UserCodeProblem.aggregate match, group, sort
query.exec (err, data) =>
if err? then return @sendDatabaseError res, err
# Build per-level common problem lists
commonProblems = {}
for item in data
levelID = item._id.levelID
commonProblems[levelID] ?= []
commonProblems[levelID].push
language: item._id.language
message: item._id.errMessage
hint: item._id.errHint
count: item.count
# Cache all the levels
for levelID of commonProblems
cacheKey = levelID
cacheKey += 's' + startDay if startDay?
cacheKey += 'e' + endDay if endDay?
@commonLevelProblemsCache[cacheKey] = commonProblems[levelID]
@sendSuccess res, commonProblems[levelSlug]
module.exports = new UserCodeProblemHandler() module.exports = new UserCodeProblemHandler()