Merge pull request #5482 from Alys/startDate-fix-2015-06-25

Refactor api.shouldDo and fix https://github.com/HabitRPG/habitrpg/issues/5395
This commit is contained in:
Alys 2015-06-28 07:31:04 +10:00
commit eceae75631
4 changed files with 33 additions and 30 deletions

View file

@ -1922,7 +1922,7 @@ api.subscriptionBlocks =
basic_12mo: months:12, price:48
_.each api.subscriptionBlocks, (b,k)->b.key = k
repeat = {m:true,t:true,w:true,th:true,f:true,s:true,su:true}
# repeat = {m:true,t:true,w:true,th:true,f:true,s:true,su:true}
api.userDefaults =
habits: [
{type: 'habit', text: t('defaultHabit1Text'), value: 0, up: true, down: false, attribute: 'per' }

View file

@ -56,6 +56,10 @@ api.startOfWeek = api.startOfWeek = (options={}) ->
moment(o.now).startOf('week')
api.startOfDay = (options={}) ->
# This is designed for use with any date that has an important time portion (e.g., when comparing the current date-time with the previous cron's date-time for determing if cron should run now).
# It changes the time portion of the date-time to be the Custom Day Start hour, so that the date-time is now the user's correct start of day.
# It SUBTRACTS a day if the date-time's original hour is before CDS (e.g., if your CDS is 5am and it's currently 4am, it's still the previous day).
# This is NOT suitable for manipulating any dates that are displayed to the user as a date with no time portion, such as a Daily's Start Dates (e.g., a Start Date of today shows only the date, so it should be considered to be today even if the hidden time portion is before CDS).
o = sanitizeOptions(options)
dayStart = moment(o.now).startOf('day').add({hours:o.dayStart})
if moment(o.now).hour() < o.dayStart
@ -75,35 +79,34 @@ api.daysSince = (yesterday, options = {}) ->
Should the user do this task on this date, given the task's repeat options and user.preferences.dayStart?
###
api.shouldDo = (day, dailyTask, options = {}) ->
return false unless dailyTask.type == 'daily' && dailyTask.repeat
if !dailyTask.startDate
dailyTask.startDate = moment().toDate()
if dailyTask.startDate instanceof String
dailyTask.startDate = moment(dailyTask.startDate).toDate()
return false unless dailyTask.type == 'daily'
o = sanitizeOptions options
day = api.startOfDay(_.defaults {now:day}, o)
dayOfWeekNum = day.day() # e.g. 1 for Monday if week starts on Mon
startOfDayWithCDSTime = api.startOfDay(_.defaults {now:day}, o) # a moment()
# check if event is today or in the future
hasStartedCheck = day >= api.startOfDay(_.defaults {now:dailyTask.startDate}, o)
# Work out if the Daily's Start Date (taskStartDate) is in the future.
# The time portion of the Start Date is never visible to or modifiable by the user so we must ignore it.
# Therefore, we must also ignore the time portion of the user's day start (startOfDayWithCDSTime), otherwise the date comparison will be wrong for some times.
# NB: The user's day start date has already been converted to the PREVIOUS day's date if the time portion was before CDS.
taskStartDate = moment(dailyTask.startDate || now()).startOf('day');
if taskStartDate > startOfDayWithCDSTime.startOf('day')
return false # Daily starts in the future
if dailyTask.frequency == 'daily'
daysSinceTaskStart = api.numDaysApart(day.startOf('day'), dailyTask.startDate, o)
if dailyTask.frequency == 'daily' # "Every X Days"
if !dailyTask.everyX
return false # error condition
daysSinceTaskStart = startOfDayWithCDSTime.startOf('day').diff(taskStartDate, 'days')
everyXCheck = (daysSinceTaskStart % dailyTask.everyX == 0)
return everyXCheck && hasStartedCheck
else if dailyTask.frequency == 'weekly'
return everyXCheck
else if dailyTask.frequency == 'weekly' # "On Certain Days of the Week"
if !dailyTask.repeat
return false # error condition
dayOfWeekNum = startOfDayWithCDSTime.day() # e.g. 0 for Sunday
dayOfWeekCheck = dailyTask.repeat[api.dayMapping[dayOfWeekNum]]
return dayOfWeekCheck && hasStartedCheck
return dayOfWeekCheck
else
# unexpected frequency string
return false
api.numDaysApart = (day1, day2, o) ->
startOfDay1 = api.startOfDay(_.defaults {now:day1}, o)
startOfDay2 = api.startOfDay(_.defaults {now:day2}, o)
numDays = Math.abs(startOfDay1.diff(startOfDay2, 'days'))
return numDays
###
------------------------------------------------------
Level cap
@ -235,7 +238,7 @@ api.taskDefaults = (task={}) ->
_.defaults(task, {up:true,down:true}) if task.type is 'habit'
_.defaults(task, {history: []}) if task.type in ['habit', 'daily']
_.defaults(task, {completed:false}) if task.type in ['daily', 'todo']
_.defaults(task, {streak:0, repeat: {su:1,m:1,t:1,w:1,th:1,f:1,s:1}}, startDate: new Date(), everyX: 1, frequency: 'weekly') if task.type is 'daily'
_.defaults(task, {streak:0, repeat: {su:true,m:true,t:true,w:true,th:true,f:true,s:true}}, startDate: new Date(), everyX: 1, frequency: 'weekly') if task.type is 'daily'
task._id = task.id # may need this for TaskSchema if we go back to using it, see http://goo.gl/a5irq4
task.value ?= if task.type is 'reward' then 10 else 0
task.priority = 1 unless _.isNumber(task.priority) # hotfix for apiv1. once we're off apiv1, we can remove this

View file

@ -127,7 +127,7 @@ cycle = (array)->
return array[n % array.length]
repeatWithoutLastWeekday = ()->
repeat = {su:1,m:1,t:1,w:1,th:1,f:1,s:1}
repeat = {su:true,m:true,t:true,w:true,th:true,f:true,s:true}
if shared.startOfWeek(moment().zone(0)).isoWeekday() == 1 # Monday
repeat.su = false
else
@ -178,7 +178,7 @@ describe 'User', ->
# Handle greyed-out dailys
yesterday = moment().subtract(1,'days')
user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = 0
user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false
_.each user.dailys[1..], (d)->d.completed = true
cron()
expect(user.stats.buffs.str).to.be 1
@ -251,7 +251,7 @@ describe 'User', ->
it 'does not reset checklist on grey incomplete dailies', ->
yesterday = moment().subtract(1,'days')
user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = 0
user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false
user.dailys[0].checklist = [
{
"text" : "1",
@ -276,7 +276,7 @@ describe 'User', ->
it 'resets checklist on complete grey complete dailies', ->
yesterday = moment().subtract(1,'days')
user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = 0
user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false
user.dailys[0].checklist = [
{
"text" : "1",
@ -905,7 +905,7 @@ describe 'Cron', ->
'due today':
# NOTE: a strange thing here, moment().startOf('week') is Sunday, but moment.zone(myTimeZone).startOf('week') is Monday.
defaults: {repeat:{su:1,m:true,t:1,w:1,th:1,f:1,s:1}}
defaults: {repeat:{su:true,m:true,t:true,w:true,th:true,f:true,s:true}}
steps:
'pre-dayStart':
defaults: {currentHour:3, dayStart:4, shouldDo:true}
@ -919,7 +919,7 @@ describe 'Cron', ->
'unchecked': {checked:false, expect: 'losePoints'}
'NOT due today':
defaults: {repeat:{su:1,m:false,t:1,w:1,th:1,f:1,s:1}}
defaults: {repeat:{su:true,m:false,t:true,w:true,th:true,f:true,s:true}}
steps:
'pre-dayStart':
defaults: {currentHour:3, dayStart:4, shouldDo:true}

View file

@ -6,7 +6,7 @@ shared = require '../../common/script/index.coffee'
shared.i18n.translations = require('../../website/src/i18n.js').translations
repeatWithoutLastWeekday = ()->
repeat = {su:1,m:1,t:1,w:1,th:1,f:1,s:1}
repeat = {su:true,m:true,t:true,w:true,th:true,f:true,s:true}
if shared.startOfWeek(moment().zone(0)).isoWeekday() == 1 # Monday
repeat.su = false
else
@ -64,7 +64,7 @@ describe 'daily/weekly that repeats everyday (default)', ->
user = newUser()
user.dailys = [
shared.taskDefaults({type:'daily', startDate: moment().add(7, 'days'), frequency: 'daily'})
shared.taskDefaults({type:'daily', startDate: moment().add(7, 'days'), frequency: 'weekly', repeat: {su:1,m:1,t:1,w:1,th:1,f:1,s:1}})
shared.taskDefaults({type:'daily', startDate: moment().add(7, 'days'), frequency: 'weekly', repeat: {su:true,m:true,t:true,w:true,th:true,f:true,s:true}})
]
daily = user.dailys[0]
weekly = user.dailys[1]