Merge remote-tracking branch 'origin/master'

Conflicts:
	app/views/play/ladder_view.coffee
This commit is contained in:
Scott Erickson 2014-02-13 15:11:11 -08:00
commit 1d3e013cc6
8 changed files with 167 additions and 35 deletions

View file

@ -33,3 +33,6 @@
&.right .arrow &.right .arrow
left: -3% left: -3%
pre
display: inline-block
padding: 5px

View file

@ -20,7 +20,8 @@ else if doc.type == 'function'
if doc.type != 'function' && doc.type != 'snippet' if doc.type != 'function' && doc.type != 'snippet'
p.value p.value
strong Current Value: strong Current Value:
code.current-value(data-prop=doc.name)= value pre
code.current-value(data-prop=doc.name)= value
if doc.args && doc.args.length if doc.args && doc.args.length
p.args p.args

View file

@ -105,9 +105,13 @@ module.exports = class HomeView extends View
@wizardSprite?.destroy() @wizardSprite?.destroy()
onSimulateButtonClick: (e) => onSimulateButtonClick: (e) =>
@alreadyPostedResults = false
$.get "/queue/scoring", (data) => $.get "/queue/scoring", (data) =>
console.log data
levelName = data.sessions[0].levelID levelName = data.sessions[0].levelID
#TODO: Refactor. So much refactor. #TODO: Refactor. So much refactor.
@taskData = data
@teamSessionMap = @generateTeamSessionMap data
world = {} world = {}
god = new God() god = new God()
levelLoader = new LevelLoader(levelName, @supermodel, data.sessions[0].sessionID) levelLoader = new LevelLoader(levelName, @supermodel, data.sessions[0].sessionID)
@ -138,8 +142,63 @@ module.exports = class HomeView extends View
Backbone.Mediator.subscribe 'god:new-world-created', @onWorldCreated, @ Backbone.Mediator.subscribe 'god:new-world-created', @onWorldCreated, @
onWorldCreated: (data) -> onWorldCreated: (data) ->
console.log "GOAL STATES" return if @alreadyPostedResults
console.log data taskResults = @translateGoalStatesIntoTaskResults data.goalStates
console.log "Task Results"
console.log taskResults
$.ajax
url: "/queue/scoring"
data: taskResults
type: 'PUT'
success: (result) =>
console.log "TASK REGISTRATION RESULT:#{JSON.stringify result}"
error: (error) =>
console.log "TASK REGISTRATION ERROR:#{JSON.stringify error}"
complete: (result) =>
@alreadyPostedResults = true
translateGoalStatesIntoTaskResults: (goalStates) =>
taskResults = {}
taskResults =
taskID: @taskData.taskID
receiptHandle: @taskData.receiptHandle
calculationTime: 500
sessions: []
for session in @taskData.sessions
sessionResult =
sessionID: session.sessionID
sessionChangedTime: session.sessionChangedTime
metrics:
rank: @calculateSessionRank session.sessionID, goalStates
taskResults.sessions.push sessionResult
taskResults
calculateSessionRank: (sessionID, goalStates) ->
humansDestroyed = goalStates["destroy-humans"].status is "success"
ogresDestroyed = goalStates["destroy-ogres"].status is "success"
console.log "Humans destroyed:#{humansDestroyed}"
console.log "Ogres destroyed:#{ogresDestroyed}"
console.log "Team Session Map: #{JSON.stringify @teamSessionMap}"
if humansDestroyed is ogresDestroyed
return 0
else if humansDestroyed and @teamSessionMap["ogres"] is sessionID
return 0
else if humansDestroyed and @teamSessionMap["ogres"] isnt sessionID
return 1
else if ogresDestroyed and @teamSessionMap["humans"] is sessionID
return 0
else
return 1
generateTeamSessionMap: (task) ->
teamSessionMap = {}
for session in @taskData.sessions
teamSessionMap[session.team] = session.sessionID
teamSessionMap
filterProgrammableComponents: (thangs, spellToSourceMap) => filterProgrammableComponents: (thangs, spellToSourceMap) =>

View file

