2014-02-05 09:39:14 -08:00
config = require ' ../../server_config '
2014-02-10 13:18:39 -08:00
log = require ' winston '
2014-02-05 09:39:14 -08:00
mongoose = require ' mongoose '
async = require ' async '
errors = require ' ../commons/errors '
aws = require ' aws-sdk '
2014-02-06 13:25:11 -08:00
db = require ' ./../routes/db '
2014-02-05 09:39:14 -08:00
mongoose = require ' mongoose '
queues = require ' ../commons/queue '
2014-02-05 15:07:15 -08:00
LevelSession = require ' ../levels/sessions/LevelSession '
2014-03-07 18:16:48 -08:00
Level = require ' ../levels/Level '
2014-02-07 15:52:24 -08:00
TaskLog = require ' ./task/ScoringTask '
2014-02-08 10:11:43 -08:00
bayes = new ( require ' bayesian-battle ' ) ( )
2014-02-05 09:39:14 -08:00
2014-02-06 14:32:35 -08:00
scoringTaskQueue = undefined
2014-02-26 17:30:56 -08:00
scoringTaskTimeoutInSeconds = 180
2014-02-06 14:32:35 -08:00
2014-02-10 13:18:39 -08:00
module.exports.setup = (app) -> connectToScoringQueue ( )
2014-02-05 15:07:15 -08:00
connectToScoringQueue = ->
2014-02-06 14:32:35 -08:00
queues . initializeQueueClient ->
2014-02-17 14:39:21 -08:00
queues . queueClient . registerQueue " scoring " , { } , (error,data) ->
if error ? then throw new Error " There was an error registering the scoring queue: #{ error } "
2014-02-06 14:32:35 -08:00
scoringTaskQueue = data
2014-02-10 13:18:39 -08:00
log . info " Connected to scoring task queue! "
2014-03-15 09:20:13 -07:00
module.exports.messagesInQueueCount = (req, res) ->
scoringTaskQueue . totalMessagesInQueue (err, count) ->
if err ? then return errors . serverError res , " There was an issue finding the Mongoose count: #{ err } "
2014-03-15 09:40:58 -07:00
response = String ( count )
2014-03-15 09:20:13 -07:00
res . send ( response )
res . end ( )
2014-03-02 17:51:57 -08:00
2014-02-26 12:14:02 -08:00
module.exports.addPairwiseTaskToQueueFromRequest = (req, res) ->
2014-02-24 07:50:43 -08:00
taskPair = req . body . sessions
2014-02-26 12:14:02 -08:00
addPairwiseTaskToQueue req . body . sessions (err, success) ->
if err ? then return errors . serverError res , " There was an error adding pairwise tasks: #{ err } "
sendResponseObject req , res , { " message " : " All task pairs were succesfully sent to the queue " }
2014-03-02 17:51:57 -08:00
2014-02-26 12:14:02 -08:00
addPairwiseTaskToQueue = (taskPair, cb) ->
2014-02-24 07:50:43 -08:00
LevelSession . findOne ( _id : taskPair [ 0 ] ) . lean ( ) . exec (err, firstSession) =>
2014-02-26 12:14:02 -08:00
if err ? then return cb err , false
2014-02-24 07:50:43 -08:00
LevelSession . find ( _id : taskPair [ 1 ] ) . exec (err, secondSession) =>
2014-02-26 12:14:02 -08:00
if err ? then return cb err , false
2014-02-24 07:50:43 -08:00
try
taskPairs = generateTaskPairs ( secondSession , firstSession )
catch e
2014-02-26 12:14:02 -08:00
if e then return cb e , false
2014-02-24 07:50:43 -08:00
2014-02-26 12:14:02 -08:00
sendEachTaskPairToTheQueue taskPairs , (taskPairError) ->
2014-03-02 17:51:57 -08:00
if taskPairError ? then return cb taskPairError , false
2014-02-26 12:14:02 -08:00
cb null , true
2014-03-02 17:51:57 -08:00
2014-02-07 12:36:56 -08:00
2014-02-10 13:18:39 -08:00
module.exports.createNewTask = (req, res) ->
2014-02-18 11:46:14 -08:00
requestSessionID = req . body . session
2014-03-07 18:16:48 -08:00
requestLevelID = req . body . originalLevelID
requestCurrentLevelID = req . body . levelID
2014-03-06 18:48:41 -08:00
requestLevelMajorVersion = parseInt ( req . body . levelMajorVersion )
2014-03-11 16:31:39 -07:00
2014-02-20 09:44:44 -08:00
validatePermissions req , requestSessionID , (error, permissionsAreValid) ->
if err ? then return errors . serverError res , " There was an error validating permissions "
unless permissionsAreValid then return errors . forbidden res , " You do not have the permissions to submit that game to the leaderboard "
2014-02-13 10:37:41 -08:00
2014-02-20 09:44:44 -08:00
return errors . badInput res , " The session ID is invalid " unless typeof requestSessionID is " string "
2014-03-07 18:16:48 -08:00
Level . findOne ( { _id: requestCurrentLevelID } ) . lean ( ) . select ( ' type ' ) . exec (err, levelWithType) ->
if err ? then return errors . serverError res , " There was an error finding the level type "
2014-03-11 16:31:39 -07:00
if not levelWithType . type or levelWithType . type isnt " ladder "
2014-03-07 18:16:48 -08:00
console . log " The level type of level with ID #{ requestLevelID } is #{ levelWithType . type } "
return errors . badInput res , " That level isn ' t a ladder level "
2014-03-11 16:31:39 -07:00
2014-03-07 18:16:48 -08:00
fetchSessionToSubmit requestSessionID , (err, sessionToSubmit) ->
if err ? then return errors . serverError res , " There was an error finding the given session. "
2014-03-11 16:31:39 -07:00
2014-03-07 18:16:48 -08:00
updateSessionToSubmit sessionToSubmit , (err, data) ->
if err ? then return errors . serverError res , " There was an error updating the session "
opposingTeam = calculateOpposingTeam ( sessionToSubmit . team )
fetchInitialSessionsToRankAgainst opposingTeam , requestLevelID , requestLevelMajorVersion , (err, sessionsToRankAgainst) ->
if err ? then return errors . serverError res , " There was an error fetching the sessions to rank against "
2014-03-11 16:31:39 -07:00
2014-03-07 18:16:48 -08:00
taskPairs = generateTaskPairs ( sessionsToRankAgainst , sessionToSubmit )
sendEachTaskPairToTheQueue taskPairs , (taskPairError) ->
if taskPairError ? then return errors . serverError res , " There was an error sending the task pairs to the queue "
2014-03-11 16:31:39 -07:00
2014-03-07 18:16:48 -08:00
sendResponseObject req , res , { " message " : " All task pairs were succesfully sent to the queue " }
2014-02-17 14:39:21 -08:00
module.exports.dispatchTaskToConsumer = (req, res) ->
if isUserAnonymous ( req ) then return errors . forbidden res , " You need to be logged in to simulate games "
2014-02-06 13:25:11 -08:00
2014-02-17 14:39:21 -08:00
scoringTaskQueue . receiveMessage (err, message) ->
2014-03-11 19:17:58 -07:00
if err ? or messageIsInvalid ( message )
res . send 204 , " No games to score. #{ message } "
return res . end ( )
2014-02-18 11:46:14 -08:00
console . log " Received Message "
2014-02-07 12:36:56 -08:00
messageBody = parseTaskQueueMessage req , res , message
2014-02-17 14:39:21 -08:00
return unless messageBody ?
2014-02-06 13:25:11 -08:00
2014-02-07 12:36:56 -08:00
constructTaskObject messageBody , (taskConstructionError, taskObject) ->
2014-02-17 14:39:21 -08:00
if taskConstructionError ? then return errors . serverError res , " There was an error constructing the scoring task "
2014-02-18 11:46:14 -08:00
console . log " Constructed task body "
2014-02-17 14:39:21 -08:00
message . changeMessageVisibilityTimeout scoringTaskTimeoutInSeconds , (err) ->
if err ? then return errors . serverError res , " There was an error changing the message visibility timeout. "
2014-02-18 11:46:14 -08:00
console . log " Changed visibility timeout "
2014-03-03 08:47:09 -08:00
constructTaskLogObject getUserIDFromRequest ( req ) , message . getReceiptHandle ( ) , (taskLogError, taskLogObject) ->
2014-02-17 14:39:21 -08:00
if taskLogError ? then return errors . serverError res , " There was an error creating the task log object. "
2014-02-07 15:52:24 -08:00
2014-02-17 14:39:21 -08:00
taskObject.taskID = taskLogObject . _id
taskObject.receiptHandle = message . getReceiptHandle ( )
2014-02-07 15:52:24 -08:00
2014-02-17 14:39:21 -08:00
sendResponseObject req , res , taskObject
2014-02-13 12:59:21 -08:00
2014-02-17 14:39:21 -08:00
module.exports.processTaskResult = (req, res) ->
clientResponseObject = verifyClientResponse req . body , res
2014-02-05 09:39:14 -08:00
2014-02-18 11:46:14 -08:00
return unless clientResponseObject ?
TaskLog . findOne { _id: clientResponseObject . taskID } , (err, taskLog) ->
2014-02-17 14:39:21 -08:00
return errors . serverError res , " There was an error retrieiving the task log object " if err ?
2014-02-10 13:18:39 -08:00
2014-02-17 14:39:21 -08:00
taskLogJSON = taskLog . toObject ( )
2014-02-10 13:18:39 -08:00
2014-02-17 14:39:21 -08:00
return errors . badInput res , " That computational task has already been performed " if taskLogJSON . calculationTimeMS
return handleTimedOutTask req , res , clientResponseObject if hasTaskTimedOut taskLogJSON . sentDate
2014-02-18 11:46:14 -08:00
scoringTaskQueue . deleteMessage clientResponseObject . receiptHandle , (err) ->
2014-03-02 17:51:57 -08:00
console . log " Deleted message. "
2014-02-18 11:46:14 -08:00
if err ? then return errors . badInput res , " The queue message is already back in the queue, rejecting results. "
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
LevelSession . findOne ( _id: clientResponseObject . originalSessionID ) . lean ( ) . exec (err, levelSession) ->
if err ? then return errors . serverError res , " There was a problem finding the level session: #{ err } "
2014-03-11 16:31:39 -07:00
2014-03-03 08:47:09 -08:00
supposedSubmissionDate = new Date ( clientResponseObject . sessions [ 0 ] . submitDate )
2014-03-11 16:31:39 -07:00
2014-03-03 08:47:09 -08:00
if Number ( supposedSubmissionDate ) isnt Number ( levelSession . submitDate )
2014-03-02 18:55:07 -08:00
return sendResponseObject req , res , { " message " : " The game has been resubmitted. Removing from queue... " }
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
logTaskComputation clientResponseObject , taskLog , (logErr) ->
if logErr ? then return errors . serverError res , " There as a problem logging the task computation: #{ logErr } "
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
updateSessions clientResponseObject , (updateError, newScoreArray) ->
if updateError ? then return errors . serverError res , " There was an error updating the scores. #{ updateError } "
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
newScoresObject = _ . indexBy newScoreArray , ' id '
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
addMatchToSessions clientResponseObject , newScoresObject , (err, data) ->
if err ? then return errors . serverError res , " There was an error updating the sessions with the match! #{ JSON . stringify err } "
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
originalSessionID = clientResponseObject . originalSessionID
originalSessionTeam = clientResponseObject . originalSessionTeam
originalSessionRank = parseInt clientResponseObject . originalSessionRank
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
determineIfSessionShouldContinueAndUpdateLog originalSessionID , originalSessionRank , (err, sessionShouldContinue) ->
if err ? then return errors . serverError res , " There was an error determining if the session should continue, #{ err } "
2014-03-11 16:31:39 -07:00
2014-03-02 18:55:07 -08:00
if sessionShouldContinue
opposingTeam = calculateOpposingTeam ( originalSessionTeam )
opponentID = _ . pull ( _ . keys ( newScoresObject ) , originalSessionID )
sessionNewScore = newScoresObject [ originalSessionID ] . totalScore
opponentNewScore = newScoresObject [ opponentID ] . totalScore
2014-03-11 16:31:39 -07:00
2014-03-06 18:48:41 -08:00
levelOriginalID = levelSession . level . original
levelOriginalMajorVersion = levelSession . level . majorVersion
2014-03-11 19:40:22 -07:00
findNearestBetterSessionID levelOriginalID , levelOriginalMajorVersion , originalSessionID , sessionNewScore , opponentNewScore , opponentID , opposingTeam , (err, opponentSessionID) ->
2014-03-02 18:55:07 -08:00
if err ? then return errors . serverError res , " There was an error finding the nearest sessionID! "
2014-03-11 19:40:22 -07:00
if opponentSessionID
addPairwiseTaskToQueue [ originalSessionID , opponentSessionID ] , (err, success) ->
if err ? then return errors . serverError res , " There was an error sending the pairwise tasks to the queue! "
sendResponseObject req , res , { " message " : " The scores were updated successfully and more games were sent to the queue! " }
else
LevelSession . update { _id: originalSessionID } , { isRanking: false } , { multi: false } , (err, affected) ->
if err ? then return errors . serverError res , " There was an error marking the victorious session as not being ranked. "
return sendResponseObject req , res , { " message " : " There were no more games to rank (game is at top)! " }
2014-03-02 18:55:07 -08:00
else
console . log " Player lost, achieved rank #{ originalSessionRank } "
2014-03-11 16:31:39 -07:00
LevelSession . update { _id: originalSessionID } , { isRanking: false } , { multi: false } , (err, affected) ->
if err ? then return errors . serverError res , " There was an error marking the completed session as not being ranked. "
sendResponseObject req , res , { " message " : " The scores were updated successfully, person lost so no more games are being inserted! " }
2014-02-26 17:30:56 -08:00
2014-03-02 17:51:57 -08:00
2014-02-26 17:30:56 -08:00
determineIfSessionShouldContinueAndUpdateLog = (sessionID, sessionRank, cb) ->
2014-03-02 17:51:57 -08:00
queryParameters =
2014-02-26 17:30:56 -08:00
_id: sessionID
2014-03-02 17:51:57 -08:00
updateParameters =
2014-02-26 17:30:56 -08:00
" $inc " : { }
2014-03-02 17:51:57 -08:00
if sessionRank is 0
2014-02-26 17:30:56 -08:00
updateParameters [ " $inc " ] = { numberOfWinsAndTies: 1 }
else
updateParameters [ " $inc " ] = { numberOfLosses: 1 }
2014-03-02 17:51:57 -08:00
2014-02-26 17:30:56 -08:00
LevelSession . findOneAndUpdate queryParameters , updateParameters , { select: ' numberOfWinsAndTies numberOfLosses ' } , (err, updatedSession) ->
if err ? then return cb err , updatedSession
updatedSession = updatedSession . toObject ( )
totalNumberOfGamesPlayed = updatedSession . numberOfWinsAndTies + updatedSession . numberOfLosses
if totalNumberOfGamesPlayed < 5
console . log " Number of games played is less than 5, continuing... "
cb null , true
else
2014-03-01 16:56:04 -08:00
ratio = ( updatedSession . numberOfLosses ) / ( totalNumberOfGamesPlayed )
2014-02-26 17:30:56 -08:00
if ratio > 0.66
cb null , false
console . log " Ratio( #{ ratio } ) is bad, ending simulation "
else
console . log " Ratio( #{ ratio } ) is good, so continuing simulations "
cb null , true
2014-03-02 17:51:57 -08:00
2014-03-06 18:48:41 -08:00
findNearestBetterSessionID = (levelOriginalID, levelMajorVersion, sessionID, sessionTotalScore, opponentSessionTotalScore, opponentSessionID, opposingTeam, cb) ->
2014-03-01 16:52:17 -08:00
retrieveAllOpponentSessionIDs sessionID , (err, opponentSessionIDs) ->
if err ? then return cb err , null
2014-03-02 17:51:57 -08:00
2014-03-01 16:52:17 -08:00
queryParameters =
2014-03-02 17:51:57 -08:00
totalScore:
2014-03-01 16:52:17 -08:00
$gt : opponentSessionTotalScore
2014-03-02 17:51:57 -08:00
_id:
2014-03-01 16:52:17 -08:00
$nin: opponentSessionIDs
2014-03-06 18:48:41 -08:00
" level.original " : levelOriginalID
" level.majorVersion " : levelMajorVersion
2014-03-01 16:52:17 -08:00
submitted: true
submittedCode:
$exists: true
team: opposingTeam
2014-03-02 17:51:57 -08:00
2014-03-01 16:52:17 -08:00
limitNumber = 1
2014-03-02 17:51:57 -08:00
2014-03-01 16:52:17 -08:00
sortParameters =
totalScore: 1
2014-03-02 17:51:57 -08:00
2014-03-01 16:52:17 -08:00
selectString = ' _id totalScore '
2014-03-02 17:51:57 -08:00
2014-03-01 16:52:17 -08:00
query = LevelSession . findOne ( queryParameters )
. sort ( sortParameters )
. limit ( limitNumber )
. select ( selectString )
. lean ( )
2014-03-02 17:51:57 -08:00
2014-03-01 16:52:17 -08:00
console . log " Finding session with score near #{ opponentSessionTotalScore } "
query . exec (err, session) ->
if err ? then return cb err , session
unless session then return cb err , null
console . log " Found session with score #{ session . totalScore } "
cb err , session . _id
2014-03-02 17:51:57 -08:00
2014-03-01 16:52:17 -08:00
retrieveAllOpponentSessionIDs = (sessionID, cb) ->
query = LevelSession . findOne ( { " _id " : sessionID } )
2014-03-11 21:48:11 -07:00
. select ( ' matches.opponents.sessionID matches.date submitDate ' )
2014-02-26 12:14:02 -08:00
. lean ( )
query . exec (err, session) ->
2014-03-01 16:52:17 -08:00
if err ? then return cb err , null
2014-03-11 21:16:53 -07:00
opponentSessionIDs = ( match . opponents [ 0 ] . sessionID for match in session . matches when match . date > session . submitDate )
2014-03-01 16:52:17 -08:00
cb err , opponentSessionIDs
2014-03-02 17:51:57 -08:00
2014-02-26 12:14:02 -08:00
calculateOpposingTeam = (sessionTeam) ->
teams = [ ' ogres ' , ' humans ' ]
opposingTeams = _ . pull teams , sessionTeam
return opposingTeams [ 0 ]
2014-03-02 17:51:57 -08:00
2014-02-20 09:44:44 -08:00
validatePermissions = (req, sessionID, callback) ->
if isUserAnonymous req then return callback null , false
if isUserAdmin req then return callback null , true
2014-02-20 10:32:32 -08:00
LevelSession . findOne ( _id : sessionID ) . select ( ' creator submittedCode code ' ) . lean ( ) . exec (err, retrievedSession) ->
if err ? then return callback err , retrievedSession
code = retrievedSession . code
submittedCode = retrievedSession . submittedCode
callback null , ( retrievedSession . creator is req . user ? . id and not _ . isEqual ( code , submittedCode ) )
2014-02-18 11:46:14 -08:00
addMatchToSessions = (clientResponseObject, newScoreObject, callback) ->
matchObject = { }
matchObject.date = new Date ( )
matchObject.opponents = { }
for session in clientResponseObject . sessions
sessionID = session . sessionID
matchObject . opponents [ sessionID ] = { }
matchObject . opponents [ sessionID ] . sessionID = sessionID
matchObject . opponents [ sessionID ] . userID = session . creator
matchObject . opponents [ sessionID ] . metrics = { }
2014-02-19 14:26:49 -08:00
matchObject . opponents [ sessionID ] . metrics.rank = Number ( newScoreObject [ sessionID ] . gameRanking )
2014-02-18 11:46:14 -08:00
log . info " Match object computed, result: #{ matchObject } "
log . info " Writing match object to database... "
#use bind with async to do the writes
sessionIDs = _ . pluck clientResponseObject . sessions , ' sessionID '
async . each sessionIDs , updateMatchesInSession . bind ( @ , matchObject ) , (err) -> callback err , null
updateMatchesInSession = (matchObject, sessionID, callback) ->
currentMatchObject = { }
currentMatchObject.date = matchObject . date
currentMatchObject.metrics = matchObject . opponents [ sessionID ] . metrics
opponentsClone = _ . cloneDeep matchObject . opponents
opponentsClone = _ . omit opponentsClone , sessionID
opponentsArray = _ . toArray opponentsClone
currentMatchObject.opponents = opponentsArray
2014-02-17 14:39:21 -08:00
2014-02-18 11:46:14 -08:00
sessionUpdateObject =
2014-03-11 16:31:39 -07:00
$push: { matches: { $each: [ currentMatchObject ] , $slice: - 200 } }
2014-02-18 11:46:14 -08:00
log . info " Updating session #{ sessionID } "
LevelSession . update { " _id " : sessionID } , sessionUpdateObject , callback
2014-02-17 14:39:21 -08:00
messageIsInvalid = (message) -> ( not message ? ) or message . isEmpty ( )
sendEachTaskPairToTheQueue = (taskPairs, callback) -> async . each taskPairs , sendTaskPairToQueue , callback
2014-02-18 11:46:14 -08:00
fetchSessionToSubmit = (submittedSessionID, callback) ->
LevelSession . findOne { _id: submittedSessionID } , (err, session) -> callback err , session ? . toObject ( )
2014-02-17 14:39:21 -08:00
2014-02-18 11:46:14 -08:00
updateSessionToSubmit = (sessionToUpdate, callback) ->
2014-02-17 14:39:21 -08:00
sessionUpdateObject =
submitted: true
submittedCode: sessionToUpdate . code
submitDate: new Date ( )
2014-02-18 11:46:14 -08:00
meanStrength: 25
standardDeviation: 25 / 3
totalScore: 10
2014-02-26 17:30:56 -08:00
numberOfWinsAndTies: 0
numberOfLosses: 0
2014-03-11 16:31:39 -07:00
isRanking: true
2014-02-18 11:46:14 -08:00
LevelSession . update { _id: sessionToUpdate . _id } , sessionUpdateObject , callback
2014-02-17 14:39:21 -08:00
2014-03-06 18:48:41 -08:00
fetchInitialSessionsToRankAgainst = (opposingTeam, levelID, levelMajorVersion, callback) ->
2014-02-26 12:14:02 -08:00
console . log " Fetching sessions to rank against for opposing team #{ opposingTeam } "
findParameters =
2014-03-06 18:48:41 -08:00
" level.original " : levelID
" level.majorVersion " : levelMajorVersion
2014-02-17 14:39:21 -08:00
submitted: true
submittedCode:
$exists: true
2014-02-26 12:14:02 -08:00
team: opposingTeam
2014-03-11 16:31:39 -07:00
2014-03-02 17:51:57 -08:00
sortParameters =
2014-02-26 12:14:02 -08:00
totalScore: 1
2014-03-02 17:51:57 -08:00
2014-02-26 12:14:02 -08:00
limitNumber = 1
2014-03-02 17:51:57 -08:00
2014-02-26 12:14:02 -08:00
query = LevelSession . find ( findParameters )
. sort ( sortParameters )
. limit ( limitNumber )
2014-03-02 17:51:57 -08:00
2014-02-26 12:14:02 -08:00
query . exec callback
2014-02-17 14:39:21 -08:00
generateTaskPairs = (submittedSessions, sessionToScore) ->
taskPairs = [ ]
for session in submittedSessions
session = session . toObject ( )
teams = [ ' ogres ' , ' humans ' ]
opposingTeams = _ . pull teams , sessionToScore . team
if String ( session . _id ) isnt String ( sessionToScore . _id ) and session . team in opposingTeams
console . log " Adding game to taskPairs! "
2014-02-18 11:46:14 -08:00
taskPairs . push [ sessionToScore . _id , String session . _id ]
return taskPairs
2014-02-17 14:39:21 -08:00
sendTaskPairToQueue = (taskPair, callback) ->
2014-03-03 08:47:09 -08:00
scoringTaskQueue . sendMessage { sessions: taskPair } , 0 , (err,data) -> callback ? err , data
2014-02-17 14:39:21 -08:00
getUserIDFromRequest = (req) -> if req . user ? then return req . user . _id else return null
isUserAnonymous = (req) -> if req . user ? then return req . user . get ( ' anonymous ' ) else return true
2014-02-08 10:11:43 -08:00
2014-02-20 09:44:44 -08:00
isUserAdmin = (req) -> return Boolean ( req . user ? . isAdmin ( ) )
2014-02-07 12:36:56 -08:00
parseTaskQueueMessage = (req, res, message) ->
try
2014-02-10 13:18:39 -08:00
if typeof message . getBody ( ) is " object " then return message . getBody ( )
2014-02-07 12:36:56 -08:00
return messageBody = JSON . parse message . getBody ( )
catch e
2014-02-07 14:24:42 -08:00
sendResponseObject req , res , { " error " : " There was an error parsing the task.Error: #{ e } " }
2014-02-07 15:52:24 -08:00
return null
2014-02-05 15:07:15 -08:00
constructTaskObject = (taskMessageBody, callback) ->
2014-02-07 14:24:42 -08:00
async . map taskMessageBody . sessions , getSessionInformation , (err, sessions) ->
2014-02-05 15:07:15 -08:00
return callback err , data if err ?
2014-02-07 12:36:56 -08:00
2014-02-05 15:07:15 -08:00
taskObject =
" messageGenerated " : Date . now ( )
2014-02-07 15:52:24 -08:00
" sessions " : [ ]
2014-02-07 14:24:42 -08:00
for session in sessions
sessionInformation =
2014-02-17 14:39:21 -08:00
" sessionID " : session . _id
" submitDate " : session . submitDate
2014-02-11 12:58:23 -08:00
" team " : session . team ? " No team "
2014-02-17 14:39:21 -08:00
" code " : session . submittedCode
2014-02-10 17:09:51 -08:00
" teamSpells " : session . teamSpells ? { }
" levelID " : session . levelID
2014-02-18 11:46:14 -08:00
" creator " : session . creator
2014-02-20 08:06:11 -08:00
" creatorName " : session . creatorName
2014-02-10 17:09:51 -08:00
2014-02-07 15:52:24 -08:00
taskObject . sessions . push sessionInformation
2014-02-05 16:41:01 -08:00
callback err , taskObject
2014-02-05 15:07:15 -08:00
2014-02-05 16:41:01 -08:00
getSessionInformation = (sessionIDString, callback) ->
2014-02-18 11:46:14 -08:00
LevelSession . findOne { _id : sessionIDString } , (err, session) ->
if err ? then return callback err , { " error " : " There was an error retrieving the session. " }
2014-02-07 12:36:56 -08:00
2014-02-17 14:39:21 -08:00
sessionInformation = session . toObject ( )
2014-02-07 12:36:56 -08:00
callback err , sessionInformation
2014-03-03 08:47:09 -08:00
constructTaskLogObject = (calculatorUserID, messageIdentifierString, callback) ->
2014-02-10 13:18:39 -08:00
taskLogObject = new TaskLog
" createdAt " : new Date ( )
" calculator " : calculatorUserID
" sentDate " : Date . now ( )
" messageIdentifierString " : messageIdentifierString
taskLogObject . save callback
2014-02-07 12:36:56 -08:00
sendResponseObject = (req,res,object) ->
res . setHeader ( ' Content-Type ' , ' application/json ' )
res . send ( object )
res . end ( )
2014-02-08 10:11:43 -08:00
hasTaskTimedOut = (taskSentTimestamp) -> taskSentTimestamp + scoringTaskTimeoutInSeconds * 1000 < Date . now ( )
2014-02-07 15:52:24 -08:00
2014-02-10 13:18:39 -08:00
handleTimedOutTask = (req, res, taskBody) -> errors . clientTimeout res , " The results weren ' t provided within the timeout "
2014-02-07 15:52:24 -08:00
2014-02-08 10:11:43 -08:00
verifyClientResponse = (responseObject, res) ->
unless typeof responseObject is " object "
errors . badInput res , " The response to that query is required to be a JSON object. "
2014-02-10 13:18:39 -08:00
null
else
responseObject
2014-02-08 10:11:43 -08:00
logTaskComputation = (taskObject,taskLogObject, callback) ->
taskLogObject.calculationTimeMS = taskObject . calculationTimeMS
taskLogObject.sessions = taskObject . sessions
2014-02-10 13:18:39 -08:00
taskLogObject . save callback
2014-02-08 10:11:43 -08:00
2014-02-17 14:39:21 -08:00
updateSessions = (taskObject,callback) ->
2014-02-08 10:11:43 -08:00
sessionIDs = _ . pluck taskObject . sessions , ' sessionID '
2014-02-10 13:18:39 -08:00
2014-02-17 14:39:21 -08:00
async . map sessionIDs , retrieveOldSessionData , (err, oldScores) ->
2014-02-18 11:46:14 -08:00
if err ? then callback err , { " error " : " There was an error retrieving the old scores " }
2014-02-10 13:18:39 -08:00
2014-02-08 14:21:16 -08:00
oldScoreArray = _ . toArray putRankingFromMetricsIntoScoreObject taskObject , oldScores
newScoreArray = bayes . updatePlayerSkills oldScoreArray
2014-02-10 10:30:08 -08:00
saveNewScoresToDatabase newScoreArray , callback
saveNewScoresToDatabase = (newScoreArray, callback) ->
2014-02-18 11:46:14 -08:00
async . eachSeries newScoreArray , updateScoreInSession , (err) -> callback err , newScoreArray
2014-02-10 10:30:08 -08:00
updateScoreInSession = (scoreObject,callback) ->
2014-02-18 11:46:14 -08:00
LevelSession . findOne { " _id " : scoreObject . id } , (err, session) ->
if err ? then return callback err , null
2014-02-10 13:18:39 -08:00
2014-02-13 17:10:01 -08:00
session = session . toObject ( )
2014-03-10 08:14:28 -07:00
newTotalScore = scoreObject . meanStrength - 1.8 * scoreObject . standardDeviation
scoreHistoryAddition = [ Date . now ( ) , newTotalScore ]
2014-02-13 12:59:21 -08:00
updateObject =
meanStrength: scoreObject . meanStrength
standardDeviation: scoreObject . standardDeviation
2014-03-10 08:14:28 -07:00
totalScore: newTotalScore
2014-03-11 16:31:39 -07:00
$push: { scoreHistory: { $each: [ scoreHistoryAddition ] , $slice: - 1000 } }
2014-03-10 08:14:28 -07:00
2014-02-18 11:46:14 -08:00
LevelSession . update { " _id " : scoreObject . id } , updateObject , callback
2014-02-13 12:59:21 -08:00
log . info " New total score for session #{ scoreObject . id } is #{ updateObject . totalScore } "
2014-02-10 10:30:08 -08:00
2014-02-07 12:36:56 -08:00
2014-02-08 14:21:16 -08:00
putRankingFromMetricsIntoScoreObject = (taskObject,scoreObject) ->
scoreObject = _ . indexBy scoreObject , ' id '
2014-02-18 11:46:14 -08:00
scoreObject [ session . sessionID ] . gameRanking = session . metrics . rank for session in taskObject . sessions
return scoreObject
2014-02-05 15:07:15 -08:00
2014-02-17 14:39:21 -08:00
retrieveOldSessionData = (sessionID, callback) ->
2014-02-18 11:46:14 -08:00
LevelSession . findOne { " _id " : sessionID } , (err, session) ->
2014-02-08 10:11:43 -08:00
return callback err , { " error " : " There was an error retrieving the session. " } if err ?
2014-02-10 13:18:39 -08:00
2014-02-13 17:10:01 -08:00
session = session . toObject ( )
2014-02-08 10:11:43 -08:00
oldScoreObject =
2014-02-18 11:46:14 -08:00
" standardDeviation " : session . standardDeviation ? 25 / 3
2014-02-08 10:11:43 -08:00
" meanStrength " : session . meanStrength ? 25
2014-02-18 11:46:14 -08:00
" totalScore " : session . totalScore ? ( 25 - 1.8 * ( 25 / 3 ) )
2014-02-08 10:18:41 -08:00
" id " : sessionID
2014-02-08 10:11:43 -08:00
callback err , oldScoreObject