mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 17:45:40 -05:00
Recalculate script mainly finished
This commit is contained in:
parent
bea751eed9
commit
bd2b289943
5 changed files with 102 additions and 10 deletions
|
@ -25,5 +25,6 @@ module.exports =
|
||||||
created: type: 'date'
|
created: type: 'date'
|
||||||
changed: type: 'date'
|
changed: type: 'date'
|
||||||
achievedAmount: type: 'number'
|
achievedAmount: type: 'number'
|
||||||
|
earnedPoints: type: 'number'
|
||||||
previouslyAchievedAmount: {type: 'number', default: 0}
|
previouslyAchievedAmount: {type: 'number', default: 0}
|
||||||
notified: type: 'boolean'
|
notified: type: 'boolean'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mongoose = require 'mongoose'
|
mongoose = require 'mongoose'
|
||||||
jsonschema = require '../../app/schemas/models/earned_achievement'
|
jsonschema = require '../../app/schemas/models/earned_achievement'
|
||||||
|
User = require '../users/User'
|
||||||
|
|
||||||
EarnedAchievementSchema = new mongoose.Schema({
|
EarnedAchievementSchema = new mongoose.Schema({
|
||||||
created:
|
created:
|
||||||
|
@ -20,8 +21,6 @@ EarnedAchievementSchema.pre 'save', (next) ->
|
||||||
EarnedAchievementSchema.index({user: 1, achievement: 1}, {unique: true, name: 'earned achievement index'})
|
EarnedAchievementSchema.index({user: 1, achievement: 1}, {unique: true, name: 'earned achievement index'})
|
||||||
EarnedAchievementSchema.index({user: 1, changed: -1}, {name: 'latest '})
|
EarnedAchievementSchema.index({user: 1, changed: -1}, {name: 'latest '})
|
||||||
|
|
||||||
EarnedAchievementSchema.static 'recalculate', (callback) ->
|
|
||||||
callback('pass')
|
|
||||||
|
|
||||||
module.exports = EarnedAchievement = mongoose.model('EarnedAchievement', EarnedAchievementSchema)
|
module.exports = EarnedAchievement = mongoose.model('EarnedAchievement', EarnedAchievementSchema)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,104 @@
|
||||||
log = require 'winston'
|
log = require 'winston'
|
||||||
mongoose = require('mongoose')
|
mongoose = require 'mongoose'
|
||||||
|
async = require 'async'
|
||||||
|
Achievement = require './Achievement'
|
||||||
EarnedAchievement = require './EarnedAchievement'
|
EarnedAchievement = require './EarnedAchievement'
|
||||||
|
User = require '../users/User'
|
||||||
Handler = require '../commons/Handler'
|
Handler = require '../commons/Handler'
|
||||||
|
LocalMongo = require '../../app/lib/LocalMongo'
|
||||||
|
|
||||||
class EarnedAchievementHandler extends Handler
|
class EarnedAchievementHandler extends Handler
|
||||||
modelClass: EarnedAchievement
|
modelClass: EarnedAchievement
|
||||||
|
|
||||||
# Don't allow POSTs or anything yet
|
# Don't allow POSTs or anything yet
|
||||||
hasAccess: (req) ->
|
hasAccess: (req) ->
|
||||||
req.method is 'GET'
|
req.method is 'GET' # or req.user.isAdmin()
|
||||||
|
|
||||||
recalculate: (req, res) ->
|
recalculate: (req, res) ->
|
||||||
EarnedAchievement.recalculate (data) => @sendSuccess(res, data)
|
onSuccess = (data) => @sendSuccess(res, data)
|
||||||
|
if 'achievements' of req.query # Support both slugs and IDs separated by commas
|
||||||
|
achievementSlugsOrIDs = req.query.id.split(',')
|
||||||
|
EarnedAchievementHandler.recalculate achievementSlugsOrIDs, onSuccess
|
||||||
|
else
|
||||||
|
EarnedAchievementHandler.recalculate onSuccess
|
||||||
|
@sendSuccess res
|
||||||
|
|
||||||
|
# Returns success: boolean
|
||||||
|
@recalculate: (callbackOrSlugsOrIDs, callback) ->
|
||||||
|
if _.isArray callbackOrSlugsOrIDs
|
||||||
|
achievementSlugs = (thing unless Handler.isID(thing) for thing in callbackOrSlugsOrIDs)
|
||||||
|
achievementIDs = (thing if Handler.isID(thing) for thing in callbackOrSlugsOrIDs)
|
||||||
|
else
|
||||||
|
callback = callbackOrSlugsOrIDs
|
||||||
|
|
||||||
|
filter = {}
|
||||||
|
filter.$or = [
|
||||||
|
_id: $in: achievementIDs
|
||||||
|
slug: $in: achievementSlugs
|
||||||
|
] if achievementSlugs? or achievementIDs?
|
||||||
|
|
||||||
|
Achievement.find filter, (err, achievements) ->
|
||||||
|
return false and log.error err if err?
|
||||||
|
User.find {}, (err, users) ->
|
||||||
|
_.each users, (user) ->
|
||||||
|
# Keep track of a user's already achieved so as to set the notified values correctly
|
||||||
|
userID = user.get('_id').toHexString()
|
||||||
|
EarnedAchievement.find {user: userID}, (err, alreadyEarned) ->
|
||||||
|
alreadyEarnedIDs = []
|
||||||
|
previousPoints = 0
|
||||||
|
_.each alreadyEarned, (earned) ->
|
||||||
|
alreadyEarnedIDs.push earned.get('achievement')
|
||||||
|
previousPoints += earned.get 'earnedPoints'
|
||||||
|
|
||||||
|
# TODO maybe also delete earned? Make sure you don't delete too many
|
||||||
|
|
||||||
|
newTotalPoints = 0
|
||||||
|
|
||||||
|
earnedAchievementSaverGenerator = (achievement) -> (callback) ->
|
||||||
|
log.debug 'Checking out tha fancy achievement'
|
||||||
|
isRepeatable = achievement.get('proportionalTo')?
|
||||||
|
model = mongoose.model(achievement.get('collection'))
|
||||||
|
if not model?
|
||||||
|
log.error "Model #{achievement.get 'collection'} doesn't even exist."
|
||||||
|
return callback()
|
||||||
|
|
||||||
|
model.findOne achievement.query, (err, something) ->
|
||||||
|
return callback() unless something
|
||||||
|
|
||||||
|
log.debug "Matched an achievement: #{achievement.get 'name'}"
|
||||||
|
|
||||||
|
earned =
|
||||||
|
user: userID
|
||||||
|
achievement: achievement._id.toHexString()
|
||||||
|
achievementName: achievement.get 'name'
|
||||||
|
notified: achievement._id in alreadyEarnedIDs
|
||||||
|
|
||||||
|
if isRepeatable
|
||||||
|
earned.achievedAmount = something.get(achievement.get 'proportionalTo')
|
||||||
|
earned.previouslyAchievedAmount = 0
|
||||||
|
|
||||||
|
expFunction = achievement.getExpFunction()
|
||||||
|
newTotalPoints += expFunction(earned.achievedAmount) * achievement.get('worth')
|
||||||
|
else
|
||||||
|
newTotalPoints += achievement.get 'worth'
|
||||||
|
|
||||||
|
EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) ->
|
||||||
|
log.error err if err?
|
||||||
|
callback()
|
||||||
|
|
||||||
|
saveUserPoints = (callback) ->
|
||||||
|
# In principle it is enough to deduct the old amount of points and add the new amount,
|
||||||
|
# but just to be entirely safe let's start from 0 in case we're updating all of a user's achievements
|
||||||
|
log.debug "Matched a total of #{newTotalPoints} new points"
|
||||||
|
if _.isEmpty filter # Completely clean
|
||||||
|
User.update {_id: userID}, {$set: points: newTotalPoints}, {}, (err) -> log.error err if err?
|
||||||
|
else
|
||||||
|
User.update {_id: userID}, {$inc: points: newTotalPoints - previousPoints}, {}, (err) -> log.error err if err?
|
||||||
|
|
||||||
|
earnedAchievementSavers = (earnedAchievementSaverGenerator(achievement) for achievement in achievements)
|
||||||
|
earnedAchievementSavers.push saveUserPoints
|
||||||
|
|
||||||
|
async.series earnedAchievementSavers
|
||||||
|
|
||||||
|
|
||||||
module.exports = new EarnedAchievementHandler()
|
module.exports = new EarnedAchievementHandler()
|
||||||
|
|
|
@ -73,17 +73,18 @@ module.exports = AchievablePlugin = (schema, options) ->
|
||||||
expFunction = achievement.getExpFunction()
|
expFunction = achievement.getExpFunction()
|
||||||
earned.notified = false
|
earned.notified = false
|
||||||
earned.achievedAmount = newAmount
|
earned.achievedAmount = newAmount
|
||||||
|
earned.earnedPoints = (expFunction(newAmount) - expFunction(originalAmount)) * worth
|
||||||
earned.previouslyAchievedAmount = originalAmount
|
earned.previouslyAchievedAmount = originalAmount
|
||||||
EarnedAchievement.findOneAndUpdate({achievement:earned.achievement, user:earned.user}, earned, upsert:true, (err, docs) ->
|
EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) ->
|
||||||
return log.debug err if err?
|
return log.debug err if err?
|
||||||
)
|
|
||||||
|
|
||||||
earnedPoints = (expFunction(newAmount) - expFunction(originalAmount)) * worth
|
earnedPoints = earned.earnedPoints
|
||||||
log.debug earnedPoints
|
log.debug earnedPoints
|
||||||
wrapUp()
|
wrapUp()
|
||||||
|
|
||||||
else # not alreadyAchieved
|
else # not alreadyAchieved
|
||||||
log.debug 'Creating a new earned achievement called \'' + (achievement.get 'name') + '\' for ' + userID
|
log.debug 'Creating a new earned achievement called \'' + (achievement.get 'name') + '\' for ' + userID
|
||||||
|
earned.earnedPoints = worth
|
||||||
(new EarnedAchievement(earned)).save (err, doc) ->
|
(new EarnedAchievement(earned)).save (err, doc) ->
|
||||||
return log.debug err if err?
|
return log.debug err if err?
|
||||||
earnedPoints = worth
|
earnedPoints = worth
|
||||||
|
|
|
@ -2,8 +2,11 @@ log = require 'winston'
|
||||||
errors = require '../commons/errors'
|
errors = require '../commons/errors'
|
||||||
handlers = require('../commons/mapping').handlers
|
handlers = require('../commons/mapping').handlers
|
||||||
|
|
||||||
|
mongoose = require('mongoose')
|
||||||
|
|
||||||
module.exports.setup = (app) ->
|
module.exports.setup = (app) ->
|
||||||
app.all '/admin/*', (req, res) ->
|
app.post '/admin/*', (req, res) ->
|
||||||
|
# TODO apparently I can leave this out as long as I use res.send
|
||||||
res.setHeader('Content-Type', 'application/json')
|
res.setHeader('Content-Type', 'application/json')
|
||||||
|
|
||||||
module = req.path[7..]
|
module = req.path[7..]
|
||||||
|
|
Loading…
Reference in a new issue