@ -20,7 +20,9 @@ class LeaderboardCollection extends CocoCollection
constructor: (level, options) -> constructor: (level, options) ->
super() super()
options ?= {} options ?= {}
@url = "/db/level/#{level.get('original')}.#{level.get('version').major}/leaderboard?#{$.param(options)}" #@url = "/db/level/#{level.get('original')}.#{level.get('version').major}/leaderboard?#{$.param(options)}"
@url = "/db/level/#{level.get('original')}/leaderboard?#{$.param(options)}"
module.exports = class LadderView extends RootView module.exports = class LadderView extends RootView
id: 'ladder-view' id: 'ladder-view'
@ -38,7 +40,8 @@ module.exports = class LadderView extends RootView
# @sessions.once 'sync', @onMySessionsLoaded, @ # @sessions.once 'sync', @onMySessionsLoaded, @
onLevelLoaded: -> @startLoadingPhaseTwoMaybe() onLevelLoaded: -> @startLoadingPhaseTwoMaybe()
onMySessionsLoaded: -> @startLoadingPhaseTwoMaybe() onMySessionsLoaded: ->
@startLoadingPhaseTwoMaybe()
startLoadingPhaseTwoMaybe: -> startLoadingPhaseTwoMaybe: ->
return unless @level.loaded # and @sessions.loaded return unless @level.loaded # and @sessions.loaded
@ -55,8 +58,9 @@ module.exports = class LadderView extends RootView
@leaderboards = {} @leaderboards = {}
@challengers = {} @challengers = {}
for team in teams for team in teams
# teamSession = _.find @sessions.models, (session) -> session.get('team') is team teamSession = _.find @sessions.models, (session) -> session.get('team') is team
teamSession = null teamSession = null
console.log "Team session: #{JSON.stringify teamSession}"
@leaderboards[team] = new LeaderboardData(@level, team, teamSession) @leaderboards[team] = new LeaderboardData(@level, team, teamSession)
@leaderboards[team].once 'sync', @onLeaderboardLoaded, @ @leaderboards[team].once 'sync', @onLeaderboardLoaded, @
# @challengers[team] = new ChallengersData(@level, team, teamSession) # @challengers[team] = new ChallengersData(@level, team, teamSession)
@ -102,7 +106,7 @@ class LeaderboardData
@topPlayers.once 'sync', @leaderboardPartLoaded, @ @topPlayers.once 'sync', @leaderboardPartLoaded, @
# if @session # if @session
# score = @session.get('score') or 25 # score = @session.get('totalScore') or 25
# @playersAbove = new LeaderboardCollection(@level, {order:1, scoreOffset: score, limit: 4, team: @team}) # @playersAbove = new LeaderboardCollection(@level, {order:1, scoreOffset: score, limit: 4, team: @team})
# @playersAbove.fetch() # @playersAbove.fetch()
# @playersAbove.once 'sync', @leaderboardPartLoaded, @ # @playersAbove.once 'sync', @leaderboardPartLoaded, @
@ -122,7 +126,7 @@ class LeaderboardData
class ChallengersData class ChallengersData
constructor: (@level, @team, @session) -> constructor: (@level, @team, @session) ->
_.extend @, Backbone.Events _.extend @, Backbone.Events
score = @session?.get('score') or 25 score = @session?.get('totalScore') or 25
@easyPlayer = new LeaderboardCollection(@level, {order:1, scoreOffset: score - 5, limit: 1, team: @team}) @easyPlayer = new LeaderboardCollection(@level, {order:1, scoreOffset: score - 5, limit: 1, team: @team})
@easyPlayer.fetch() @easyPlayer.fetch()
@easyPlayer.once 'sync', @challengerLoaded, @ @easyPlayer.once 'sync', @challengerLoaded, @

View file

