mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-05-02 17:03:42 -04:00
A few poll fixes.
This commit is contained in:
parent
8c1de9dfa2
commit
9c99fc455a
6 changed files with 132 additions and 116 deletions
app
models
styles/play/modal
templates/play/modal
views/play/modal
server
|
@ -118,6 +118,7 @@ module.exports = class User extends CocoModel
|
||||||
@announcesActionAudioGroup
|
@announcesActionAudioGroup
|
||||||
|
|
||||||
# Signs and Portents was receiving updates after test started, and also had a big bug on March 4, so just look at test from March 5 on.
|
# Signs and Portents was receiving updates after test started, and also had a big bug on March 4, so just look at test from March 5 on.
|
||||||
|
# ... and stopped working well until another update on March 10, so maybe March 11+...
|
||||||
getFourthLevelGroup: ->
|
getFourthLevelGroup: ->
|
||||||
return @fourthLevelGroup if @fourthLevelGroup
|
return @fourthLevelGroup if @fourthLevelGroup
|
||||||
group = me.get('testGroupNumber') % 8
|
group = me.get('testGroupNumber') % 8
|
||||||
|
|
|
@ -118,14 +118,16 @@
|
||||||
|
|
||||||
.progress-bar
|
.progress-bar
|
||||||
background-color: rgb(245, 170, 49)
|
background-color: rgb(245, 170, 49)
|
||||||
|
border-radius: 10px
|
||||||
|
border: 3px solid rgb(45, 36, 29)
|
||||||
@include transition(none)
|
@include transition(none)
|
||||||
|
|
||||||
&.votes-cell
|
&.votes-cell
|
||||||
max-width: 34px
|
max-width: 34px
|
||||||
|
|
||||||
.vote-percentage.badge
|
.badge
|
||||||
background-color: rgb(245, 170, 49)
|
background-color: rgb(245, 170, 49)
|
||||||
text-shadow: -1px -1px 0px black, 1px 1px 0px black, -1px 1px 0px black, 1px -1px 0px black
|
color: rgb(45, 36, 29)
|
||||||
|
|
||||||
table:not(.answered)
|
table:not(.answered)
|
||||||
tr
|
tr
|
||||||
|
|
|
@ -23,6 +23,9 @@ block modal-body-content
|
||||||
.progress-bar
|
.progress-bar
|
||||||
td.votes-cell
|
td.votes-cell
|
||||||
span.badge.vote-percentage
|
span.badge.vote-percentage
|
||||||
|
if me.isAdmin()
|
||||||
|
td.votes-cell
|
||||||
|
span.badge.vote-count
|
||||||
|
|
||||||
.random-gems-container-wrapper
|
.random-gems-container-wrapper
|
||||||
.random-gems-container
|
.random-gems-container
|
||||||
|
|
|
@ -66,6 +66,7 @@ module.exports = class PollModal extends ModalView
|
||||||
votePercentage = Math.round(100 * votes / totalVotes) + '%'
|
votePercentage = Math.round(100 * votes / totalVotes) + '%'
|
||||||
$answer.find('.progress-bar').css('width', '0%').animate({width: widthPercentage}, 'slow')
|
$answer.find('.progress-bar').css('width', '0%').animate({width: widthPercentage}, 'slow')
|
||||||
$answer.find('.vote-percentage').text votePercentage
|
$answer.find('.vote-percentage').text votePercentage
|
||||||
|
$answer.find('.vote-count').text votes if me.isAdmin()
|
||||||
|
|
||||||
@trigger 'vote-updated'
|
@trigger 'vote-updated'
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ Handler = require '../commons/Handler'
|
||||||
LocalMongo = require '../../app/lib/LocalMongo'
|
LocalMongo = require '../../app/lib/LocalMongo'
|
||||||
util = require '../../app/core/utils'
|
util = require '../../app/core/utils'
|
||||||
LevelSession = require '../levels/sessions/LevelSession'
|
LevelSession = require '../levels/sessions/LevelSession'
|
||||||
|
UserPollsRecord = require '../polls/UserPollsRecord'
|
||||||
|
|
||||||
class EarnedAchievementHandler extends Handler
|
class EarnedAchievementHandler extends Handler
|
||||||
modelClass: EarnedAchievement
|
modelClass: EarnedAchievement
|
||||||
|
@ -191,126 +192,134 @@ class EarnedAchievementHandler extends Handler
|
||||||
# Keep track of a user's already achieved in order to set the notified values correctly
|
# Keep track of a user's already achieved in order to set the notified values correctly
|
||||||
userID = user.get('_id').toHexString()
|
userID = user.get('_id').toHexString()
|
||||||
|
|
||||||
# Fetch all of a user's earned achievements
|
# Fetch a user's poll record so we can get the gems they should have from that.
|
||||||
EarnedAchievement.find {user: userID}, (err, alreadyEarned) ->
|
UserPollsRecord.findOne {user: userID}, (err, userPollsRecord) ->
|
||||||
alreadyEarnedIDs = []
|
log.error err if err
|
||||||
previousPoints = 0
|
pollGems = 0
|
||||||
previousRewards = heroes: [], items: [], levels: [], gems: 0
|
for pollID, reward of userPollsRecord?.get('rewards') or {}
|
||||||
async.each alreadyEarned, ((earned, doneWithEarned) ->
|
pollGems += Math.ceil 2 * reward.random * reward.level
|
||||||
if (_.find achievements, (single) -> earned.get('achievement') is single.get('_id').toHexString()) # if already earned
|
|
||||||
alreadyEarnedIDs.push earned.get('achievement') + ''
|
# Fetch all of a user's earned achievements
|
||||||
previousPoints += earned.get 'earnedPoints'
|
EarnedAchievement.find {user: userID}, (err, alreadyEarned) ->
|
||||||
for rewardType in ['heroes', 'items', 'levels']
|
|
||||||
previousRewards[rewardType] = previousRewards[rewardType].concat(earned.get('earnedRewards')?[rewardType] ? [])
|
|
||||||
previousRewards.gems += earned.get('earnedRewards')?.gems ? 0
|
|
||||||
doneWithEarned()
|
|
||||||
), (err) -> # After checking already achieved
|
|
||||||
log.error err if err
|
log.error err if err
|
||||||
# TODO maybe also delete earned? Make sure you don't delete too many
|
alreadyEarnedIDs = []
|
||||||
|
previousPoints = 0
|
||||||
newTotalPoints = 0
|
previousRewards = heroes: [], items: [], levels: [], gems: 0
|
||||||
newTotalRewards = heroes: [], items: [], levels: [], gems: 0
|
async.each alreadyEarned, ((earned, doneWithEarned) ->
|
||||||
|
if (_.find achievements, (single) -> earned.get('achievement') is single.get('_id').toHexString()) # if already earned
|
||||||
async.each achievements, ((achievement, doneWithAchievement) ->
|
alreadyEarnedIDs.push earned.get('achievement') + ''
|
||||||
isRepeatable = achievement.get('proportionalTo')?
|
previousPoints += earned.get 'earnedPoints'
|
||||||
model = mongoose.modelNameByCollection(achievement.get('collection'))
|
|
||||||
return doneWithAchievement new Error "Model with collection '#{achievement.get 'collection'}' doesn't exist." unless model?
|
|
||||||
|
|
||||||
finalQuery = _.clone achievement.get 'query'
|
|
||||||
return doneWithAchievement() if _.isEmpty finalQuery
|
|
||||||
finalQuery.$or = [{}, {}] # Allow both ObjectIDs or hex string IDs
|
|
||||||
finalQuery.$or[0][achievement.userField] = userID
|
|
||||||
finalQuery.$or[1][achievement.userField] = mongoose.Types.ObjectId userID
|
|
||||||
|
|
||||||
model.findOne finalQuery, (err, something) ->
|
|
||||||
return doneWithAchievement() if _.isEmpty something
|
|
||||||
|
|
||||||
#log.debug "Matched an achievement: #{achievement.get 'name'} for #{user.get 'name'}"
|
|
||||||
|
|
||||||
earned =
|
|
||||||
user: userID
|
|
||||||
achievement: achievement._id.toHexString()
|
|
||||||
achievementName: achievement.get 'name'
|
|
||||||
notified: achievement._id.toHexString() in alreadyEarnedIDs
|
|
||||||
|
|
||||||
if isRepeatable
|
|
||||||
earned.achievedAmount = util.getByPath(something.toObject(), achievement.get 'proportionalTo') or 0
|
|
||||||
earned.previouslyAchievedAmount = 0
|
|
||||||
|
|
||||||
expFunction = achievement.getExpFunction()
|
|
||||||
newPoints = expFunction(earned.achievedAmount) * achievement.get('worth') ? 10
|
|
||||||
newGems = expFunction(earned.achievedAmount) * (achievement.get('rewards')?.gems ? 0)
|
|
||||||
else
|
|
||||||
newPoints = achievement.get('worth') ? 10
|
|
||||||
newGems = achievement.get('rewards')?.gems ? 0
|
|
||||||
|
|
||||||
earned.earnedPoints = newPoints
|
|
||||||
newTotalPoints += newPoints
|
|
||||||
|
|
||||||
earned.earnedRewards = achievement.get('rewards')
|
|
||||||
for rewardType in ['heroes', 'items', 'levels']
|
for rewardType in ['heroes', 'items', 'levels']
|
||||||
newTotalRewards[rewardType] = newTotalRewards[rewardType].concat(achievement.get('rewards')?[rewardType] ? [])
|
previousRewards[rewardType] = previousRewards[rewardType].concat(earned.get('earnedRewards')?[rewardType] ? [])
|
||||||
if isRepeatable and earned.earnedRewards
|
previousRewards.gems += earned.get('earnedRewards')?.gems ? 0
|
||||||
earned.earnedRewards = _.clone earned.earnedRewards
|
doneWithEarned()
|
||||||
earned.earnedRewards.gems = newGems
|
), (err) -> # After checking already achieved
|
||||||
newTotalRewards.gems += newGems
|
|
||||||
|
|
||||||
EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) ->
|
|
||||||
doneWithAchievement err
|
|
||||||
), (err) -> # Wrap up a user, save points
|
|
||||||
log.error err if err
|
log.error err if err
|
||||||
#console.log 'User', user.get('name'), 'had newTotalPoints', newTotalPoints, 'and newTotalRewards', newTotalRewards, 'previousRewards', previousRewards
|
# TODO maybe also delete earned? Make sure you don't delete too many
|
||||||
return doneWithUser(user) unless newTotalPoints or newTotalRewards.gems or _.some(newTotalRewards, (r) -> r.length)
|
|
||||||
#log.debug "Matched a total of #{newTotalPoints} new points"
|
newTotalPoints = 0
|
||||||
#log.debug "Incrementing score for these achievements with #{newTotalPoints - previousPoints}"
|
newTotalRewards = heroes: [], items: [], levels: [], gems: 0
|
||||||
pointDelta = newTotalPoints - previousPoints
|
|
||||||
pctDone = (100 * usersFinished / total).toFixed(2)
|
async.each achievements, ((achievement, doneWithAchievement) ->
|
||||||
console.log "Updated points to #{newTotalPoints} (#{if pointDelta < 0 then '' else '+'}#{pointDelta}) for #{user.get('name') or '???'} (#{user.get('_id')}) (#{pctDone}%)"
|
isRepeatable = achievement.get('proportionalTo')?
|
||||||
if recalculatingAll
|
model = mongoose.modelNameByCollection(achievement.get('collection'))
|
||||||
update = {$set: {points: newTotalPoints, 'earned.gems': 0, 'earned.heroes': [], 'earned.items': [], 'earned.levels': []}}
|
return doneWithAchievement new Error "Model with collection '#{achievement.get 'collection'}' doesn't exist." unless model?
|
||||||
else
|
|
||||||
update = {$inc: {points: pointDelta}}
|
finalQuery = _.clone achievement.get 'query'
|
||||||
secondUpdate = {} # In case we need to pull, then push.
|
return doneWithAchievement() if _.isEmpty finalQuery
|
||||||
for rewardType, rewards of newTotalRewards
|
finalQuery.$or = [{}, {}] # Allow both ObjectIDs or hex string IDs
|
||||||
updateKey = "earned.#{rewardType}"
|
finalQuery.$or[0][achievement.userField] = userID
|
||||||
if rewardType is 'gems'
|
finalQuery.$or[1][achievement.userField] = mongoose.Types.ObjectId userID
|
||||||
if recalculatingAll
|
|
||||||
update.$set[updateKey] = rewards
|
model.findOne finalQuery, (err, something) ->
|
||||||
|
return doneWithAchievement() if _.isEmpty something
|
||||||
|
|
||||||
|
#log.debug "Matched an achievement: #{achievement.get 'name'} for #{user.get 'name'}"
|
||||||
|
|
||||||
|
earned =
|
||||||
|
user: userID
|
||||||
|
achievement: achievement._id.toHexString()
|
||||||
|
achievementName: achievement.get 'name'
|
||||||
|
notified: achievement._id.toHexString() in alreadyEarnedIDs
|
||||||
|
|
||||||
|
if isRepeatable
|
||||||
|
earned.achievedAmount = util.getByPath(something.toObject(), achievement.get 'proportionalTo') or 0
|
||||||
|
earned.previouslyAchievedAmount = 0
|
||||||
|
|
||||||
|
expFunction = achievement.getExpFunction()
|
||||||
|
newPoints = expFunction(earned.achievedAmount) * achievement.get('worth') ? 10
|
||||||
|
newGems = expFunction(earned.achievedAmount) * (achievement.get('rewards')?.gems ? 0)
|
||||||
else
|
else
|
||||||
update.$inc[updateKey] = rewards - previousRewards.gems
|
newPoints = achievement.get('worth') ? 10
|
||||||
|
newGems = achievement.get('rewards')?.gems ? 0
|
||||||
|
|
||||||
|
earned.earnedPoints = newPoints
|
||||||
|
newTotalPoints += newPoints
|
||||||
|
|
||||||
|
earned.earnedRewards = achievement.get('rewards')
|
||||||
|
for rewardType in ['heroes', 'items', 'levels']
|
||||||
|
newTotalRewards[rewardType] = newTotalRewards[rewardType].concat(achievement.get('rewards')?[rewardType] ? [])
|
||||||
|
if isRepeatable and earned.earnedRewards
|
||||||
|
earned.earnedRewards = _.clone earned.earnedRewards
|
||||||
|
earned.earnedRewards.gems = newGems
|
||||||
|
newTotalRewards.gems += newGems
|
||||||
|
|
||||||
|
EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) ->
|
||||||
|
doneWithAchievement err
|
||||||
|
), (err) -> # Wrap up a user, save points
|
||||||
|
log.error err if err
|
||||||
|
#console.log 'User', user.get('name'), 'had newTotalPoints', newTotalPoints, 'and newTotalRewards', newTotalRewards, 'previousRewards', previousRewards
|
||||||
|
return doneWithUser(user) unless newTotalPoints or newTotalRewards.gems or _.some(newTotalRewards, (r) -> r.length)
|
||||||
|
#log.debug "Matched a total of #{newTotalPoints} new points"
|
||||||
|
#log.debug "Incrementing score for these achievements with #{newTotalPoints - previousPoints}"
|
||||||
|
pointDelta = newTotalPoints - previousPoints
|
||||||
|
pctDone = (100 * usersFinished / total).toFixed(2)
|
||||||
|
console.log "Updated points to #{newTotalPoints} (#{if pointDelta < 0 then '' else '+'}#{pointDelta}) for #{user.get('name') or '???'} (#{user.get('_id')}) (#{pctDone}%)"
|
||||||
|
if recalculatingAll
|
||||||
|
update = {$set: {points: newTotalPoints, 'earned.gems': 0, 'earned.heroes': [], 'earned.items': [], 'earned.levels': []}}
|
||||||
else
|
else
|
||||||
if recalculatingAll
|
update = {$inc: {points: pointDelta}}
|
||||||
update.$set[updateKey] = _.uniq rewards
|
secondUpdate = {} # In case we need to pull, then push.
|
||||||
|
for rewardType, rewards of newTotalRewards
|
||||||
|
updateKey = "earned.#{rewardType}"
|
||||||
|
if rewardType is 'gems'
|
||||||
|
if recalculatingAll
|
||||||
|
update.$set[updateKey] = rewards + pollGems
|
||||||
|
else
|
||||||
|
update.$inc[updateKey] = rewards - previousRewards.gems
|
||||||
|
else
|
||||||
|
if recalculatingAll
|
||||||
|
update.$set[updateKey] = _.uniq rewards
|
||||||
|
else
|
||||||
|
previousCounts = _.countBy previousRewards[rewardType]
|
||||||
|
newCounts = _.countBy rewards
|
||||||
|
relevantRewards = _.union _.keys(previousCounts), _.keys(newCounts)
|
||||||
|
for reward in relevantRewards
|
||||||
|
[previousCount, newCount] = [previousCounts[reward], newCounts[reward]]
|
||||||
|
if newCount and not previousCount
|
||||||
|
update.$addToSet ?= {}
|
||||||
|
update.$addToSet[updateKey] ?= {$each: []}
|
||||||
|
update.$addToSet[updateKey].$each.push reward
|
||||||
|
else if previousCount and not newCount
|
||||||
|
# Might $pull $each also work here?
|
||||||
|
update.$pullAll ?= {}
|
||||||
|
update.$pullAll[updateKey] ?= []
|
||||||
|
update.$pullAll[updateKey].push reward
|
||||||
|
if update.$addToSet?[updateKey] and update.$pullAll?[updateKey]
|
||||||
|
# Perform the update in two calls to avoid "MongoError: Cannot update 'earned.levels' and 'earned.levels' at the same time"
|
||||||
|
secondUpdate.$addToSet ?= {}
|
||||||
|
secondUpdate.$addToSet[updateKey] = update.$addToSet[updateKey]
|
||||||
|
delete update.$addToSet[updateKey]
|
||||||
|
delete update.$addToSet unless _.size update.$addToSet
|
||||||
|
#console.log 'recalculatingAll?', recalculatingAll, 'so update is', update, 'secondUpdate', secondUpdate
|
||||||
|
User.update {_id: userID}, update, {}, (err) ->
|
||||||
|
log.error err if err?
|
||||||
|
if _.size secondUpdate
|
||||||
|
User.update {_id: userID}, secondUpdate, {}, (err) ->
|
||||||
|
log.error err if err?
|
||||||
|
doneWithUser user
|
||||||
else
|
else
|
||||||
previousCounts = _.countBy previousRewards[rewardType]
|
|
||||||
newCounts = _.countBy rewards
|
|
||||||
relevantRewards = _.union _.keys(previousCounts), _.keys(newCounts)
|
|
||||||
for reward in relevantRewards
|
|
||||||
[previousCount, newCount] = [previousCounts[reward], newCounts[reward]]
|
|
||||||
if newCount and not previousCount
|
|
||||||
update.$addToSet ?= {}
|
|
||||||
update.$addToSet[updateKey] ?= {$each: []}
|
|
||||||
update.$addToSet[updateKey].$each.push reward
|
|
||||||
else if previousCount and not newCount
|
|
||||||
# Might $pull $each also work here?
|
|
||||||
update.$pullAll ?= {}
|
|
||||||
update.$pullAll[updateKey] ?= []
|
|
||||||
update.$pullAll[updateKey].push reward
|
|
||||||
if update.$addToSet?[updateKey] and update.$pullAll?[updateKey]
|
|
||||||
# Perform the update in two calls to avoid "MongoError: Cannot update 'earned.levels' and 'earned.levels' at the same time"
|
|
||||||
secondUpdate.$addToSet ?= {}
|
|
||||||
secondUpdate.$addToSet[updateKey] = update.$addToSet[updateKey]
|
|
||||||
delete update.$addToSet[updateKey]
|
|
||||||
delete update.$addToSet unless _.size update.$addToSet
|
|
||||||
#console.log 'recalculatingAll?', recalculatingAll, 'so update is', update, 'secondUpdate', secondUpdate
|
|
||||||
User.update {_id: userID}, update, {}, (err) ->
|
|
||||||
log.error err if err?
|
|
||||||
if _.size secondUpdate
|
|
||||||
User.update {_id: userID}, secondUpdate, {}, (err) ->
|
|
||||||
log.error err if err?
|
|
||||||
doneWithUser user
|
doneWithUser user
|
||||||
else
|
|
||||||
doneWithUser user
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = new EarnedAchievementHandler()
|
module.exports = new EarnedAchievementHandler()
|
||||||
|
|
|
@ -32,7 +32,7 @@ PollHandler = class PollHandler extends Handler
|
||||||
@sendSuccess res, @formatEntity(req, poll)
|
@sendSuccess res, @formatEntity(req, poll)
|
||||||
|
|
||||||
findPollPriority: (lastPollID, callback) ->
|
findPollPriority: (lastPollID, callback) ->
|
||||||
return callback null, -9001 #unless lastPollID
|
return callback null, -9001 unless lastPollID
|
||||||
Poll.findById mongoose.Types.ObjectId(lastPollID), 'priority', {lean: true}, (err, poll) ->
|
Poll.findById mongoose.Types.ObjectId(lastPollID), 'priority', {lean: true}, (err, poll) ->
|
||||||
callback err, poll?.priority
|
callback err, poll?.priority
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue