mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 00:40:56 -05:00
Instrument user code problems
This commit is contained in:
parent
719e64e85f
commit
9b79e2ca27
11 changed files with 185 additions and 4 deletions
6
app/models/UserCodeProblem.coffee
Normal file
6
app/models/UserCodeProblem.coffee
Normal file
|
@ -0,0 +1,6 @@
|
|||
CocoModel = require './CocoModel'
|
||||
|
||||
module.exports = class UserCodeProblem extends CocoModel
|
||||
@className: 'UserCodeProblem'
|
||||
@schema: require 'schemas/models/user_code_problem'
|
||||
urlRoot: '/db/user.code.problem'
|
25
app/schemas/models/user_code_problem.coffee
Normal file
25
app/schemas/models/user_code_problem.coffee
Normal file
|
@ -0,0 +1,25 @@
|
|||
c = require './../schemas'
|
||||
|
||||
UserCodeProblemSchema = c.object {
|
||||
title: 'User Code Problem'
|
||||
description: 'Data for a problem in user code.'
|
||||
}
|
||||
|
||||
_.extend UserCodeProblemSchema.properties,
|
||||
creator: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}])
|
||||
created: c.date({title: 'Created', readOnly: true})
|
||||
|
||||
code: String
|
||||
codeSnippet: String
|
||||
errHint: String
|
||||
errId: String
|
||||
errLevel: String
|
||||
errMessage: String
|
||||
errRange: []
|
||||
errType: String
|
||||
language: String
|
||||
levelID: String
|
||||
|
||||
c.extendBasicProperties UserCodeProblemSchema, 'user.code.problem'
|
||||
|
||||
module.exports = UserCodeProblemSchema
|
|
@ -1,20 +1,23 @@
|
|||
ProblemAlertView = require './ProblemAlertView'
|
||||
Range = ace.require('ace/range').Range
|
||||
UserCodeProblem = require 'models/UserCodeProblem'
|
||||
|
||||
module.exports = class Problem
|
||||
annotation: null
|
||||
alertView: null
|
||||
markerRange: null
|
||||
constructor: (@aether, @aetherProblem, @ace, withAlert=false, withRange=false) ->
|
||||
constructor: (@aether, @aetherProblem, @ace, withAlert=false, isCast=false, @levelID) ->
|
||||
@buildAnnotation()
|
||||
@buildAlertView() if withAlert
|
||||
@buildMarkerRange() if withRange
|
||||
@buildMarkerRange() if isCast
|
||||
@saveUserCodeProblem() if isCast
|
||||
|
||||
destroy: ->
|
||||
unless @alertView?.destroyed
|
||||
@alertView?.$el?.remove()
|
||||
@alertView?.destroy()
|
||||
@removeMarkerRange()
|
||||
@userCodeProblem.off() if @userCodeProblem
|
||||
|
||||
buildAnnotation: ->
|
||||
return unless @aetherProblem.range
|
||||
|
@ -46,3 +49,21 @@ module.exports = class Problem
|
|||
@ace.getSession().removeMarker @markerRange.id
|
||||
@markerRange.start.detach()
|
||||
@markerRange.end.detach()
|
||||
|
||||
saveUserCodeProblem: () ->
|
||||
@userCodeProblem = new UserCodeProblem()
|
||||
@userCodeProblem.set 'code', @aether.raw
|
||||
if @aetherProblem.range
|
||||
rawLines = @aether.raw.split '\n'
|
||||
errorLines = rawLines.slice @aetherProblem.range[0].row, @aetherProblem.range[1].row + 1
|
||||
@userCodeProblem.set 'codeSnippet', errorLines.join '\n'
|
||||
@userCodeProblem.set 'errHint', @aetherProblem.hint if @aetherProblem.hint
|
||||
@userCodeProblem.set 'errId', @aetherProblem.id if @aetherProblem.id
|
||||
@userCodeProblem.set 'errLevel', @aetherProblem.level if @aetherProblem.level
|
||||
@userCodeProblem.set 'errMessage', @aetherProblem.message if @aetherProblem.message
|
||||
@userCodeProblem.set 'errRange', @aetherProblem.range if @aetherProblem.range
|
||||
@userCodeProblem.set 'errType', @aetherProblem.type if @aetherProblem.type
|
||||
@userCodeProblem.set 'language', @aether.language.id if @aether.language?.id
|
||||
@userCodeProblem.set 'levelID', @levelID if @levelID
|
||||
@userCodeProblem.save()
|
||||
null
|
|
@ -19,6 +19,7 @@ module.exports = class Spell
|
|||
@supermodel = options.supermodel
|
||||
@skipProtectAPI = options.skipProtectAPI
|
||||
@worker = options.worker
|
||||
@levelID = options.levelID
|
||||
|
||||
p = options.programmableMethod
|
||||
@languages = p.languages ? {}
|
||||
|
|
|
@ -414,7 +414,7 @@ module.exports = class SpellView extends CocoView
|
|||
for aetherProblem, problemIndex in aether.getAllProblems()
|
||||
continue if key = aetherProblem.userInfo?.key and key of seenProblemKeys
|
||||
seenProblemKeys[key] = true if key
|
||||
@problems.push problem = new Problem aether, aetherProblem, @ace, isCast and problemIndex is 0, isCast
|
||||
@problems.push problem = new Problem aether, aetherProblem, @ace, isCast and problemIndex is 0, isCast, @spell.levelID
|
||||
annotations.push problem.annotation if problem.annotation
|
||||
@aceSession.setAnnotations annotations
|
||||
@highlightCurrentLine aether.flow unless _.isEmpty aether.flow
|
||||
|
|
|
@ -132,6 +132,7 @@ module.exports = class TomeView extends CocoView
|
|||
worker: @worker
|
||||
language: language
|
||||
spectateView: @options.spectateView
|
||||
levelID: @options.levelID
|
||||
|
||||
for thangID, spellKeys of @thangSpells
|
||||
thang = world.getThangByID thangID
|
||||
|
|
|
@ -8,6 +8,7 @@ module.exports.handlers =
|
|||
'patch': 'patches/patch_handler'
|
||||
'thang_type': 'levels/thangs/thang_type_handler'
|
||||
'user': 'users/user_handler'
|
||||
'user_code_problem': 'user_code_problems/user_code_problem_handler'
|
||||
'user_remark': 'users/remarks/user_remark_handler'
|
||||
'mail_sent': 'mail/sent/mail_sent_handler'
|
||||
'achievement': 'achievements/achievement_handler'
|
||||
|
|
|
@ -28,7 +28,7 @@ module.exports.setup = (app) ->
|
|||
return errors.unauthorized(res, 'Must have an identity to do anything with the db. Do you have cookies enabled?') unless req.user
|
||||
|
||||
try
|
||||
moduleName = module.replace '.', '_'
|
||||
moduleName = module.replace new RegExp('\\.', 'g'), '_'
|
||||
name = handlers[moduleName]
|
||||
handler = require('../' + name)
|
||||
return handler.getLatestVersion(req, res, parts[1], parts[3]) if parts[2] is 'version'
|
||||
|
|
10
server/user_code_problems/UserCodeProblem.coffee
Normal file
10
server/user_code_problems/UserCodeProblem.coffee
Normal file
|
@ -0,0 +1,10 @@
|
|||
mongoose = require 'mongoose'
|
||||
plugins = require '../plugins/plugins'
|
||||
|
||||
UserCodeProblemSchema = new mongoose.Schema({
|
||||
created:
|
||||
type: Date
|
||||
'default': Date.now
|
||||
}, {strict: false})
|
||||
|
||||
module.exports = UserCodeProblem = mongoose.model('user.code.problem', UserCodeProblemSchema)
|
25
server/user_code_problems/user_code_problem_handler.coffee
Normal file
25
server/user_code_problems/user_code_problem_handler.coffee
Normal file
|
@ -0,0 +1,25 @@
|
|||
UserCodeProblem = require './UserCodeProblem'
|
||||
Handler = require '../commons/Handler'
|
||||
|
||||
class UserCodeProblemHandler extends Handler
|
||||
modelClass: UserCodeProblem
|
||||
jsonSchema: require '../../app/schemas/models/user_code_problem'
|
||||
editableProperties: [
|
||||
'code'
|
||||
'codeSnippet'
|
||||
'errHint'
|
||||
'errId'
|
||||
'errLevel'
|
||||
'errMessage'
|
||||
'errRange'
|
||||
'errType'
|
||||
'language'
|
||||
'levelID'
|
||||
]
|
||||
|
||||
makeNewInstance: (req) ->
|
||||
ucp = super(req)
|
||||
ucp.set('creator', req.user._id)
|
||||
ucp
|
||||
|
||||
module.exports = new UserCodeProblemHandler()
|
91
test/app/views/play/level/tome/Problem.spec.coffee
Normal file
91
test/app/views/play/level/tome/Problem.spec.coffee
Normal file
|
@ -0,0 +1,91 @@
|
|||
Problem = require 'views/play/level/tome/Problem'
|
||||
|
||||
describe 'Problem', ->
|
||||
# boilerplate problem params
|
||||
ace = {
|
||||
getSession: -> {
|
||||
getDocument: -> {
|
||||
createAnchor: ->
|
||||
}
|
||||
addMarker: ->
|
||||
}
|
||||
}
|
||||
aether = {
|
||||
raw: "this.say('hi');\nthis.sad('bye');"
|
||||
language: { id: 'javascript' }
|
||||
}
|
||||
aetherProblem = {
|
||||
hint: 'did you mean say instead of sad?'
|
||||
id: 'unknown_ReferenceError'
|
||||
level: 'error'
|
||||
message: 'Line 1: tmp2[tmp3] is not a function'
|
||||
range: [
|
||||
{ row: 1 }
|
||||
{ row: 1 }
|
||||
]
|
||||
type: 'runtime'
|
||||
}
|
||||
levelID = 'awesome'
|
||||
|
||||
it 'save user code problem', ->
|
||||
new Problem aether, aetherProblem, ace, false, true, levelID
|
||||
expect(jasmine.Ajax.requests.count()).toBe(1)
|
||||
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
expect(request.url).toEqual("/db/user.code.problem")
|
||||
|
||||
params = JSON.parse(request.params)
|
||||
expect(params.code).toEqual(aether.raw)
|
||||
expect(params.codeSnippet).toEqual("this.sad('bye');")
|
||||
expect(params.errHint).toEqual(aetherProblem.hint)
|
||||
expect(params.errId).toEqual(aetherProblem.id)
|
||||
expect(params.errLevel).toEqual(aetherProblem.level)
|
||||
expect(params.errMessage).toEqual(aetherProblem.message)
|
||||
expect(params.errRange).toEqual(aetherProblem.range)
|
||||
expect(params.errType).toEqual(aetherProblem.type)
|
||||
expect(params.language).toEqual(aether.language.id)
|
||||
expect(params.levelID).toEqual(levelID)
|
||||
|
||||
it 'save user code problem no range', ->
|
||||
aetherProblem.range = null
|
||||
new Problem aether, aetherProblem, ace, false, true, levelID
|
||||
expect(jasmine.Ajax.requests.count()).toBe(1)
|
||||
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
expect(request.url).toEqual("/db/user.code.problem")
|
||||
|
||||
params = JSON.parse(request.params)
|
||||
expect(params.code).toEqual(aether.raw)
|
||||
expect(params.errHint).toEqual(aetherProblem.hint)
|
||||
expect(params.errId).toEqual(aetherProblem.id)
|
||||
expect(params.errLevel).toEqual(aetherProblem.level)
|
||||
expect(params.errMessage).toEqual(aetherProblem.message)
|
||||
expect(params.errType).toEqual(aetherProblem.type)
|
||||
expect(params.language).toEqual(aether.language.id)
|
||||
expect(params.levelID).toEqual(levelID)
|
||||
|
||||
# Difference when no range
|
||||
expect(params.codeSnippet).toBeUndefined()
|
||||
expect(params.errRange).toBeUndefined()
|
||||
|
||||
it 'save user code problem multi-line snippet', ->
|
||||
aether.raw = "this.say('hi');\nthis.sad\n('bye');"
|
||||
aetherProblem.range = [ { row: 1 }, { row: 2 } ]
|
||||
|
||||
new Problem aether, aetherProblem, ace, false, true, levelID
|
||||
expect(jasmine.Ajax.requests.count()).toBe(1)
|
||||
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
expect(request.url).toEqual("/db/user.code.problem")
|
||||
|
||||
params = JSON.parse(request.params)
|
||||
expect(params.code).toEqual(aether.raw)
|
||||
expect(params.codeSnippet).toEqual("this.sad\n('bye');")
|
||||
expect(params.errHint).toEqual(aetherProblem.hint)
|
||||
expect(params.errId).toEqual(aetherProblem.id)
|
||||
expect(params.errLevel).toEqual(aetherProblem.level)
|
||||
expect(params.errMessage).toEqual(aetherProblem.message)
|
||||
expect(params.errRange).toEqual(aetherProblem.range)
|
||||
expect(params.errType).toEqual(aetherProblem.type)
|
||||
expect(params.language).toEqual(aether.language.id)
|
||||
expect(params.levelID).toEqual(levelID)
|
Loading…
Reference in a new issue