@ -8,6 +8,39 @@ filters = require 'lib/image_filter'
# If we use marked somewhere else, we'll have to make sure to preserve options # If we use marked somewhere else, we'll have to make sure to preserve options
marked.setOptions {gfm: true, sanitize: false, smartLists: true, breaks: true} marked.setOptions {gfm: true, sanitize: false, smartLists: true, breaks: true}
safeJSONStringify = (input, maxDepth) ->
recursion = (input, path, depth) ->
output = {}
pPath = undefined
refIdx = undefined
path = path or ""
depth = depth or 0
depth++
return "{depth over " + maxDepth + "}" if maxDepth and depth > maxDepth
for p of input
pPath = ((if path then (path + ".") else "")) + p
if typeof input[p] is "function"
output[p] = "{function}"
else if typeof input[p] is "object"
refIdx = refs.indexOf(input[p])
if -1 isnt refIdx
output[p] = "{reference to " + refsPaths[refIdx] + "}"
else
refs.push input[p]
refsPaths.push pPath
output[p] = recursion(input[p], pPath, depth)
else
output[p] = input[p]
output
refs = []
refsPaths = []
maxDepth = maxDepth or 5
if typeof input is "object"
output = recursion(input)
else
output = input
JSON.stringify output, null, 1
module.exports = class SpellPaletteEntryView extends View module.exports = class SpellPaletteEntryView extends View
tagName: 'div' # Could also try <code> instead of <div>, but would need to adjust colors tagName: 'div' # Could also try <code> instead of <div>, but would need to adjust colors
className: 'spell-palette-entry-view' className: 'spell-palette-entry-view'
@ -61,6 +94,7 @@ module.exports = class SpellPaletteEntryView extends View
formatPopover: -> formatPopover: ->
content = popoverTemplate doc: @doc, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? []) content = popoverTemplate doc: @doc, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? [])
owner = if @doc.owner is 'this' then @thang else window[@doc.owner] owner = if @doc.owner is 'this' then @thang else window[@doc.owner]
content = content.replace /#{spriteName}/g, @thang.spriteName # No quotes like we'd get with @formatValue
content.replace /\#\{(.*?)\}/g, (s, properties) => @formatValue downTheChain(owner, properties.split('.')) content.replace /\#\{(.*?)\}/g, (s, properties) => @formatValue downTheChain(owner, properties.split('.'))
formatValue: (v) -> formatValue: (v) ->
@ -70,7 +104,7 @@ module.exports = class SpellPaletteEntryView extends View
v = @thang[@doc.name] v = @thang[@doc.name]
else else
v = window[@doc.owner][@doc.name] v = window[@doc.owner][@doc.name]
if @type is 'number' and not isNaN v if @doc.type is 'number' and not isNaN v
if v == Math.round v if v == Math.round v
return v return v
return v.toFixed 2 return v.toFixed 2
@ -82,6 +116,8 @@ module.exports = class SpellPaletteEntryView extends View
return v.name return v.name
if _.isArray v if _.isArray v
return '[' + (@formatValue v2 for v2 in v).join(', ') + ']' return '[' + (@formatValue v2 for v2 in v).join(', ') + ']'
if _.isPlainObject v
return safeJSONStringify v, 2
v v
onMouseOver: (e) -> onMouseOver: (e) ->
@ -95,7 +131,6 @@ module.exports = class SpellPaletteEntryView extends View
onFrameChanged: (e) -> onFrameChanged: (e) ->
return unless e.selectedThang?.id is @thang.id return unless e.selectedThang?.id is @thang.id
@options.thang = @thang = e.selectedThang # Update our thang to the current version @options.thang = @thang = e.selectedThang # Update our thang to the current version
@$el.find("code.current-value").text(@formatValue())
destroy: -> destroy: ->
@$el.off() @$el.off()

View file

