From a73662bab610397eda1c26f15f078ee3b288e7e7 Mon Sep 17 00:00:00 2001 From: Alys Date: Sat, 21 Nov 2015 20:33:01 +1000 Subject: [PATCH 01/10] refactor api.dayMapping to DAY_MAPPING const, not exposed on api --- common/script/cron.js | 16 ++++++++++++++++ common/script/index.js | 17 +++++------------ test/common/algos.mocha.js | 10 +++++++--- 3 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 common/script/cron.js diff --git a/common/script/cron.js b/common/script/cron.js new file mode 100644 index 0000000000..9dddeaaa7f --- /dev/null +++ b/common/script/cron.js @@ -0,0 +1,16 @@ +/* + ------------------------------------------------------ + Cron and time / day functions + ------------------------------------------------------ + */ + +export const DAY_MAPPING = { + 0: 'su', + 1: 'm', + 2: 't', + 3: 'w', + 4: 'th', + 5: 'f', + 6: 's', +}; + diff --git a/common/script/index.js b/common/script/index.js index 6a3558f937..c56d9b0f0d 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -1,3 +1,7 @@ +import { + DAY_MAPPING // temporary, pending further refactoring +} from '../../common/script/cron'; + var $w, _, api, content, i18n, moment, preenHistory, sanitizeOptions, sortOrder, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -113,17 +117,6 @@ api.startOfDay = function(options) { return dayStart; }; -api.dayMapping = { - 0: 'su', - 1: 'm', - 2: 't', - 3: 'w', - 4: 'th', - 5: 'f', - 6: 's' -}; - - /* Absolute diff from "yesterday" till now */ @@ -175,7 +168,7 @@ api.shouldDo = function(day, dailyTask, options) { return false; } dayOfWeekNum = startOfDayWithCDSTime.day(); - dayOfWeekCheck = dailyTask.repeat[api.dayMapping[dayOfWeekNum]]; + dayOfWeekCheck = dailyTask.repeat[DAY_MAPPING[dayOfWeekNum]]; return dayOfWeekCheck; } else { return false; diff --git a/test/common/algos.mocha.js b/test/common/algos.mocha.js index af902bd46c..c10e18c6d7 100644 --- a/test/common/algos.mocha.js +++ b/test/common/algos.mocha.js @@ -1,4 +1,8 @@ /* eslint-disable camelcase, func-names, no-shadow */ +import { + DAY_MAPPING, +} from '../../common/script/cron'; + let expect = require('expect.js'); let sinon = require('sinon'); let moment = require('moment'); @@ -301,7 +305,7 @@ describe('User', () => { let yesterday = moment().subtract(1, 'days'); - user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false; + user.dailys[0].repeat[DAY_MAPPING[yesterday.day()]] = false; _.each(user.dailys.slice(1), (d) => { d.completed = true; }); @@ -383,7 +387,7 @@ describe('User', () => { it('does not reset checklist on grey incomplete dailies', () => { let yesterday = moment().subtract(1, 'days'); - user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false; + user.dailys[0].repeat[DAY_MAPPING[yesterday.day()]] = false; user.dailys[0].checklist = [ { text: '1', @@ -407,7 +411,7 @@ describe('User', () => { it('resets checklist on complete grey complete dailies', () => { let yesterday = moment().subtract(1, 'days'); - user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false; + user.dailys[0].repeat[DAY_MAPPING[yesterday.day()]] = false; user.dailys[0].checklist = [ { text: '1', From e0784a9791cf83227d6cbca8234090fca19c6bc9 Mon Sep 17 00:00:00 2001 From: Alys Date: Sat, 21 Nov 2015 22:06:02 +1000 Subject: [PATCH 02/10] refactor sanitizeOptions from index.js to cron.js --- common/script/cron.js | 23 +++++++++++++++++++++++ common/script/index.js | 24 +++--------------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index 9dddeaaa7f..797adff67a 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -3,6 +3,9 @@ Cron and time / day functions ------------------------------------------------------ */ +import _ from 'lodash'; + +let moment = require('moment'); export const DAY_MAPPING = { 0: 'su', @@ -14,3 +17,23 @@ export const DAY_MAPPING = { 6: 's', }; +/* + Each time we perform date maths (cron, task-due-days, etc), we need to consider user preferences. + Specifically {dayStart} (custom day start) and {timezoneOffset}. This function sanitizes / defaults those values. + {now} is also passed in for various purposes, one example being the test scripts scripts testing different "now" times. + */ + +export function sanitizeOptions (o) { + let ref = Number(o.dayStart); + let dayStart = !_.isNaN(ref) && ref >= 0 && ref <= 24 ? ref : 0; + let timezoneOffset = o.timezoneOffset ? Number(o.timezoneOffset) : Number(moment().zone()); + // TODO: check and clean timezoneOffset as for dayStart (e.g., might not be a number) + let now = o.now ? moment(o.now).zone(timezoneOffset) : moment().zone(timezoneOffset); + + // return a new object, we don't want to add "now" to user object + return { + dayStart, + timezoneOffset, + now, + }; +} diff --git a/common/script/index.js b/common/script/index.js index c56d9b0f0d..2260195328 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -1,8 +1,9 @@ import { - DAY_MAPPING // temporary, pending further refactoring + DAY_MAPPING, // temporary, pending further refactoring + sanitizeOptions, // temporary, pending further refactoring } from '../../common/script/cron'; -var $w, _, api, content, i18n, moment, preenHistory, sanitizeOptions, sortOrder, +var $w, _, api, content, i18n, moment, preenHistory, sortOrder, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; moment = require('moment'); @@ -72,25 +73,6 @@ api.planGemLimits = { ------------------------------------------------------ */ - -/* - Each time we're performing date math (cron, task-due-days, etc), we need to take user preferences into consideration. - Specifically {dayStart} (custom day start) and {timezoneOffset}. This function sanitizes / defaults those values. - {now} is also passed in for various purposes, one example being the test scripts scripts testing different "now" times - */ - -sanitizeOptions = function(o) { - var dayStart, now, ref, timezoneOffset; - dayStart = !_.isNaN(+o.dayStart) && (0 <= (ref = +o.dayStart) && ref <= 24) ? +o.dayStart : 0; - timezoneOffset = o.timezoneOffset ? +o.timezoneOffset : +moment().zone(); - now = o.now ? moment(o.now).zone(timezoneOffset) : moment(+(new Date)).zone(timezoneOffset); - return { - dayStart: dayStart, - timezoneOffset: timezoneOffset, - now: now - }; -}; - api.startOfWeek = api.startOfWeek = function(options) { var o; if (options == null) { From 6183fd9e7142ea29b4c95b347deb630f6296613b Mon Sep 17 00:00:00 2001 From: Alys Date: Sat, 21 Nov 2015 22:26:05 +1000 Subject: [PATCH 03/10] refactor api.startOfWeek to a cron.js function not exposed on api --- common/script/cron.js | 10 ++++++++++ common/script/index.js | 9 --------- test/common/algos.mocha.js | 5 +++-- test/common/dailies.js | 5 ++++- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index 797adff67a..dba2a13717 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -37,3 +37,13 @@ export function sanitizeOptions (o) { now, }; } + +export function startOfWeek (options) { + if (options === null) { + options = {}; + } + let o = sanitizeOptions(options); + + return moment(o.now).startOf('week'); +} + diff --git a/common/script/index.js b/common/script/index.js index 2260195328..7d012165f4 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -73,15 +73,6 @@ api.planGemLimits = { ------------------------------------------------------ */ -api.startOfWeek = api.startOfWeek = function(options) { - var o; - if (options == null) { - options = {}; - } - o = sanitizeOptions(options); - return moment(o.now).startOf('week'); -}; - api.startOfDay = function(options) { var dayStart, o; if (options == null) { diff --git a/test/common/algos.mocha.js b/test/common/algos.mocha.js index c10e18c6d7..a57a049233 100644 --- a/test/common/algos.mocha.js +++ b/test/common/algos.mocha.js @@ -1,6 +1,7 @@ /* eslint-disable camelcase, func-names, no-shadow */ import { DAY_MAPPING, + startOfWeek, } from '../../common/script/cron'; let expect = require('expect.js'); @@ -209,7 +210,7 @@ let repeatWithoutLastWeekday = () => { s: true, }; - if (shared.startOfWeek(moment().zone(0)).isoWeekday() === 1) { + if (startOfWeek(moment().zone(0)).isoWeekday() === 1) { repeat.su = false; } else { repeat.s = false; @@ -1270,7 +1271,7 @@ describe('Cron', () => { function runCron (options) { _.each([480, 240, 0, -120], function (timezoneOffset) { - let now = shared.startOfWeek({ + let now = startOfWeek({ timezoneOffset, }).add(options.currentHour || 0, 'hours'); diff --git a/test/common/dailies.js b/test/common/dailies.js index d9c1621904..079ecb59e9 100644 --- a/test/common/dailies.js +++ b/test/common/dailies.js @@ -1,4 +1,7 @@ /* eslint-disable camelcase */ +import { + startOfWeek, +} from '../../common/script/cron'; let expect = require('expect.js'); // eslint-disable-line no-shadow let moment = require('moment'); @@ -17,7 +20,7 @@ let repeatWithoutLastWeekday = () => { // eslint-disable-line no-unused-vars s: true, }; - if (shared.startOfWeek(moment().zone(0)).isoWeekday() === 1) { + if (startOfWeek(moment().zone(0)).isoWeekday() === 1) { repeat.su = false; } else { repeat.s = false; From 9a76e7a62cdcc56a06916bcdc709d8abf4019d93 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sat, 21 Nov 2015 09:33:29 -0600 Subject: [PATCH 04/10] chore(gulp): Add convience task for starting up a test server. --- tasks/gulp-tests.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tasks/gulp-tests.js b/tasks/gulp-tests.js index bbaf401bfe..c553fdcd3f 100644 --- a/tasks/gulp-tests.js +++ b/tasks/gulp-tests.js @@ -39,6 +39,13 @@ let testBin = (string) => { return `NODE_ENV=testing ./node_modules/.bin/${string}`; }; +gulp.task('test:nodemon', (done) => { + process.env.PORT = TEST_SERVER_PORT; + process.env.NODE_DB_URI=TEST_DB_URI; + + runSequence('nodemon') +}); + gulp.task('test:prepare:mongo', (cb) => { mongoose.connect(TEST_DB_URI, (err) => { if (err) return cb(`Unable to connect to mongo database. Are you sure it's running? \n\n${err}`); From 2f8b6d68e802e5422c457df3dc21fb13155db639 Mon Sep 17 00:00:00 2001 From: Alys Date: Sun, 22 Nov 2015 07:13:44 +1000 Subject: [PATCH 05/10] refactor api.startOfDay to a cron.js function not exposed on api --- common/script/cron.js | 21 +++++++++++++++++++++ common/script/index.js | 24 ++++-------------------- test/common/algos.mocha.js | 11 ++++++----- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index dba2a13717..2de430e73b 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -47,3 +47,24 @@ export function startOfWeek (options) { return moment(o.now).startOf('week'); } + +/* + 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). + */ + +export function startOfDay (options) { + if (options === null) { + options = {}; + } + let o = sanitizeOptions(options); + let dayStart = moment(o.now).startOf('day').add({ hours: o.dayStart }); + + if (moment(o.now).hour() < o.dayStart) { + dayStart.subtract({ days: 1 }); + } + return dayStart; +} + diff --git a/common/script/index.js b/common/script/index.js index 7d012165f4..350f8c4c5f 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -1,6 +1,7 @@ import { DAY_MAPPING, // temporary, pending further refactoring sanitizeOptions, // temporary, pending further refactoring + startOfDay, // temporary, pending further refactoring } from '../../common/script/cron'; var $w, _, api, content, i18n, moment, preenHistory, sortOrder, @@ -73,23 +74,6 @@ api.planGemLimits = { ------------------------------------------------------ */ -api.startOfDay = function(options) { - var dayStart, o; - if (options == null) { - options = {}; - } - o = sanitizeOptions(options); - dayStart = moment(o.now).startOf('day').add({ - hours: o.dayStart - }); - if (moment(o.now).hour() < o.dayStart) { - dayStart.subtract({ - days: 1 - }); - } - return dayStart; -}; - /* Absolute diff from "yesterday" till now */ @@ -100,9 +84,9 @@ api.daysSince = function(yesterday, options) { options = {}; } o = sanitizeOptions(options); - return api.startOfDay(_.defaults({ + return startOfDay(_.defaults({ now: o.now - }, o)).diff(api.startOfDay(_.defaults({ + }, o)).diff(startOfDay(_.defaults({ now: yesterday }, o)), 'days'); }; @@ -121,7 +105,7 @@ api.shouldDo = function(day, dailyTask, options) { return false; } o = sanitizeOptions(options); - startOfDayWithCDSTime = api.startOfDay(_.defaults({ + startOfDayWithCDSTime = startOfDay(_.defaults({ now: day }, o)); taskStartDate = moment(dailyTask.startDate).zone(o.timezoneOffset); diff --git a/test/common/algos.mocha.js b/test/common/algos.mocha.js index a57a049233..293018c74c 100644 --- a/test/common/algos.mocha.js +++ b/test/common/algos.mocha.js @@ -2,6 +2,7 @@ import { DAY_MAPPING, startOfWeek, + startOfDay, } from '../../common/script/cron'; let expect = require('expect.js'); @@ -1190,7 +1191,7 @@ describe('Cron', () => { let fstr = 'YYYY-MM-DD HH: mm: ss'; it('startOfDay before dayStart', () => { - let start = shared.startOfDay({ + let start = startOfDay({ now: moment('2014-10-09 02: 30: 00'), dayStart, }); @@ -1198,7 +1199,7 @@ describe('Cron', () => { expect(start.format(fstr)).to.eql('2014-10-08 04: 00: 00'); }); it('startOfDay after dayStart', () => { - let start = shared.startOfDay({ + let start = startOfDay({ now: moment('2014-10-09 05: 30: 00'), dayStart, }); @@ -1501,17 +1502,17 @@ describe('Helper', () => { let today = '2013-01-01 00: 00: 00'; let zone = moment(today).zone(); - expect(shared.startOfDay({ + expect(startOfDay({ now: new Date(2013, 0, 1, 0), }, { timezoneOffset: zone, }).format(fstr)).to.eql(today); - expect(shared.startOfDay({ + expect(startOfDay({ now: new Date(2013, 0, 1, 5), }, { timezoneOffset: zone, }).format(fstr)).to.eql(today); - expect(shared.startOfDay({ + expect(startOfDay({ now: new Date(2013, 0, 1, 23, 59, 59), timezoneOffset: zone, }).format(fstr)).to.eql(today); From a380c1aa302d5ab16dbd52fada171501c1012428 Mon Sep 17 00:00:00 2001 From: Alys Date: Sun, 22 Nov 2015 08:38:45 +1000 Subject: [PATCH 06/10] refactor api.daysSince to a cron.js function not exposed on api --- common/script/cron.js | 11 ++++++++++- common/script/index.js | 23 +++-------------------- test/common/algos.mocha.js | 13 +++++++------ 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index 2de430e73b..12b8ab60cd 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -47,7 +47,6 @@ export function startOfWeek (options) { return moment(o.now).startOf('week'); } - /* 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. @@ -68,3 +67,13 @@ export function startOfDay (options) { return dayStart; } +/* + Absolute diff from "yesterday" till now + */ + +export function daysSince (yesterday, options = {}) { + let o = sanitizeOptions(options); + + return startOfDay(_.defaults({ now: o.now }, o)).diff(startOfDay(_.defaults({ now: yesterday }, o)), 'days'); +} + diff --git a/common/script/index.js b/common/script/index.js index 350f8c4c5f..8d97b3fc58 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -2,6 +2,7 @@ import { DAY_MAPPING, // temporary, pending further refactoring sanitizeOptions, // temporary, pending further refactoring startOfDay, // temporary, pending further refactoring + daysSince, } from '../../common/script/cron'; var $w, _, api, content, i18n, moment, preenHistory, sortOrder, @@ -74,24 +75,6 @@ api.planGemLimits = { ------------------------------------------------------ */ -/* - Absolute diff from "yesterday" till now - */ - -api.daysSince = function(yesterday, options) { - var o; - if (options == null) { - options = {}; - } - o = sanitizeOptions(options); - return startOfDay(_.defaults({ - now: o.now - }, o)).diff(startOfDay(_.defaults({ - now: yesterday - }, o)), 'days'); -}; - - /* Should the user do this task on this date, given the task's repeat options and user.preferences.dayStart? */ @@ -2276,7 +2259,7 @@ api.wrap = function(user, main) { } } dropMultiplier = ((ref1 = user.purchased) != null ? (ref2 = ref1.plan) != null ? ref2.customerId : void 0 : void 0) ? 2 : 1; - if ((api.daysSince(user.items.lastDrop.date, user.preferences) === 0) && (user.items.lastDrop.count >= dropMultiplier * (5 + Math.floor(user._statsComputed.per / 25) + (user.contributor.level || 0)))) { + if ((daysSince(user.items.lastDrop.date, user.preferences) === 0) && (user.items.lastDrop.count >= dropMultiplier * (5 + Math.floor(user._statsComputed.per / 25) + (user.contributor.level || 0)))) { return; } if (((ref3 = user.flags) != null ? ref3.dropsEnabled : void 0) && user.fns.predictableRandom(user.stats.exp) < chance) { @@ -2483,7 +2466,7 @@ api.wrap = function(user, main) { options = {}; } now = +options.now || +(new Date); - daysMissed = api.daysSince(user.lastCron, _.defaults({ + daysMissed = daysSince(user.lastCron, _.defaults({ now: now }, user.preferences)); if (!(daysMissed > 0)) { diff --git a/test/common/algos.mocha.js b/test/common/algos.mocha.js index 293018c74c..b398a5bb72 100644 --- a/test/common/algos.mocha.js +++ b/test/common/algos.mocha.js @@ -3,6 +3,7 @@ import { DAY_MAPPING, startOfWeek, startOfDay, + daysSince, } from '../../common/script/cron'; let expect = require('expect.js'); @@ -1208,7 +1209,7 @@ describe('Cron', () => { }); it('daysSince cron before, now after', () => { let lastCron = moment('2014-10-09 02: 30: 00'); - let days = shared.daysSince(lastCron, { + let days = daysSince(lastCron, { now: moment('2014-10-09 11: 30: 00'), dayStart, }); @@ -1217,7 +1218,7 @@ describe('Cron', () => { }); it('daysSince cron before, now before', () => { let lastCron = moment('2014-10-09 02: 30: 00'); - let days = shared.daysSince(lastCron, { + let days = daysSince(lastCron, { now: moment('2014-10-09 03: 30: 00'), dayStart, }); @@ -1226,7 +1227,7 @@ describe('Cron', () => { }); it('daysSince cron after, now after', () => { let lastCron = moment('2014-10-09 05: 30: 00'); - let days = shared.daysSince(lastCron, { + let days = daysSince(lastCron, { now: moment('2014-10-09 06: 30: 00'), dayStart, }); @@ -1235,7 +1236,7 @@ describe('Cron', () => { }); it('daysSince cron after, now tomorrow before', () => { let lastCron = moment('2014-10-09 12: 30: 00'); - let days = shared.daysSince(lastCron, { + let days = daysSince(lastCron, { now: moment('2014-10-10 01: 30: 00'), dayStart, }); @@ -1244,7 +1245,7 @@ describe('Cron', () => { }); it('daysSince cron after, now tomorrow after', () => { let lastCron = moment('2014-10-09 12: 30: 00'); - let days = shared.daysSince(lastCron, { + let days = daysSince(lastCron, { now: moment('2014-10-10 10: 30: 00'), dayStart, }); @@ -1253,7 +1254,7 @@ describe('Cron', () => { }); xit('daysSince, last cron before new dayStart', () => { let lastCron = moment('2014-10-09 01: 00: 00'); - let days = shared.daysSince(lastCron, { + let days = daysSince(lastCron, { now: moment('2014-10-09 05: 00: 00'), dayStart, }); From 728a94d19900a46e142934dd1189aa65cb5e177a Mon Sep 17 00:00:00 2001 From: Alys Date: Sun, 22 Nov 2015 11:05:03 +1000 Subject: [PATCH 07/10] improve coding style --- common/script/cron.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index 12b8ab60cd..017f7e1d4a 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -4,8 +4,7 @@ ------------------------------------------------------ */ import _ from 'lodash'; - -let moment = require('moment'); +import moment from 'moment'; export const DAY_MAPPING = { 0: 'su', @@ -38,10 +37,7 @@ export function sanitizeOptions (o) { }; } -export function startOfWeek (options) { - if (options === null) { - options = {}; - } +export function startOfWeek (options = {}) { let o = sanitizeOptions(options); return moment(o.now).startOf('week'); @@ -54,10 +50,7 @@ export function startOfWeek (options) { 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). */ -export function startOfDay (options) { - if (options === null) { - options = {}; - } +export function startOfDay (options = {}) { let o = sanitizeOptions(options); let dayStart = moment(o.now).startOf('day').add({ hours: o.dayStart }); From 83ea085c218b627976ce8c5f51733dc080d7c91c Mon Sep 17 00:00:00 2001 From: Alys Date: Sun, 22 Nov 2015 11:28:51 +1000 Subject: [PATCH 08/10] refactor api.shouldDo and associated code; fix new bug in sanitizeOptions() --- common/script/cron.js | 42 ++++++++++++++++++++++++++++++++-- common/script/index.js | 52 ++++-------------------------------------- 2 files changed, 45 insertions(+), 49 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index 017f7e1d4a..639bbf6b65 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -22,8 +22,8 @@ export const DAY_MAPPING = { {now} is also passed in for various purposes, one example being the test scripts scripts testing different "now" times. */ -export function sanitizeOptions (o) { - let ref = Number(o.dayStart); +function sanitizeOptions (o) { + let ref = Number(o.dayStart || 0); let dayStart = !_.isNaN(ref) && ref >= 0 && ref <= 24 ? ref : 0; let timezoneOffset = o.timezoneOffset ? Number(o.timezoneOffset) : Number(moment().zone()); // TODO: check and clean timezoneOffset as for dayStart (e.g., might not be a number) @@ -70,3 +70,41 @@ export function daysSince (yesterday, options = {}) { return startOfDay(_.defaults({ now: o.now }, o)).diff(startOfDay(_.defaults({ now: yesterday }, o)), 'days'); } +/* + Should the user do this task on this date, given the task's repeat options and user.preferences.dayStart? + */ + +export function shouldDo (day, dailyTask, options = {}) { + if (dailyTask.type !== 'daily') { + return false; + } + let o = sanitizeOptions(options); + let startOfDayWithCDSTime = startOfDay(_.defaults({ now: day }, o)); + + // 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. + let taskStartDate = moment(dailyTask.startDate).zone(o.timezoneOffset); + + taskStartDate = moment(taskStartDate).startOf('day'); + if (taskStartDate > startOfDayWithCDSTime.startOf('day')) { + return false; // Daily starts in the future + } + if (dailyTask.frequency === 'daily') { // "Every X Days" + if (!dailyTask.everyX) { + return false; // error condition + } + let daysSinceTaskStart = startOfDayWithCDSTime.startOf('day').diff(taskStartDate, 'days'); + + return daysSinceTaskStart % dailyTask.everyX === 0; + } else if (dailyTask.frequency === 'weekly') { // "On Certain Days of the Week" + if (!dailyTask.repeat) { + return false; // error condition + } + let dayOfWeekNum = startOfDayWithCDSTime.day(); // e.g., 0 for Sunday + + return dailyTask.repeat[DAY_MAPPING[dayOfWeekNum]]; + } else { + return false; // error condition - unexpected frequency string + } +} diff --git a/common/script/index.js b/common/script/index.js index 8d97b3fc58..8f21077f1a 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -1,8 +1,6 @@ import { - DAY_MAPPING, // temporary, pending further refactoring - sanitizeOptions, // temporary, pending further refactoring - startOfDay, // temporary, pending further refactoring daysSince, + shouldDo, } from '../../common/script/cron'; var $w, _, api, content, i18n, moment, preenHistory, sortOrder, @@ -19,6 +17,7 @@ i18n = require('./i18n'); api = module.exports = {}; api.i18n = i18n; +api.shouldDo = shouldDo; $w = api.$w = function(s) { return s.split(' '); @@ -75,47 +74,6 @@ api.planGemLimits = { ------------------------------------------------------ */ -/* - Should the user do this task on this date, given the task's repeat options and user.preferences.dayStart? - */ - -api.shouldDo = function(day, dailyTask, options) { - var dayOfWeekCheck, dayOfWeekNum, daysSinceTaskStart, everyXCheck, o, startOfDayWithCDSTime, taskStartDate; - if (options == null) { - options = {}; - } - if (dailyTask.type !== 'daily') { - return false; - } - o = sanitizeOptions(options); - startOfDayWithCDSTime = startOfDay(_.defaults({ - now: day - }, o)); - taskStartDate = moment(dailyTask.startDate).zone(o.timezoneOffset); - taskStartDate = moment(taskStartDate).startOf('day'); - if (taskStartDate > startOfDayWithCDSTime.startOf('day')) { - return false; - } - if (dailyTask.frequency === 'daily') { - if (!dailyTask.everyX) { - return false; - } - daysSinceTaskStart = startOfDayWithCDSTime.startOf('day').diff(taskStartDate, 'days'); - everyXCheck = daysSinceTaskStart % dailyTask.everyX === 0; - return everyXCheck; - } else if (dailyTask.frequency === 'weekly') { - if (!dailyTask.repeat) { - return false; - } - dayOfWeekNum = startOfDayWithCDSTime.day(); - dayOfWeekCheck = dailyTask.repeat[DAY_MAPPING[dayOfWeekNum]]; - return dayOfWeekCheck; - } else { - return false; - } -}; - - /* ------------------------------------------------------ Level cap @@ -468,7 +426,7 @@ api.taskClasses = function(task, filters, dayStart, lastCron, showCompleted, mai classes += " beingEdited"; } if (type === 'todo' || type === 'daily') { - if (completed || (type === 'daily' && !api.shouldDo(+(new Date), task, { + if (completed || (type === 'daily' && !shouldDo(+(new Date), task, { dayStart: dayStart }))) { classes += " completed"; @@ -2532,7 +2490,7 @@ api.wrap = function(user, main) { thatDay = moment(now).subtract({ days: 1 }); - if (api.shouldDo(thatDay.toDate(), daily, user.preferences) || completed) { + if (shouldDo(thatDay.toDate(), daily, user.preferences) || completed) { _.each(daily.checklist, (function(box) { box.completed = false; return true; @@ -2586,7 +2544,7 @@ api.wrap = function(user, main) { thatDay = moment(now).subtract({ days: n + 1 }); - if (api.shouldDo(thatDay.toDate(), task, user.preferences)) { + if (shouldDo(thatDay.toDate(), task, user.preferences)) { scheduleMisses++; if (user.stats.buffs.stealth) { user.stats.buffs.stealth--; From 1082bb1f815510231e00617daf43738d671437eb Mon Sep 17 00:00:00 2001 From: Alys Date: Sun, 22 Nov 2015 15:52:30 +1000 Subject: [PATCH 09/10] refactor preenHistory from index.js to cron.js --- common/script/cron.js | 68 ++++++++++++++++++++++++++++++++++++++++++ common/script/index.js | 45 ++-------------------------- 2 files changed, 70 insertions(+), 43 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index 639bbf6b65..36dd405ee0 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -108,3 +108,71 @@ export function shouldDo (day, dailyTask, options = {}) { return false; // error condition - unexpected frequency string } } + + +/* + Preen history for users with > 7 history entries + This takes an infinite array of single day entries [day day day day day...], and turns it into a condensed array of averages, condensing more the further back in time we go. + Eg, 7 entries each for last 7 days; 1 entry each week of this month; 1 entry for each month of this year; 1 entry per previous year: [day*7 week*4 month*12 year*infinite] + */ + +export function preenHistory (history) { + // 'export' is temporary pending further refactoring + history = _.filter(history, function discardNulls (h) { + return Boolean(h); // nulls are from corruption somehow - TODO is this still needed? + }); + let newHistory = []; + let preen = function preener (amount, groupBy) { + let groups = _.chain(history).groupBy(function getDateGroupings (h) { + return moment(h.date).format(groupBy); + }).sortBy(function sortByDate (h, k) { + return k; + }).value(); // turn into an array + + groups = groups.slice(-amount); + groups.pop(); // get rid of 'this week', 'this month', etc (except for case of days) + return _.each(groups, function recreateHistory (group) { + newHistory.push({ + date: moment(group[0].date).toDate(), + value: _.reduce(group, function makeAverage (m, obj) { + return m + obj.value; + }, 0) / group.length, + }); + return true; + }); + }; + + // Keep the last: + preen(50, 'YYYY'); // 50 years (habit will toootally be around that long!) + preen(moment().format('MM'), 'YYYYMM'); // last MM months (eg, if today is 05, keep the last 5 months) + // Then keep all days of this month. Note, the extra logic is to account for Habits, which can be counted multiple times per day + // FIXME I'd rather keep 1-entry/week of this month, then last 'd' days in this week. However, I'm having issues where the 1st starts mid week + let thisMonth = moment().format('YYYYMM'); + + newHistory = newHistory.concat(_.filter(history, function keepThisMonthsData (h) { + return moment(h.date).format('YYYYMM') === thisMonth; + })); + return newHistory; +} + +/* +export function preenUserHistory (minHistLen = 7) { + _.each(user.habits.concat(user.dailys), function(task) { + var ref; + if (((ref = task.history) != null ? ref.length : void 0) > minHistLen) { + task.history = preenHistory(task.history); + } + return true; + }); + _.defaults(user.history, { + todos: [], + exp: [] + }); + if (user.history.exp.length > minHistLen) { + user.history.exp = preenHistory(user.history.exp); + } + if (user.history.todos.length > minHistLen) { + return user.history.todos = preenHistory(user.history.todos); + } +} +*/ diff --git a/common/script/index.js b/common/script/index.js index 8f21077f1a..33e317eb16 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -1,9 +1,10 @@ import { daysSince, shouldDo, + preenHistory, // temporary pending further refactoring } from '../../common/script/cron'; -var $w, _, api, content, i18n, moment, preenHistory, sortOrder, +var $w, _, api, content, i18n, moment, sortOrder, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; moment = require('moment'); @@ -130,48 +131,6 @@ api.monod = function(bonus, rateOfIncrease, max) { }; -/* -Preen history for users with > 7 history entries -This takes an infinite array of single day entries [day day day day day...], and turns it into a condensed array -of averages, condensing more the further back in time we go. Eg, 7 entries each for last 7 days; 1 entry each week -of this month; 1 entry for each month of this year; 1 entry per previous year: [day*7 week*4 month*12 year*infinite] - */ - -preenHistory = function(history) { - var newHistory, preen, thisMonth; - history = _.filter(history, function(h) { - return !!h; - }); - newHistory = []; - preen = function(amount, groupBy) { - var groups; - groups = _.chain(history).groupBy(function(h) { - return moment(h.date).format(groupBy); - }).sortBy(function(h, k) { - return k; - }).value(); - groups = groups.slice(-amount); - groups.pop(); - return _.each(groups, function(group) { - newHistory.push({ - date: moment(group[0].date).toDate(), - value: _.reduce(group, (function(m, obj) { - return m + obj.value; - }), 0) / group.length - }); - return true; - }); - }; - preen(50, "YYYY"); - preen(moment().format('MM'), "YYYYMM"); - thisMonth = moment().format('YYYYMM'); - newHistory = newHistory.concat(_.filter(history, function(h) { - return moment(h.date).format('YYYYMM') === thisMonth; - })); - return newHistory; -}; - - /* Preen 3-day past-completed To-Dos from Angular & mobile app */ From 65c276c477aafa24e2eb49ed5d270531fb73ef4f Mon Sep 17 00:00:00 2001 From: Alys Date: Sun, 22 Nov 2015 21:08:51 +1000 Subject: [PATCH 10/10] fix formatting in preenHistory --- common/script/cron.js | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/common/script/cron.js b/common/script/cron.js index 36dd405ee0..53a0e1d6fa 100644 --- a/common/script/cron.js +++ b/common/script/cron.js @@ -122,7 +122,8 @@ export function preenHistory (history) { return Boolean(h); // nulls are from corruption somehow - TODO is this still needed? }); let newHistory = []; - let preen = function preener (amount, groupBy) { + + function preen (amount, groupBy) { let groups = _.chain(history).groupBy(function getDateGroupings (h) { return moment(h.date).format(groupBy); }).sortBy(function sortByDate (h, k) { @@ -140,7 +141,7 @@ export function preenHistory (history) { }); return true; }); - }; + } // Keep the last: preen(50, 'YYYY'); // 50 years (habit will toootally be around that long!) @@ -154,25 +155,3 @@ export function preenHistory (history) { })); return newHistory; } - -/* -export function preenUserHistory (minHistLen = 7) { - _.each(user.habits.concat(user.dailys), function(task) { - var ref; - if (((ref = task.history) != null ? ref.length : void 0) > minHistLen) { - task.history = preenHistory(task.history); - } - return true; - }); - _.defaults(user.history, { - todos: [], - exp: [] - }); - if (user.history.exp.length > minHistLen) { - user.history.exp = preenHistory(user.history.exp); - } - if (user.history.todos.length > minHistLen) { - return user.history.todos = preenHistory(user.history.todos); - } -} -*/