Reverted should do file (#8556)

This commit is contained in:
Keith Holliday
2017-03-10 16:37:52 -07:00
committed by Sabe Jones
parent d0d4b47c47
commit bde41699ee
2 changed files with 288 additions and 313 deletions

View File

@@ -1,310 +1,310 @@
import { shouldDo, DAY_MAPPING } from '../../website/common/script/cron'; // import { shouldDo, DAY_MAPPING } from '../../website/common/script/cron';
import moment from 'moment'; // import moment from 'moment';
import 'moment-recur'; // import 'moment-recur';
describe('shouldDo', () => { // describe('shouldDo', () => {
let day, dailyTask; // let day, dailyTask;
let options = {}; // let options = {};
beforeEach(() => { // beforeEach(() => {
day = new Date(); // day = new Date();
dailyTask = { // dailyTask = {
completed: 'false', // completed: 'false',
everyX: 1, // everyX: 1,
frequency: 'weekly', // frequency: 'weekly',
type: 'daily', // type: 'daily',
repeat: { // repeat: {
su: true, // su: true,
s: true, // s: true,
f: true, // f: true,
th: true, // th: true,
w: true, // w: true,
t: true, // t: true,
m: true, // m: true,
}, // },
startDate: new Date(), // startDate: new Date(),
}; // };
}); // });
it('leaves Daily inactive before start date', () => { // it('leaves Daily inactive before start date', () => {
dailyTask.startDate = moment().add(1, 'days').toDate(); // dailyTask.startDate = moment().add(1, 'days').toDate();
expect(shouldDo(day, dailyTask, options)).to.equal(false); // expect(shouldDo(day, dailyTask, options)).to.equal(false);
}); // });
context('Every X Days', () => { // context('Every X Days', () => {
it('leaves Daily inactive in between X Day intervals', () => { // it('leaves Daily inactive in between X Day intervals', () => {
dailyTask.startDate = moment().subtract(1, 'days').toDate(); // dailyTask.startDate = moment().subtract(1, 'days').toDate();
dailyTask.frequency = 'daily'; // dailyTask.frequency = 'daily';
dailyTask.everyX = 2; // dailyTask.everyX = 2;
expect(shouldDo(day, dailyTask, options)).to.equal(false); // expect(shouldDo(day, dailyTask, options)).to.equal(false);
}); // });
it('activates Daily on multiples of X Days', () => { // it('activates Daily on multiples of X Days', () => {
dailyTask.startDate = moment().subtract(7, 'days').toDate(); // dailyTask.startDate = moment().subtract(7, 'days').toDate();
dailyTask.frequency = 'daily'; // dailyTask.frequency = 'daily';
dailyTask.everyX = 7; // dailyTask.everyX = 7;
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
}); // });
}); // });
context('Certain Days of the Week', () => { // context('Certain Days of the Week', () => {
it('leaves Daily inactive if day of the week does not match', () => { // it('leaves Daily inactive if day of the week does not match', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: false, // th: false,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
for (let weekday of [0, 1, 2, 3, 4, 5, 6]) { // for (let weekday of [0, 1, 2, 3, 4, 5, 6]) {
day = moment().day(weekday).toDate(); // day = moment().day(weekday).toDate();
expect(shouldDo(day, dailyTask, options)).to.equal(false); // expect(shouldDo(day, dailyTask, options)).to.equal(false);
} // }
}); // });
it('leaves Daily inactive if day of the week does not match and active on the day it matches', () => { // it('leaves Daily inactive if day of the week does not match and active on the day it matches', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: true, // th: true,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
for (let weekday of [0, 1, 2, 3, 4, 5, 6]) { // for (let weekday of [0, 1, 2, 3, 4, 5, 6]) {
day = moment().add(1, 'weeks').day(weekday).toDate(); // day = moment().add(1, 'weeks').day(weekday).toDate();
if (weekday === 4) { // if (weekday === 4) {
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
} else { // } else {
expect(shouldDo(day, dailyTask, options)).to.equal(false); // expect(shouldDo(day, dailyTask, options)).to.equal(false);
} // }
} // }
}); // });
it('activates Daily on matching days of the week', () => { // it('activates Daily on matching days of the week', () => {
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
}); // });
}); // });
context('Every X Weeks', () => { // context('Every X Weeks', () => {
it('leaves daily inactive if it has not been the specified number of weeks', () => { // it('leaves daily inactive if it has not been the specified number of weeks', () => {
dailyTask.everyX = 3; // dailyTask.everyX = 3;
let tomorrow = moment().add(1, 'day').toDate(); // let tomorrow = moment().add(1, 'day').toDate();
expect(shouldDo(tomorrow, dailyTask, options)).to.equal(false); // expect(shouldDo(tomorrow, dailyTask, options)).to.equal(false);
}); // });
it('leaves daily inactive if on every (x) week on weekday it is incorrect weekday', () => { // it('leaves daily inactive if on every (x) week on weekday it is incorrect weekday', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: false, // th: false,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
day = moment(); // day = moment();
dailyTask.repeat[DAY_MAPPING[day.day()]] = true; // dailyTask.repeat[DAY_MAPPING[day.day()]] = true;
dailyTask.everyX = 3; // dailyTask.everyX = 3;
let threeWeeksFromTodayPlusOne = day.add(1, 'day').add(3, 'weeks').toDate(); // let threeWeeksFromTodayPlusOne = day.add(1, 'day').add(3, 'weeks').toDate();
expect(shouldDo(threeWeeksFromTodayPlusOne, dailyTask, options)).to.equal(false); // expect(shouldDo(threeWeeksFromTodayPlusOne, dailyTask, options)).to.equal(false);
}); // });
it('activates Daily on matching week', () => { // it('activates Daily on matching week', () => {
dailyTask.everyX = 3; // dailyTask.everyX = 3;
let threeWeeksFromToday = moment().add(3, 'weeks').toDate(); // let threeWeeksFromToday = moment().add(3, 'weeks').toDate();
expect(shouldDo(threeWeeksFromToday, dailyTask, options)).to.equal(true); // expect(shouldDo(threeWeeksFromToday, dailyTask, options)).to.equal(true);
}); // });
it('activates Daily on every (x) week on weekday', () => { // it('activates Daily on every (x) week on weekday', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: false, // th: false,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
day = moment(); // day = moment();
dailyTask.repeat[DAY_MAPPING[day.day()]] = true; // dailyTask.repeat[DAY_MAPPING[day.day()]] = true;
dailyTask.everyX = 3; // dailyTask.everyX = 3;
let threeWeeksFromToday = day.add(6, 'weeks').day(day.day()).toDate(); // let threeWeeksFromToday = day.add(6, 'weeks').day(day.day()).toDate();
expect(shouldDo(threeWeeksFromToday, dailyTask, options)).to.equal(true); // expect(shouldDo(threeWeeksFromToday, dailyTask, options)).to.equal(true);
}); // });
}); // });
context('Monthly - Every X Months on a specified date', () => { // context('Monthly - Every X Months on a specified date', () => {
it('leaves daily inactive if not day of the month', () => { // it('leaves daily inactive if not day of the month', () => {
dailyTask.everyX = 1; // dailyTask.everyX = 1;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
dailyTask.daysOfMonth = [15]; // dailyTask.daysOfMonth = [15];
let tomorrow = moment().add(1, 'day').toDate();// @TODO: make sure this is not the 15 // let tomorrow = moment().add(1, 'day').toDate();// @TODO: make sure this is not the 15
expect(shouldDo(tomorrow, dailyTask, options)).to.equal(false); // expect(shouldDo(tomorrow, dailyTask, options)).to.equal(false);
}); // });
it('activates Daily on matching day of month', () => { // it('activates Daily on matching day of month', () => {
day = moment(); // day = moment();
dailyTask.everyX = 1; // dailyTask.everyX = 1;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
dailyTask.daysOfMonth = [day.date()]; // dailyTask.daysOfMonth = [day.date()];
day = day.add(1, 'months').date(day.date()).toDate(); // day = day.add(1, 'months').date(day.date()).toDate();
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
}); // });
it('leaves daily inactive if not on date of the x month', () => { // it('leaves daily inactive if not on date of the x month', () => {
dailyTask.everyX = 2; // dailyTask.everyX = 2;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
dailyTask.daysOfMonth = [15]; // dailyTask.daysOfMonth = [15];
let tomorrow = moment().add(2, 'months').add(1, 'day').toDate(); // let tomorrow = moment().add(2, 'months').add(1, 'day').toDate();
expect(shouldDo(tomorrow, dailyTask, options)).to.equal(false); // expect(shouldDo(tomorrow, dailyTask, options)).to.equal(false);
}); // });
it('activates Daily if on date of the x month', () => { // it('activates Daily if on date of the x month', () => {
dailyTask.everyX = 2; // dailyTask.everyX = 2;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
dailyTask.daysOfMonth = [15]; // dailyTask.daysOfMonth = [15];
day = moment().add(2, 'months').date(15).toDate(); // day = moment().add(2, 'months').date(15).toDate();
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
}); // });
}); // });
context('Monthly - Certain days of the nth Week', () => { // context('Monthly - Certain days of the nth Week', () => {
it('leaves daily inactive if not the correct week of the month on the day of the start date', () => { // it('leaves daily inactive if not the correct week of the month on the day of the start date', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: false, // th: false,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
let today = moment('01/27/2017'); // let today = moment('01/27/2017');
let week = today.monthWeek(); // let week = today.monthWeek();
let dayOfWeek = today.day(); // let dayOfWeek = today.day();
dailyTask.startDate = today.toDate(); // dailyTask.startDate = today.toDate();
dailyTask.weeksOfMonth = [week]; // dailyTask.weeksOfMonth = [week];
dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true; // dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true;
dailyTask.everyX = 1; // dailyTask.everyX = 1;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
day = moment('02/23/2017'); // day = moment('02/23/2017');
expect(shouldDo(day, dailyTask, options)).to.equal(false); // expect(shouldDo(day, dailyTask, options)).to.equal(false);
}); // });
it('activates Daily if correct week of the month on the day of the start date', () => { // it('activates Daily if correct week of the month on the day of the start date', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: false, // th: false,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
let today = moment('01/27/2017'); // let today = moment('01/27/2017');
let week = today.monthWeek(); // let week = today.monthWeek();
let dayOfWeek = today.day(); // let dayOfWeek = today.day();
dailyTask.startDate = today.toDate(); // dailyTask.startDate = today.toDate();
dailyTask.weeksOfMonth = [week]; // dailyTask.weeksOfMonth = [week];
dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true; // dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true;
dailyTask.everyX = 1; // dailyTask.everyX = 1;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
day = moment('02/24/2017'); // day = moment('02/24/2017');
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
}); // });
it('leaves daily inactive if not day of the month with every x month on weekday', () => { // it('leaves daily inactive if not day of the month with every x month on weekday', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: false, // th: false,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
let today = moment('01/26/2017'); // let today = moment('01/26/2017');
let week = today.monthWeek(); // let week = today.monthWeek();
let dayOfWeek = today.day(); // let dayOfWeek = today.day();
dailyTask.startDate = today.toDate(); // dailyTask.startDate = today.toDate();
dailyTask.weeksOfMonth = [week]; // dailyTask.weeksOfMonth = [week];
dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true; // dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true;
dailyTask.everyX = 2; // dailyTask.everyX = 2;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
day = moment('03/24/2017'); // day = moment('03/24/2017');
expect(shouldDo(day, dailyTask, options)).to.equal(false); // expect(shouldDo(day, dailyTask, options)).to.equal(false);
}); // });
it('activates Daily if on nth weekday of the x month', () => { // it('activates Daily if on nth weekday of the x month', () => {
dailyTask.repeat = { // dailyTask.repeat = {
su: false, // su: false,
s: false, // s: false,
f: false, // f: false,
th: false, // th: false,
w: false, // w: false,
t: false, // t: false,
m: false, // m: false,
}; // };
let today = moment('01/27/2017'); // let today = moment('01/27/2017');
let week = today.monthWeek(); // let week = today.monthWeek();
let dayOfWeek = today.day(); // let dayOfWeek = today.day();
dailyTask.startDate = today.toDate(); // dailyTask.startDate = today.toDate();
dailyTask.weeksOfMonth = [week]; // dailyTask.weeksOfMonth = [week];
dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true; // dailyTask.repeat[DAY_MAPPING[dayOfWeek]] = true;
dailyTask.everyX = 2; // dailyTask.everyX = 2;
dailyTask.frequency = 'monthly'; // dailyTask.frequency = 'monthly';
day = moment('03/24/2017'); // day = moment('03/24/2017');
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
}); // });
}); // });
context('Every X Years', () => { // context('Every X Years', () => {
it('leaves daily inactive if not the correct year', () => { // it('leaves daily inactive if not the correct year', () => {
day = moment(); // day = moment();
dailyTask.everyX = 2; // dailyTask.everyX = 2;
dailyTask.frequency = 'yearly'; // dailyTask.frequency = 'yearly';
day = day.add(1, 'day').toDate(); // day = day.add(1, 'day').toDate();
expect(shouldDo(day, dailyTask, options)).to.equal(false); // expect(shouldDo(day, dailyTask, options)).to.equal(false);
}); // });
it('activates Daily on matching year', () => { // it('activates Daily on matching year', () => {
day = moment(); // day = moment();
dailyTask.everyX = 2; // dailyTask.everyX = 2;
dailyTask.frequency = 'yearly'; // dailyTask.frequency = 'yearly';
day = day.add(2, 'years').toDate(); // day = day.add(2, 'years').toDate();
expect(shouldDo(day, dailyTask, options)).to.equal(true); // expect(shouldDo(day, dailyTask, options)).to.equal(true);
}); // });
}); // });
}); // });

View File

@@ -4,10 +4,8 @@
Cron and time / day functions Cron and time / day functions
------------------------------------------------------ ------------------------------------------------------
*/ */
import defaults from 'lodash/defaults'; import _ from 'lodash'; // eslint-disable-line lodash/import-scope
import invert from 'lodash/invert';
import moment from 'moment'; import moment from 'moment';
import 'moment-recur';
export const DAY_MAPPING = { export const DAY_MAPPING = {
0: 'su', 0: 'su',
@@ -19,8 +17,6 @@ export const DAY_MAPPING = {
6: 's', 6: 's',
}; };
export const DAY_MAPPING_STRING_TO_NUMBER = invert(DAY_MAPPING);
/* /*
Each time we perform date maths (cron, task-due-days, etc), we need to consider user preferences. 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. Specifically {dayStart} (custom day start) and {timezoneOffset}. This function sanitizes / defaults those values.
@@ -29,13 +25,13 @@ export const DAY_MAPPING_STRING_TO_NUMBER = invert(DAY_MAPPING);
function sanitizeOptions (o) { function sanitizeOptions (o) {
let ref = Number(o.dayStart || 0); let ref = Number(o.dayStart || 0);
let dayStart = !Number.isNaN(ref) && ref >= 0 && ref <= 24 ? ref : 0; let dayStart = !_.isNaN(ref) && ref >= 0 && ref <= 24 ? ref : 0;
let timezoneOffset; let timezoneOffset;
let timezoneOffsetDefault = Number(moment().zone()); let timezoneOffsetDefault = Number(moment().zone());
if (Number.isFinite(o.timezoneOffsetOverride)) { if (_.isFinite(o.timezoneOffsetOverride)) {
timezoneOffset = Number(o.timezoneOffsetOverride); timezoneOffset = Number(o.timezoneOffsetOverride);
} else if (Number.isFinite(o.timezoneOffset)) { } else if (_.isFinite(o.timezoneOffset)) {
timezoneOffset = Number(o.timezoneOffset); timezoneOffset = Number(o.timezoneOffset);
} else { } else {
timezoneOffset = timezoneOffsetDefault; timezoneOffset = timezoneOffsetDefault;
@@ -85,65 +81,44 @@ export function startOfDay (options = {}) {
export function daysSince (yesterday, options = {}) { export function daysSince (yesterday, options = {}) {
let o = sanitizeOptions(options); let o = sanitizeOptions(options);
return startOfDay(defaults({ now: o.now }, o)).diff(startOfDay(defaults({ now: yesterday }, o)), 'days'); 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? Should the user do this task on this date, given the task's repeat options and user.preferences.dayStart?
*/ */
export function shouldDo (day, dailyTask) { export function shouldDo (day, dailyTask, options = {}) {
if (dailyTask.type !== 'daily') { if (dailyTask.type !== 'daily') {
return false; return false;
} }
let o = sanitizeOptions(options);
let startOfDayWithCDSTime = startOfDay(_.defaults({ now: day }, o));
day = moment(day).startOf('day').toDate(); // The time portion of the Start Date is never visible to or modifiable by the user so we must ignore it.
let startDate = moment(dailyTask.startDate).startOf('day').toDate(); // 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);
let daysOfTheWeek = []; taskStartDate = moment(taskStartDate).startOf('day');
if (taskStartDate > startOfDayWithCDSTime.startOf('day')) {
if (dailyTask.repeat) { return false; // Daily starts in the future
for (let [repeatDay, active] of Object.entries(dailyTask.repeat)) {
if (active) daysOfTheWeek.push(parseInt(DAY_MAPPING_STRING_TO_NUMBER[repeatDay], 10));
}
} }
if (dailyTask.frequency === 'daily') { // "Every X Days"
if (dailyTask.frequency === 'daily') { if (!dailyTask.everyX) {
if (!dailyTask.everyX) return false; // error condition return false; // error condition
let schedule = moment(startDate).recur()
.every(dailyTask.everyX).days();
return schedule.matches(day);
} else if (dailyTask.frequency === 'weekly') {
let schedule = moment(startDate).recur();
if (dailyTask.everyX > 1) {
schedule = schedule.every(dailyTask.everyX).weeks();
} }
let daysSinceTaskStart = startOfDayWithCDSTime.startOf('day').diff(taskStartDate, 'days');
schedule = schedule.every(daysOfTheWeek).daysOfWeek(); return daysSinceTaskStart % dailyTask.everyX === 0;
} else if (dailyTask.frequency === 'weekly') { // "On Certain Days of the Week"
return schedule.matches(day); if (!dailyTask.repeat) {
} else if (dailyTask.frequency === 'monthly') { return false; // error condition
let schedule = moment(startDate).recur();
let differenceInMonths = moment(day).month() + 1 - moment(startDate).month() + 1;
let matchEveryX = differenceInMonths % dailyTask.everyX === 0;
if (dailyTask.weeksOfMonth && dailyTask.weeksOfMonth.length > 0) {
schedule = schedule.every(daysOfTheWeek).daysOfWeek()
.every(dailyTask.weeksOfMonth).weeksOfMonthByDay();
} else if (dailyTask.daysOfMonth && dailyTask.daysOfMonth.length > 0) {
schedule = schedule.every(dailyTask.daysOfMonth).daysOfMonth();
} }
let dayOfWeekNum = startOfDayWithCDSTime.day(); // e.g., 0 for Sunday
return schedule.matches(day) && matchEveryX; return dailyTask.repeat[DAY_MAPPING[dayOfWeekNum]];
} else if (dailyTask.frequency === 'yearly') { } else {
let schedule = moment(startDate).recur(); return false; // error condition - unexpected frequency string
schedule = schedule.every(dailyTask.everyX).years();
return schedule.matches(day);
} }
}
return false;
}