mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
* Implemented repeat after completion * Added tests for repeat after completion in shouldDo.test.js * Remove lastTicked * Undoes removal of website/client/README.md
This commit is contained in:
@@ -278,6 +278,68 @@ describe('shouldDo', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context('When repeat after completion is on', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
dailyTask.repeatAfterCompletion = true;
|
||||||
|
dailyTask.everyX = 5;
|
||||||
|
day = moment('2017-05-01').toDate();
|
||||||
|
dailyTask.startDate = day;
|
||||||
|
});
|
||||||
|
|
||||||
|
context('last completed is set', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
day = moment('2017-05-03').toDate();
|
||||||
|
dailyTask.lastCompleted = day;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should compute daily nextDue values', () => {
|
||||||
|
options.timezoneOffset = 0;
|
||||||
|
options.nextDue = true;
|
||||||
|
|
||||||
|
nextDue = shouldDo(day, dailyTask, options);
|
||||||
|
expect(nextDue.length).to.eql(6);
|
||||||
|
expect(moment(nextDue[0]).toDate()).to.eql(moment.utc('2017-05-08').toDate());
|
||||||
|
expect(moment(nextDue[1]).toDate()).to.eql(moment.utc('2017-05-09').toDate());
|
||||||
|
expect(moment(nextDue[2]).toDate()).to.eql(moment.utc('2017-05-10').toDate());
|
||||||
|
expect(moment(nextDue[3]).toDate()).to.eql(moment.utc('2017-05-11').toDate());
|
||||||
|
expect(moment(nextDue[4]).toDate()).to.eql(moment.utc('2017-05-12').toDate());
|
||||||
|
expect(moment(nextDue[5]).toDate()).to.eql(moment.utc('2017-05-13').toDate());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false before X Days passes after completion', () => {
|
||||||
|
day = moment('2017-05-05').toDate();
|
||||||
|
expect(shouldDo(day, dailyTask, options)).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true after X Days passes after completion', () => {
|
||||||
|
day = moment('2017-05-10').toDate();
|
||||||
|
expect(shouldDo(day, dailyTask, options)).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('last completed is not set', () => {
|
||||||
|
it('should compute daily nextDue values', () => {
|
||||||
|
options.timezoneOffset = 0;
|
||||||
|
options.nextDue = true;
|
||||||
|
|
||||||
|
nextDue = shouldDo(day, dailyTask, options);
|
||||||
|
expect(nextDue.length).to.eql(6);
|
||||||
|
expect(moment(nextDue[0]).toDate()).to.eql(moment.utc('2017-05-02').toDate());
|
||||||
|
expect(moment(nextDue[1]).toDate()).to.eql(moment.utc('2017-05-03').toDate());
|
||||||
|
expect(moment(nextDue[2]).toDate()).to.eql(moment.utc('2017-05-04').toDate());
|
||||||
|
expect(moment(nextDue[3]).toDate()).to.eql(moment.utc('2017-05-05').toDate());
|
||||||
|
expect(moment(nextDue[4]).toDate()).to.eql(moment.utc('2017-05-06').toDate());
|
||||||
|
expect(moment(nextDue[5]).toDate()).to.eql(moment.utc('2017-05-07').toDate());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true after start date', () => {
|
||||||
|
day = moment('2017-05-04').toDate();
|
||||||
|
expect(shouldDo(day, dailyTask, options)).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
context('If number of X days is zero', () => {
|
context('If number of X days is zero', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dailyTask.everyX = 0;
|
dailyTask.everyX = 0;
|
||||||
|
|||||||
@@ -95,6 +95,12 @@
|
|||||||
input(type="number", v-model="task.everyX", min="0", required)
|
input(type="number", v-model="task.everyX", min="0", required)
|
||||||
| {{ repeatSuffix }}
|
| {{ repeatSuffix }}
|
||||||
br
|
br
|
||||||
|
template(v-if="task.frequency === 'daily'")
|
||||||
|
.form-check
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", v-model="task.repeatAfterCompletion")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description {{ $t('repeatAfterCompletionTitle', {everyX: task.everyX}) }}
|
||||||
template(v-if="task.frequency === 'weekly'")
|
template(v-if="task.frequency === 'weekly'")
|
||||||
.form-check-inline.weekday-check(
|
.form-check-inline.weekday-check(
|
||||||
v-for="(day, dayNumber) in ['su','m','t','w','th','f','s']",
|
v-for="(day, dayNumber) in ['su','m','t','w','th','f','s']",
|
||||||
|
|||||||
@@ -169,6 +169,7 @@
|
|||||||
"taskApprovalHasBeenRequested": "Approval has been requested",
|
"taskApprovalHasBeenRequested": "Approval has been requested",
|
||||||
"approvals": "Approvals",
|
"approvals": "Approvals",
|
||||||
"approvalRequired": "Approval Required",
|
"approvalRequired": "Approval Required",
|
||||||
|
"repeatAfterCompletionTitle": "Repeat Every <%= everyX %> Days Since Last Completed",
|
||||||
"repeatZero": "Daily is never due",
|
"repeatZero": "Daily is never due",
|
||||||
"repeatType": "Repeat Type",
|
"repeatType": "Repeat Type",
|
||||||
"repeatTypeHelpTitle": "What kind of repeat is this?",
|
"repeatTypeHelpTitle": "What kind of repeat is this?",
|
||||||
|
|||||||
@@ -122,19 +122,39 @@ export function shouldDo (day, dailyTask, options = {}) {
|
|||||||
|
|
||||||
if (dailyTask.frequency === 'daily') {
|
if (dailyTask.frequency === 'daily') {
|
||||||
if (!dailyTask.everyX) return false; // error condition
|
if (!dailyTask.everyX) return false; // error condition
|
||||||
let schedule = moment(startDate).recur()
|
let lastCompletedDate;
|
||||||
.every(dailyTask.everyX).days();
|
if (dailyTask.repeatAfterCompletion && dailyTask.lastCompleted) {
|
||||||
|
lastCompletedDate = moment(dailyTask.lastCompleted).zone(o.timezoneOffset).startOf('day');
|
||||||
|
}
|
||||||
|
|
||||||
if (options.nextDue) {
|
if (options.nextDue) {
|
||||||
let filteredDates = [];
|
let filteredDates = [];
|
||||||
for (let i = 1; filteredDates.length < 6; i++) {
|
for (let i = 1; filteredDates.length < 6; i++) {
|
||||||
let calcDate = moment(startDate).add(dailyTask.everyX * i, 'days');
|
let calcDate;
|
||||||
|
if (dailyTask.repeatAfterCompletion) {
|
||||||
|
if (lastCompletedDate) {
|
||||||
|
calcDate = moment(lastCompletedDate).add(dailyTask.everyX + i - 1, 'days');
|
||||||
|
} else {
|
||||||
|
calcDate = moment(startDate).add(i, 'days');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
calcDate = moment(startDate).add(dailyTask.everyX * i, 'days');
|
||||||
|
}
|
||||||
if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate);
|
if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate);
|
||||||
}
|
}
|
||||||
return filteredDates;
|
return filteredDates;
|
||||||
}
|
}
|
||||||
|
|
||||||
return schedule.matches(startOfDayWithCDSTime);
|
if (dailyTask.repeatAfterCompletion) {
|
||||||
|
if (lastCompletedDate) {
|
||||||
|
return moment(lastCompletedDate).add(dailyTask.everyX, 'days').isSameOrBefore(startOfDayWithCDSTime);
|
||||||
|
} else {
|
||||||
|
return moment(startDate).isSameOrBefore(startOfDayWithCDSTime);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let schedule = moment(startDate).recur().every(dailyTask.everyX).days();
|
||||||
|
return schedule.matches(startOfDayWithCDSTime);
|
||||||
|
}
|
||||||
} else if (dailyTask.frequency === 'weekly') {
|
} else if (dailyTask.frequency === 'weekly') {
|
||||||
let schedule = moment(startDate).recur();
|
let schedule = moment(startDate).recur();
|
||||||
|
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ export function cron (options = {}) {
|
|||||||
if (dailiesDaysMissed > 1) dailiesDaysMissed = 1;
|
if (dailiesDaysMissed > 1) dailiesDaysMissed = 1;
|
||||||
|
|
||||||
if (completed) {
|
if (completed) {
|
||||||
|
task.lastCompleted = moment(now).subtract({days: 1}).toDate();
|
||||||
dailyChecked += 1;
|
dailyChecked += 1;
|
||||||
if (!atLeastOneDailyDue) { // only bother checking until the first thing is found
|
if (!atLeastOneDailyDue) { // only bother checking until the first thing is found
|
||||||
let thatDay = moment(now).subtract({days: daysMissed});
|
let thatDay = moment(now).subtract({days: daysMissed});
|
||||||
|
|||||||
@@ -224,6 +224,8 @@ export let habit = Task.discriminator('habit', HabitSchema);
|
|||||||
export let DailySchema = new Schema(_.defaults({
|
export let DailySchema = new Schema(_.defaults({
|
||||||
frequency: {type: String, default: 'weekly', enum: ['daily', 'weekly', 'monthly', 'yearly']},
|
frequency: {type: String, default: 'weekly', enum: ['daily', 'weekly', 'monthly', 'yearly']},
|
||||||
everyX: {type: Number, default: 1}, // e.g. once every X weeks
|
everyX: {type: Number, default: 1}, // e.g. once every X weeks
|
||||||
|
repeatAfterCompletion: {type: Boolean, default: false},
|
||||||
|
lastCompleted: Date,
|
||||||
startDate: {
|
startDate: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default () {
|
default () {
|
||||||
|
|||||||
Reference in New Issue
Block a user