@ -10,7 +10,7 @@ crypto = require 'crypto'
module.exports.queueClient = undefined module.exports.queueClient = undefined
defaultMessageVisibilityTimeoutInSeconds = 20 defaultMessageVisibilityTimeoutInSeconds = 500
defaultMessageReceiptTimeout = 10 defaultMessageReceiptTimeout = 10
@ -190,7 +190,7 @@ class MongoQueue extends events.EventEmitter
conditions = conditions =
queue: @queueName queue: @queueName
scheduledVisibilityTime: scheduledVisibilityTime:
$lt: new Date() $lte: new Date()
options = options =
sort: 'scheduledVisibilityTime' sort: 'scheduledVisibilityTime'
@ -214,7 +214,7 @@ class MongoQueue extends events.EventEmitter
queue: @queueName queue: @queueName
receiptHandle: receiptHandle receiptHandle: receiptHandle
scheduledVisibilityTime: scheduledVisibilityTime:
$lt: new Date() $gte: new Date()
@Message.findOneAndRemove conditions, {}, (err, data) => @Message.findOneAndRemove conditions, {}, (err, data) =>
if err? then @emit 'error',err,data else @emit 'delete',err,data if err? then @emit 'error',err,data else @emit 'delete',err,data
@ -237,14 +237,19 @@ class MongoQueue extends events.EventEmitter
queue: @queueName queue: @queueName
receiptHandle: receiptHandle receiptHandle: receiptHandle
scheduledVisibilityTime: scheduledVisibilityTime:
$lt: new Date() $gte: new Date()
update = update =
$set: $set:
scheduledVisibilityTime: @_constructDefaultVisibilityTimeoutDate secondsFromNow scheduledVisibilityTime: @_constructDefaultVisibilityTimeoutDate secondsFromNow
@Message.findOneAndUpdate conditions, update, (err, data) => @Message.findOneAndUpdate conditions, update, (err, data) =>
if err? then @emit 'error',err,data else @emit 'update',err,data if err?
log.error "There was a problem updating the message visibility timeout:#{err}"
@emit 'error',err,data
else
@emit 'update',err,data
log.info "The message visibility time was updated"
callback? err, data callback? err, data

View file

@ -70,10 +70,10 @@ LevelHandler = class LevelHandler extends Handler
sessionQuery = { sessionQuery = {
level: {original: level.original.toString(), majorVersion: level.version.major} level: {original: level.original.toString(), majorVersion: level.version.major}
creator: req.user.id #creator: req.user.id
submitted: true
} }
Session.find sessionQuery, '_id totalScore submitted team creatorName', (err, results) =>
Session.find(sessionQuery).exec (err, results) =>
return @sendDatabaseError(res, err) if err return @sendDatabaseError(res, err) if err
res.send(results) res.send(results)
res.end() res.end()
@ -83,7 +83,28 @@ LevelHandler = class LevelHandler extends Handler
# [original, version] = id.split('.') # [original, version] = id.split('.')
# version = parseInt version # version = parseInt version
# console.log 'get leaderboard for', original, version, req.query # console.log 'get leaderboard for', original, version, req.query
return res.send([]) @getDocumentForIdOrSlug id, (err, level) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless level?
return @sendUnauthorizedError(res) unless @hasAccessToDocument(req, level)
if parseInt(req.query.order) is 1
scoreQuery = {"$gte":parseFloat req.query.scoreOffset}
else
scoreQuery = {"$lte": parseFloat req.query.scoreOffset}
sessionsQuery =
level: {original: level.original.toString(), majorVersion: level.version.major}
team: req.query.team
totalScore: scoreQuery
submitted: true
console.log sessionsQuery
query = Session.find(sessionsQuery).sort({"totalScore":parseInt(req.query.order)}).limit(parseInt req.query.limit)
Session.find sessionsQuery, '_id totalScore submitted team creatorName', (err, resultSessions) =>
return @sendDatabaseError(res, err) if err
if resultSessions
return @sendSuccess res, resultSessions
res.send([])
getFeedback: (req, res, id) -> getFeedback: (req, res, id) ->
@getDocumentForIdOrSlug id, (err, level) => @getDocumentForIdOrSlug id, (err, level) =>

View file

