mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-07-31 19:20:25 +00:00
Merge and fix tests
This commit is contained in:
commit
822f779812
8 changed files with 308 additions and 327 deletions
|
|
@ -45,13 +45,13 @@ get '/', (page, model, params, next) ->
|
|||
|
||||
ready (model) ->
|
||||
user = model.at('_user')
|
||||
scoring.setModel(model)
|
||||
score = new scoring.Scoring(model)
|
||||
|
||||
#set cron immediately
|
||||
lastCron = user.get('lastCron')
|
||||
user.set('lastCron', +new Date) if (!lastCron? or lastCron == 'new')
|
||||
|
||||
scoring.cron()
|
||||
score.cron()
|
||||
|
||||
character.app(exports, model)
|
||||
tasks.app(exports, model)
|
||||
|
|
|
|||
|
|
@ -16,31 +16,8 @@ partyUnsubscribe = (model, cb) ->
|
|||
1) If the user is solo, just subscribe to the user.
|
||||
2) If in a an empty party, just subscribe to the user & party meta.
|
||||
3) If full party, subscribe to everything.
|
||||
###
|
||||
module.exports.partySubscribe = partySubscribe = (page, model, params, next, cb) ->
|
||||
|
||||
# unsubscribe from everything - we're starting over
|
||||
# partyUnsubscribe model, ->
|
||||
|
||||
# Restart subscription to the main user
|
||||
selfQ = model.query('users').withId model.get('_userId') #or model.session.userId # see http://goo.gl/TPYIt
|
||||
selfQ.subscribe (err, self) ->
|
||||
return next(err) if err
|
||||
u = self.at(0)
|
||||
uObj = u.get()
|
||||
|
||||
# Attempted handling for 'party of undefined' error, which is caused by bustedSession (see derby-auth).
|
||||
# Theoretically simply reloading the page should restore model.at('_userId') and the second load should work just fine
|
||||
# bustedSession victims might hit a redirection loop if I'm wrong :/
|
||||
return page.redirect('/') unless uObj
|
||||
|
||||
## (1) User is solo, just return that subscription
|
||||
unless uObj.party?.current?
|
||||
model.ref '_user', u
|
||||
return cb()
|
||||
|
||||
###
|
||||
Note this strange hack - we subscribe to queries incrementally. First self, then party, then party members.
|
||||
Note a strange hack - we subscribe to queries incrementally. First self, then party, then party members.
|
||||
Party members come with limited fields, so you can't hack their stuff. Strangely, subscribing to the members after
|
||||
already subscribing to self limits self's fields to the fields which members are limited to. As a result, we have
|
||||
to re-subscribe to self to get all the fields (otherwise everything breaks). Weirdly, this last subscription doesn't
|
||||
|
|
@ -48,40 +25,48 @@ module.exports.partySubscribe = partySubscribe = (page, model, params, next, cb)
|
|||
|
||||
Another issue: `model.unsubscribe(selfQ)` would seem to mitigate the above, so we at least don't have a stray
|
||||
subscription floating around - but alas, it doesn't seem to work (or at least never calls the callback)
|
||||
###
|
||||
finished = ->
|
||||
# model.unsubscribe selfQ, ->
|
||||
selfQ.subscribe (err, self) ->
|
||||
###
|
||||
module.exports.partySubscribe = partySubscribe = (page, model, params, next, cb) ->
|
||||
|
||||
# unsubscribe from everything - we're starting over
|
||||
# partyUnsubscribe model, ->
|
||||
|
||||
# Restart subscription to the main user
|
||||
selfQ = model.query('users').withId (model.get('_userId') or model.session.userId) # see http://goo.gl/TPYIt
|
||||
selfQ.fetch (err, user) ->
|
||||
return next(err) if err
|
||||
model.ref '_user', self.at(0)
|
||||
return next("User not found - this shouldn't be happening!") unless user.get()
|
||||
|
||||
finished = (descriptors, paths) ->
|
||||
model.subscribe.apply model, descriptors.concat ->
|
||||
[err, refs] = [arguments[0], arguments]
|
||||
return next(err) if err
|
||||
_.each paths, (path, idx) -> model.ref path, refs[idx+1]
|
||||
cb()
|
||||
|
||||
# User in a party
|
||||
partiesQ = model.query('parties').withId(uObj.party.current)
|
||||
partiesQ.fetch (err, res) ->
|
||||
return next(err) if err
|
||||
p = res.at(0)
|
||||
model.ref '_party', p
|
||||
ids = p.get('members')
|
||||
|
||||
# FIXME this is the kicker right here. This isn't getting triggered, and it's the reason why we have to refresh
|
||||
# after every event. Get this working
|
||||
#p.on '*', 'members', (ids) ->
|
||||
# console.log("members listener got called")
|
||||
# debugger
|
||||
# membersSubscribe model, ids
|
||||
# Attempted handling for 'party of undefined' error, which is caused by bustedSession (see derby-auth).
|
||||
# Theoretically simply reloading the page should restore model.at('_userId') and the second load should work just fine
|
||||
# bustedSession victims might hit a redirection loop if I'm wrong :/
|
||||
# return page.redirect('/') unless uObj
|
||||
|
||||
partyId = user.get('party.current')
|
||||
|
||||
# (1) Solo player
|
||||
return finished([selfQ], ['_user']) unless partyId
|
||||
|
||||
# User in a party
|
||||
partyQ = model.query('parties').withId(partyId)
|
||||
partyQ.fetch (err, party) ->
|
||||
return next(err) if err
|
||||
members = party.get('members')
|
||||
|
||||
## (2) Party has no members, just subscribe to the party itself
|
||||
if _.isEmpty(ids)
|
||||
return finished()
|
||||
return finished([partyQ, selfQ], ['_party', '_user']) if _.isEmpty(members)
|
||||
|
||||
else
|
||||
## (3) Party has members, subscribe to those users too
|
||||
membersQ = model.query('users').party(ids)
|
||||
membersQ.fetch (err, members) ->
|
||||
return next(err) if err
|
||||
model.ref '_partyMembers', members
|
||||
finished()
|
||||
membersQ = model.query('users').party(members)
|
||||
return finished [partyQ, membersQ, selfQ], ['_party', '_partyMembers', '_user']
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
|
|
@ -121,7 +106,7 @@ module.exports.app = (appExports, model) ->
|
|||
query = model.query('users').party([id])
|
||||
model.fetch query, (err, res) ->
|
||||
throw err if err
|
||||
u = res.at(0).get()
|
||||
u = res.get()
|
||||
if !u?.id?
|
||||
model.set "_view.partyError", "User with id #{id} not found."
|
||||
return
|
||||
|
|
@ -143,9 +128,9 @@ module.exports.app = (appExports, model) ->
|
|||
user.set 'party.current', partyId
|
||||
# model.push "parties.#{partyId}.members", user.get('id'), -> #FIXME why this not working?
|
||||
model.query('parties').withId(partyId).fetch (err, p) ->
|
||||
members = p.at(0).get('members')
|
||||
members = p.get('members')
|
||||
members.push user.get('id')
|
||||
p.at(0).set 'members', members, ->
|
||||
p.set 'members', members, ->
|
||||
window.location.reload true
|
||||
# partySubscribe model, ->
|
||||
# p = model.at('_party')
|
||||
|
|
@ -174,5 +159,5 @@ module.exports.app = (appExports, model) ->
|
|||
# partyUnsubscribe model, ->
|
||||
# selfQ = model.query('users').withId model.get('_userId') #or model.session.userId # see http://goo.gl/TPYIt
|
||||
# selfQ.subscribe (err, u) ->
|
||||
# model.ref '_user', u.at(0)
|
||||
# model.ref '_user', u
|
||||
# browser.resetDom model
|
||||
|
|
@ -5,21 +5,18 @@ helpers = require './helpers'
|
|||
browser = require './browser'
|
||||
character = require './character'
|
||||
items = require './items'
|
||||
MODIFIER = .02 # each new level, armor, weapon add 2% modifier (this number may change)
|
||||
user = undefined
|
||||
model = undefined
|
||||
|
||||
# This is required by all the functions, make sure it's set before anythign else is called
|
||||
setModel = (m) ->
|
||||
model = m
|
||||
user = model.at('_user')
|
||||
module.exports.Scoring = (model) ->
|
||||
|
||||
###
|
||||
MODIFIER = .02 # each new level, armor, weapon add 2% modifier (this mechanism will change)
|
||||
user = model.at '_user'
|
||||
|
||||
###
|
||||
Calculates Exp & GP modification based on weapon & lvl
|
||||
{value} task.value for gain
|
||||
{modifiers} may manually pass in stats as {weapon, exp}. This is used for testing
|
||||
###
|
||||
expModifier = (value, modifiers = {}) ->
|
||||
###
|
||||
expModifier = (value, modifiers = {}) ->
|
||||
weapon = modifiers.weapon || user.get('items.weapon')
|
||||
lvl = modifiers.lvl || user.get('stats.lvl')
|
||||
dmg = items.items.weapon[weapon].modifier # each new weapon increases exp gain
|
||||
|
|
@ -27,12 +24,12 @@ expModifier = (value, modifiers = {}) ->
|
|||
modified = value + (value * dmg)
|
||||
return modified
|
||||
|
||||
###
|
||||
###
|
||||
Calculates HP-loss modification based on armor & lvl
|
||||
{value} task.value which is hurting us
|
||||
{modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing
|
||||
###
|
||||
hpModifier = (value, modifiers = {}) ->
|
||||
###
|
||||
hpModifier = (value, modifiers = {}) ->
|
||||
armor = modifiers.armor || user.get('items.armor')
|
||||
head = modifiers.head || user.get('items.head')
|
||||
shield = modifiers.shield || user.get('items.shield')
|
||||
|
|
@ -42,25 +39,25 @@ hpModifier = (value, modifiers = {}) ->
|
|||
modified = value - (value * ac)
|
||||
return modified
|
||||
|
||||
###
|
||||
###
|
||||
Calculates the next task.value based on direction
|
||||
For negative values, use a line: something like y=-.1x+1
|
||||
For positibe values, taper off with inverse log: y=.9^x
|
||||
Would love to use inverse log for the whole thing, but after 13 fails it hits infinity. Revisit this formula later
|
||||
{currentValue} the current value of the task, determines it's next value
|
||||
{direction} 'up' or 'down'
|
||||
###
|
||||
taskDeltaFormula = (currentValue, direction) ->
|
||||
###
|
||||
taskDeltaFormula = (currentValue, direction) ->
|
||||
sign = if (direction == "up") then 1 else -1
|
||||
delta = if (currentValue < 0) then (( -0.1 * currentValue + 1 ) * sign) else (( Math.pow(0.9,currentValue) ) * sign)
|
||||
return delta
|
||||
|
||||
###
|
||||
###
|
||||
Updates user stats with new stats. Handles death, leveling up, etc
|
||||
{stats} new stats
|
||||
{update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately
|
||||
###
|
||||
updateStats = (newStats, batch) ->
|
||||
###
|
||||
updateStats = (newStats, batch) ->
|
||||
obj = batch.obj()
|
||||
|
||||
# if user is dead, dont do anything
|
||||
|
|
@ -105,11 +102,11 @@ updateStats = (newStats, batch) ->
|
|||
gp = 0.0 if (!gp? or gp<0)
|
||||
obj.stats.gp = newStats.gp
|
||||
|
||||
# {taskId} task you want to score
|
||||
# {direction} 'up' or 'down'
|
||||
# {times} # times to call score on this task (1 unless cron, usually)
|
||||
# {update} if we're running updates en-mass (eg, cron on server) pass in userObj
|
||||
score = (taskId, direction, times, batch, cron) ->
|
||||
# {taskId} task you want to score
|
||||
# {direction} 'up' or 'down'
|
||||
# {times} # times to call score on this task (1 unless cron, usually)
|
||||
# {update} if we're running updates en-mass (eg, cron on server) pass in userObj
|
||||
score = (taskId, direction, times, batch, cron) ->
|
||||
|
||||
commit = false
|
||||
unless batch?
|
||||
|
|
@ -197,15 +194,15 @@ score = (taskId, direction, times, batch, cron) ->
|
|||
newStats = _.clone batch.obj().stats
|
||||
_.each Object.keys(origStats), (key) -> obj.stats[key] = origStats[key]
|
||||
batch.setStats(newStats)
|
||||
# batch.setStats()
|
||||
# batch.setStats()
|
||||
batch.commit()
|
||||
return delta
|
||||
|
||||
###
|
||||
###
|
||||
At end of day, add value to all incomplete Daily & Todo tasks (further incentive)
|
||||
For incomplete Dailys, deduct experience
|
||||
###
|
||||
cron = () ->
|
||||
###
|
||||
cron = () ->
|
||||
today = +new Date
|
||||
daysPassed = helpers.daysBetween(today, user.get('lastCron'))
|
||||
if daysPassed > 0
|
||||
|
|
@ -264,8 +261,7 @@ cron = () ->
|
|||
setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss
|
||||
|
||||
|
||||
module.exports = {
|
||||
setModel: setModel
|
||||
return {
|
||||
MODIFIER: MODIFIER
|
||||
score: score
|
||||
cron: cron
|
||||
|
|
@ -274,4 +270,4 @@ module.exports = {
|
|||
expModifier: expModifier
|
||||
hpModifier: hpModifier
|
||||
taskDeltaFormula: taskDeltaFormula
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ module.exports.view = (view) ->
|
|||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
score = new scoring.Scoring(model)
|
||||
|
||||
user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) ->
|
||||
return if passed? && passed.cron # Don't do this stuff on cron
|
||||
|
|
@ -36,7 +37,7 @@ module.exports.app = (appExports, model) ->
|
|||
|
||||
# Score the user based on todo task
|
||||
task = user.at("tasks.#{i}")
|
||||
scoring.score(i, direction())
|
||||
score.score(i, direction())
|
||||
|
||||
appExports.addTask = (e, el, next) ->
|
||||
type = $(el).attr('data-task-type')
|
||||
|
|
@ -81,7 +82,7 @@ module.exports.app = (appExports, model) ->
|
|||
return # Cancel. Don't delete, don't hurt user
|
||||
else
|
||||
task.set('type','habit') # hack to make sure it hits HP, instead of performing "undo checkbox"
|
||||
scoring.score(id, direction:'down')
|
||||
score.score(id, direction:'down')
|
||||
|
||||
# prevent accidently deleting long-standing tasks
|
||||
else
|
||||
|
|
@ -171,4 +172,4 @@ module.exports.app = (appExports, model) ->
|
|||
direction = 'up' if direction == 'true/'
|
||||
direction = 'down' if direction == 'false/'
|
||||
task = model.at $(el).parents('li')[0]
|
||||
scoring.score(task.get('id'), direction)
|
||||
score.score(task.get('id'), direction)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ auth = (req, res, next) ->
|
|||
|
||||
query.fetch (err, user) ->
|
||||
return res.json err: err if err
|
||||
user = user.at(0)
|
||||
req.user = user
|
||||
req.userObj = user.get()
|
||||
return res.json 401, NO_USER_FOUND if !req.userObj || _.isEmpty(req.userObj)
|
||||
|
|
@ -109,7 +108,7 @@ router.get '/users/:uid/calendar.ics', (req, res) ->
|
|||
query = model.query('users').withIdAndToken(uid, apiToken)
|
||||
query.fetch (err, result) ->
|
||||
return res.send(400, err) if err
|
||||
tasks = result.at(0).get('tasks')
|
||||
tasks = result.get('tasks')
|
||||
# tasks = result[0].tasks
|
||||
tasksWithDates = _.filter tasks, (task) -> !!task.date
|
||||
return res.send(400, "No events found") if _.isEmpty(tasksWithDates)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ module.exports.app = (appExports, model) ->
|
|||
token = (res) ->
|
||||
console.log(res);
|
||||
$.ajax({
|
||||
type:"POST",
|
||||
url:"/charge",
|
||||
data:res
|
||||
type: "POST",
|
||||
url: "/charge",
|
||||
data: res
|
||||
}).success ->
|
||||
window.location.href = "/"
|
||||
.error (err) ->
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ REST = (store) ->
|
|||
store.query.expose "users", "withIdAndToken", (uid, token) ->
|
||||
@byId(uid)
|
||||
.where('apiToken').equals(token)
|
||||
.one
|
||||
.findOne()
|
||||
|
||||
store.queryAccess "users", "withIdAndToken", (uid, token, accept, err) ->
|
||||
return accept(true) if uid && token
|
||||
|
|
@ -101,6 +101,7 @@ partySystem = (store) ->
|
|||
|
||||
store.query.expose "parties", "withId", (id) ->
|
||||
@where("id").equals(id)
|
||||
.findOne()
|
||||
store.queryAccess "parties", "withId", (id, accept, err) ->
|
||||
# return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@)
|
||||
return accept(false) if derbyAuth.bustedSession(@)
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ describe 'API', ->
|
|||
expect(res.statusCode).to.be 201
|
||||
expect(res.body.id).not.to.be.empty()
|
||||
# Ensure that user owns the newly created object
|
||||
expect(user.at(0).get().tasks[res.body.id]).to.be.an('object')
|
||||
expect(user.get().tasks[res.body.id]).to.be.an('object')
|
||||
done()
|
||||
|
||||
it 'PUT /api/v1/task/:id', (done) ->
|
||||
|
|
@ -128,11 +128,12 @@ describe 'API', ->
|
|||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.send(title: 'a new title')
|
||||
.send(title: 'a new title',text: 'hi')
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
currentUser.tasks[tid].title = 'a new title'
|
||||
currentUser.tasks[tid].text = 'hi'
|
||||
expect(res.body).to.eql currentUser.tasks[tid]
|
||||
done()
|
||||
|
||||
|
|
@ -146,8 +147,7 @@ describe 'API', ->
|
|||
query.fetch (err, user) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
currentUser = user.at(0).get()
|
||||
model.ref '_user', user.at(0)
|
||||
model.ref '_user', user
|
||||
tasks = []
|
||||
for type in ['habit','todo','daily','reward']
|
||||
model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
|
||||
|
|
@ -169,8 +169,7 @@ describe 'API', ->
|
|||
query.fetch (err, user) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
currentUser = user.at(0).get()
|
||||
model.ref '_user', user.at(0)
|
||||
model.ref '_user', user
|
||||
model.refList "_todoList", "_user.tasks", "_user.todoIds"
|
||||
tasks = model.get("_todoList")
|
||||
# Ensure that user owns the tasks
|
||||
|
|
|
|||
Loading…
Reference in a new issue