A few poll fixes.

This commit is contained in:
Nick Winter 2015-03-10 09:45:21 -07:00
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

View file

@ -118,6 +118,7 @@ module.exports = class User extends CocoModel
@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.
# ... and stopped working well until another update on March 10, so maybe March 11+...
getFourthLevelGroup: ->
return @fourthLevelGroup if @fourthLevelGroup
group = me.get('testGroupNumber') % 8

View file

@ -118,14 +118,16 @@
.progress-bar
background-color: rgb(245, 170, 49)
border-radius: 10px
border: 3px solid rgb(45, 36, 29)
@include transition(none)
&.votes-cell
max-width: 34px
.vote-percentage.badge
.badge
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)
tr

View file

@ -23,6 +23,9 @@ block modal-body-content
.progress-bar
td.votes-cell
span.badge.vote-percentage
if me.isAdmin()
td.votes-cell
span.badge.vote-count
.random-gems-container-wrapper
.random-gems-container

View file

@ -66,6 +66,7 @@ module.exports = class PollModal extends ModalView
votePercentage = Math.round(100 * votes / totalVotes) + '%'
$answer.find('.progress-bar').css('width', '0%').animate({width: widthPercentage}, 'slow')
$answer.find('.vote-percentage').text votePercentage
$answer.find('.vote-count').text votes if me.isAdmin()
@trigger 'vote-updated'

View file

@ -8,6 +8,7 @@ Handler = require '../commons/Handler'
LocalMongo = require '../../app/lib/LocalMongo'
util = require '../../app/core/utils'
LevelSession = require '../levels/sessions/LevelSession'
UserPollsRecord = require '../polls/UserPollsRecord'
class EarnedAchievementHandler extends Handler
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
userID = user.get('_id').toHexString()
# Fetch all of a user's earned achievements
EarnedAchievement.find {user: userID}, (err, alreadyEarned) ->
alreadyEarnedIDs = []
previousPoints = 0
previousRewards = 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
alreadyEarnedIDs.push earned.get('achievement') + ''
previousPoints += earned.get 'earnedPoints'
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
# Fetch a user's poll record so we can get the gems they should have from that.
UserPollsRecord.findOne {user: userID}, (err, userPollsRecord) ->
log.error err if err
pollGems = 0
for pollID, reward of userPollsRecord?.get('rewards') or {}
pollGems += Math.ceil 2 * reward.random * reward.level
# Fetch all of a user's earned achievements
EarnedAchievement.find {user: userID}, (err, alreadyEarned) ->
log.error err if err
# TODO maybe also delete earned? Make sure you don't delete too many
newTotalPoints = 0
newTotalRewards = heroes: [], items: [], levels: [], gems: 0
async.each achievements, ((achievement, doneWithAchievement) ->
isRepeatable = achievement.get('proportionalTo')?
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')
alreadyEarnedIDs = []
previousPoints = 0
previousRewards = 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
alreadyEarnedIDs.push earned.get('achievement') + ''
previousPoints += earned.get 'earnedPoints'
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
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
#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
update = {$inc: {points: pointDelta}}
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
# TODO maybe also delete earned? Make sure you don't delete too many
newTotalPoints = 0
newTotalRewards = heroes: [], items: [], levels: [], gems: 0
async.each achievements, ((achievement, doneWithAchievement) ->
isRepeatable = achievement.get('proportionalTo')?
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
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
if recalculatingAll
update.$set[updateKey] = _.uniq rewards
update = {$inc: {points: pointDelta}}
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
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
doneWithUser user
module.exports = new EarnedAchievementHandler()

View file

@ -32,7 +32,7 @@ PollHandler = class PollHandler extends Handler
@sendSuccess res, @formatEntity(req, poll)
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) ->
callback err, poll?.priority