codecombat/server/levels/level_handler.coffee
Michael Schmatz 7ddd84248b Simulator fixes!
Fixed memory bug with aggregation framework
2014-04-11 17:11:57 -07:00

276 lines
9.4 KiB
CoffeeScript

Level = require('./Level')
Session = require('./sessions/LevelSession')
User = require '../users/User'
SessionHandler = require('./sessions/level_session_handler')
Feedback = require('./feedbacks/LevelFeedback')
Handler = require('../commons/Handler')
mongoose = require('mongoose')
LevelHandler = class LevelHandler extends Handler
modelClass: Level
editableProperties: [
'description'
'documentation'
'background'
'nextLevel'
'scripts'
'thangs'
'systems'
'victory'
'name'
'i18n'
'icon'
'goals'
'type'
'showsGuide'
]
postEditableProperties: ['name']
getByRelationship: (req, res, args...) ->
return @getSession(req, res, args[0]) if args[1] is 'session'
return @getLeaderboard(req, res, args[0]) if args[1] is 'leaderboard'
return @getMyLeaderboardRank(req, res, args[0]) if args[1] is 'leaderboard_rank'
return @getMySessions(req, res, args[0]) if args[1] is 'my_sessions'
return @getFeedback(req, res, args[0]) if args[1] is 'feedback'
return @getRandomSessionPair(req,res,args[0]) if args[1] is 'random_session_pair'
return @getLeaderboardFacebookFriends(req, res, args[0]) if args[1] is 'leaderboard_facebook_friends'
return @getLeaderboardGPlusFriends(req, res, args[0]) if args[1] is 'leaderboard_gplus_friends'
return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data'
return @checkExistence(req, res, args[0]) if args[1] is 'exists'
return @sendNotFoundError(res)
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
@getDocumentForIdOrSlug id, (err, level) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless level?
return @sendUnauthorizedError(res) unless @hasAccessToDocument(req, level, 'get')
callback err, level
getSession: (req, res, id) ->
return @sendNotFoundError(res) unless req.user
@fetchLevelByIDAndHandleErrors id, req, res, (err, level) =>
sessionQuery =
level:
original: level.original.toString()
majorVersion: level.version.major
creator: req.user.id
if req.query.team?
sessionQuery.team = req.query.team
# TODO: generalize this for levels based on their teams
else if level.get('type') is 'ladder'
sessionQuery.team = 'humans'
Session.findOne(sessionQuery).exec (err, doc) =>
return @sendDatabaseError(res, err) if err
return @sendSuccess(res, doc) if doc?
@createAndSaveNewSession sessionQuery, req, res
createAndSaveNewSession: (sessionQuery, req, res) =>
initVals = sessionQuery
initVals.state =
complete:false
scripts:
currentScript:null # will not save empty objects
initVals.permissions = [
{
target:req.user.id
access:'owner'
}
{
target:'public'
access:'write'
}
]
session = new Session(initVals)
session.save (err) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, @formatEntity(req, session))
# TODO: tying things like @formatEntity and saveChangesToDocument don't make sense
# associated with the handler, because the handler might return a different type
# of model, like in this case. Refactor to move that logic to the model instead.
getMySessions: (req, res, slugOrID) ->
findParameters = {}
if Handler.isID slugOrID
findParameters["_id"] = slugOrID
else
findParameters["slug"] = slugOrID
selectString = 'original version.major permissions'
query = Level.findOne(findParameters)
.select(selectString)
.lean()
query.exec (err, level) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless level?
sessionQuery =
level:
original: level.original.toString()
majorVersion: level.version.major
creator: req.user._id+''
query = Session.find(sessionQuery).select('-screenshot')
query.exec (err, results) =>
if err then @sendDatabaseError(res, err) else @sendSuccess res, results
getHistogramData: (req, res,slug) ->
query = Session.aggregate [
{$match: {"levelID":slug, "submitted": true, "team":req.query.team}}
{$project: {totalScore: 1, _id: 0}}
]
query.exec (err, data) =>
if err? then return @sendDatabaseError res, err
valueArray = _.pluck data, "totalScore"
@sendSuccess res, valueArray
checkExistence: (req, res, slugOrID) ->
findParameters = {}
if Handler.isID slugOrID
findParameters["_id"] = slugOrID
else
findParameters["slug"] = slugOrID
selectString = 'original version.major permissions'
query = Level.findOne(findParameters)
.select(selectString)
.lean()
query.exec (err, level) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless level?
res.send({"exists":true})
res.end()
getLeaderboard: (req, res, id) ->
sessionsQueryParameters = @makeLeaderboardQueryParameters(req, id)
sortParameters =
"totalScore": req.query.order
selectProperties = ['totalScore', 'creatorName', 'creator']
query = Session
.find(sessionsQueryParameters)
.limit(req.query.limit)
.sort(sortParameters)
.select(selectProperties.join ' ')
query.exec (err, resultSessions) =>
return @sendDatabaseError(res, err) if err
resultSessions ?= []
@sendSuccess res, resultSessions
getMyLeaderboardRank: (req, res, id) ->
req.query.order = 1
sessionsQueryParameters = @makeLeaderboardQueryParameters(req, id)
Session.count sessionsQueryParameters, (err, count) =>
return @sendDatabaseError(res, err) if err
res.send JSON.stringify(count + 1)
makeLeaderboardQueryParameters: (req, id) ->
@validateLeaderboardRequestParameters req
[original, version] = id.split '.'
version = parseInt(version) ? 0
scoreQuery = {}
scoreQuery[if req.query.order is 1 then "$gt" else "$lt"] = req.query.scoreOffset
query =
level:
original: original
majorVersion: version
team: req.query.team
totalScore: scoreQuery
submitted: true
query
validateLeaderboardRequestParameters: (req) ->
req.query.order = parseInt(req.query.order) ? -1
req.query.scoreOffset = parseFloat(req.query.scoreOffset) ? 100000
req.query.team ?= 'humans'
req.query.limit = parseInt(req.query.limit) ? 20
getLeaderboardFacebookFriends: (req, res, id) -> @getLeaderboardFriends(req, res, id, 'facebookID')
getLeaderboardGPlusFriends: (req, res, id) -> @getLeaderboardFriends(req, res, id, 'gplusID')
getLeaderboardFriends: (req, res, id, serviceProperty) ->
friendIDs = req.body.friendIDs or []
return res.send([]) unless friendIDs.length
q = {}
q[serviceProperty] = {$in:friendIDs}
query = User.find(q).select("#{serviceProperty} name").lean()
query.exec (err, userResults) ->
return res.send([]) unless userResults.length
[id, version] = id.split('.')
userIDs = (r._id+'' for r in userResults)
q = {'level.original':id, 'level.majorVersion': parseInt(version), creator: {$in:userIDs}, totalScore:{$exists:true}}
query = Session.find(q)
.select('creator creatorName totalScore team')
.lean()
query.exec (err, sessionResults) ->
return res.send([]) unless sessionResults.length
userMap = {}
userMap[u._id] = u[serviceProperty] for u in userResults
session[serviceProperty] = userMap[session.creator] for session in sessionResults
res.send(sessionResults)
getRandomSessionPair: (req, res, slugOrID) ->
findParameters = {}
if Handler.isID slugOrID
findParameters["_id"] = slugOrID
else
findParameters["slug"] = slugOrID
selectString = 'original version'
query = Level.findOne(findParameters)
.select(selectString)
.lean()
query.exec (err, level) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless level?
sessionsQueryParameters =
level:
original: level.original.toString()
majorVersion: level.version.major
submitted:true
query = Session
.find(sessionsQueryParameters)
.select('team')
.lean()
query.exec (err, resultSessions) =>
return @sendDatabaseError res, err if err? or not resultSessions
teamSessions = _.groupBy resultSessions, 'team'
sessions = []
numberOfTeams = 0
for team of teamSessions
numberOfTeams += 1
sessions.push _.sample(teamSessions[team])
if numberOfTeams != 2 then return @sendDatabaseError res, "There aren't sessions of 2 teams, so cannot choose random opponents!"
@sendSuccess res, sessions
getFeedback: (req, res, id) ->
return @sendNotFoundError(res) unless req.user
@fetchLevelByIDAndHandleErrors id, req, res, (err, level) =>
feedbackQuery =
creator: mongoose.Types.ObjectId(req.user.id.toString())
'level.original': level.original.toString()
'level.majorVersion': level.version.major
Feedback.findOne(feedbackQuery).exec (err, doc) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless doc?
@sendSuccess(res, doc)
module.exports = new LevelHandler()