@ -39,7 +39,7 @@ module.exports.createNewTask = (req, res) ->
LevelSession.find { "levelID": "project-dota", "submitted": true}, (err, submittedSessions) -> LevelSession.find { "levelID": "project-dota", "submitted": true}, (err, submittedSessions) ->
taskPairs = [] taskPairs = []
for session in submittedSessions for session in submittedSessions
if String(session._id) isnt req.body.session if String(session._id) isnt req.body.session and session.team isnt sessionToScore.team
taskPairs.push [req.body.session,String session._id] taskPairs.push [req.body.session,String session._id]
async.each taskPairs, sendTaskPairToQueue, (taskPairError) -> async.each taskPairs, sendTaskPairToQueue, (taskPairError) ->
return errors.serverError res, "There was an error sending the task pairs to the queue" if taskPairError? return errors.serverError res, "There was an error sending the task pairs to the queue" if taskPairError?
@ -61,6 +61,7 @@ module.exports.dispatchTaskToConsumer = (req, res) ->
if (not message?) or message.isEmpty() or taskQueueReceiveError? if (not message?) or message.isEmpty() or taskQueueReceiveError?
return errors.gatewayTimeoutError res, "No messages were receieved from the queue. Msg:#{taskQueueReceiveError}" return errors.gatewayTimeoutError res, "No messages were receieved from the queue. Msg:#{taskQueueReceiveError}"
messageBody = parseTaskQueueMessage req, res, message messageBody = parseTaskQueueMessage req, res, message
return errors.serverError res, "There was an error parsing the queue message" unless messageBody? return errors.serverError res, "There was an error parsing the queue message" unless messageBody?
@ -74,6 +75,8 @@ module.exports.dispatchTaskToConsumer = (req, res) ->
setTaskObjectTaskLogID taskObject, taskLogObject._id setTaskObjectTaskLogID taskObject, taskLogObject._id
taskObject.receiptHandle = message.getReceiptHandle()
sendResponseObject req, res, taskObject sendResponseObject req, res, taskObject
@ -159,16 +162,18 @@ module.exports.processTaskResult = (req, res) ->
return errors.badInput res, "That computational task has already been performed" if taskLogJSON.calculationTimeMS return errors.badInput res, "That computational task has already been performed" if taskLogJSON.calculationTimeMS
return handleTimedOutTask req, res, clientResponseObject if hasTaskTimedOut taskLogJSON.sentDate return handleTimedOutTask req, res, clientResponseObject if hasTaskTimedOut taskLogJSON.sentDate
destroyQueueMessage clientResponseObject.receiptHandle, (err) ->
return errors.badInput res, "The queue message is already back in the queue, rejecting results." if err?
logTaskComputation clientResponseObject, taskLog, (loggingError) -> logTaskComputation clientResponseObject, taskLog, (loggingError) ->
if loggingError? if loggingError?
return errors.serverError res, "There as a problem logging the task computation: #{loggingError}" return errors.serverError res, "There as a problem logging the task computation: #{loggingError}"
updateScores clientResponseObject, (updatingScoresError, newScores) -> updateScores clientResponseObject, (updatingScoresError, newScores) ->
if updatingScoresError? if updatingScoresError?
return errors.serverError res, "There was an error updating the scores.#{updatingScoresError}" return errors.serverError res, "There was an error updating the scores.#{updatingScoresError}"
sendResponseObject req, res, {"message":"The scores were updated successfully!"} sendResponseObject req, res, {"message":"The scores were updated successfully!"}
@ -178,6 +183,7 @@ hasTaskTimedOut = (taskSentTimestamp) -> taskSentTimestamp + scoringTaskTimeoutI
handleTimedOutTask = (req, res, taskBody) -> errors.clientTimeout res, "The results weren't provided within the timeout" handleTimedOutTask = (req, res, taskBody) -> errors.clientTimeout res, "The results weren't provided within the timeout"
destroyQueueMessage = (receiptHandle, callback) -> scoringTaskQueue.deleteMessage receiptHandle, callback
verifyClientResponse = (responseObject, res) -> verifyClientResponse = (responseObject, res) ->
unless typeof responseObject is "object" unless typeof responseObject is "object"
@ -217,14 +223,12 @@ updateScoreInSession = (scoreObject,callback) ->
LevelSession.findOne sessionObjectQuery, (err, session) -> LevelSession.findOne sessionObjectQuery, (err, session) ->
return callback err, null if err? return callback err, null if err?
updateObject =
session.meanStrength = scoreObject.meanStrength meanStrength: scoreObject.meanStrength
session.standardDeviation = scoreObject.standardDeviation standardDeviation: scoreObject.standardDeviation
session.totalScore = scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation totalScore: scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation
log.info "New total score for session #{scoreObject.id} is #{updateObject.totalScore}"
log.info "Saving session #{session._id}!" LevelSession.update sessionObjectQuery, updateObject, callback
session.save callback
putRankingFromMetricsIntoScoreObject = (taskObject,scoreObject) -> putRankingFromMetricsIntoScoreObject = (taskObject,scoreObject) ->