From 727cdc940209b5ad84227aeba444d678924687cd Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Wed, 10 May 2017 07:40:45 -0600 Subject: [PATCH] Tasks is due (#8711) * Added isDue field and isDue set on create * Added isDue update on update task * Add isdue calc to score task * Added isdue calc to cron * Fixed lint issue * Added isDue to no set and updated grammar --- .../tasks/POST-tasks_id_score_direction.test.js | 7 +++++++ test/api/v3/integration/tasks/POST-tasks_user.test.js | 1 + test/api/v3/integration/tasks/PUT-tasks_id.test.js | 3 +++ test/api/v3/unit/libs/cron.test.js | 8 ++++++++ website/server/controllers/api-v3/tasks.js | 9 +++++++++ website/server/libs/cron.js | 1 + website/server/libs/taskManager.js | 3 +++ website/server/models/task.js | 3 ++- 8 files changed, 34 insertions(+), 1 deletion(-) diff --git a/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js b/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js index 20b03b302c..0918780bae 100644 --- a/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js @@ -208,6 +208,13 @@ describe('POST /tasks/:id/score/:direction', () => { expect(task.completed).to.equal(false); }); + it('computes isDue', async () => { + await user.post(`/tasks/${daily._id}/score/up`); + let task = await user.get(`/tasks/${daily._id}`); + + expect(task.isDue).to.equal(true); + }); + it('scores up daily even if it is already completed'); // Yes? it('scores down daily even if it is already uncompleted'); // Yes? diff --git a/test/api/v3/integration/tasks/POST-tasks_user.test.js b/test/api/v3/integration/tasks/POST-tasks_user.test.js index 96506d2461..c1385ea93e 100644 --- a/test/api/v3/integration/tasks/POST-tasks_user.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_user.test.js @@ -509,6 +509,7 @@ describe('POST /tasks/user', () => { expect(task.daysOfMonth).to.eql([15]); expect(task.weeksOfMonth).to.eql([3]); expect(new Date(task.startDate)).to.eql(now); + expect(task.isDue).to.be.true; }); it('creates multiple dailys', async () => { diff --git a/test/api/v3/integration/tasks/PUT-tasks_id.test.js b/test/api/v3/integration/tasks/PUT-tasks_id.test.js index cb0a715caf..ce17b9c94b 100644 --- a/test/api/v3/integration/tasks/PUT-tasks_id.test.js +++ b/test/api/v3/integration/tasks/PUT-tasks_id.test.js @@ -1,3 +1,4 @@ +import moment from 'moment'; import { generateUser, generateGroup, @@ -395,12 +396,14 @@ describe('PUT /tasks/:id', () => { notes: 'some new notes', frequency: 'daily', everyX: 5, + startDate: moment().add(1, 'days').toDate(), }); expect(savedDaily.text).to.eql('some new text'); expect(savedDaily.notes).to.eql('some new notes'); expect(savedDaily.frequency).to.eql('daily'); expect(savedDaily.everyX).to.eql(5); + expect(savedDaily.isDue).to.be.false; }); it('can update checklists (replace it)', async () => { diff --git a/test/api/v3/unit/libs/cron.test.js b/test/api/v3/unit/libs/cron.test.js index df8becc493..93d164f1fd 100644 --- a/test/api/v3/unit/libs/cron.test.js +++ b/test/api/v3/unit/libs/cron.test.js @@ -358,6 +358,14 @@ describe('cron', () => { }; }); + it('computes isDue', () => { + tasksByType.dailys[0].frequency = 'daily'; + tasksByType.dailys[0].everyX = 5; + tasksByType.dailys[0].startDate = moment().add(1, 'days').toDate(); + cron({user, tasksByType, daysMissed, analytics}); + expect(tasksByType.dailys[0].isDue).to.be.false; + }); + it('should add history', () => { cron({user, tasksByType, daysMissed, analytics}); expect(tasksByType.dailys[0].history).to.be.lengthOf(1); diff --git a/website/server/controllers/api-v3/tasks.js b/website/server/controllers/api-v3/tasks.js index ff7d90505a..401a432e24 100644 --- a/website/server/controllers/api-v3/tasks.js +++ b/website/server/controllers/api-v3/tasks.js @@ -431,6 +431,7 @@ api.updateTask = { } else if (task.userId !== user._id) { // If the task is owned by a user make it's the current one throw new NotFound(res.t('taskNotFound')); } + let oldCheckList = task.checklist; // we have to convert task to an object because otherwise things don't get merged correctly. Bad for performances? let [updatedTaskObj] = common.ops.updateTask(task.toObject(), req); @@ -455,6 +456,10 @@ api.updateTask = { task.group.approval.required = true; } + if (sanitizedObj.type === 'daily') { + task.isDue = common.shouldDo(Date.now(), sanitizedObj, user.preferences); + } + let savedTask = await task.save(); if (group && task.group.id && task.group.assignedUsers.length > 0) { @@ -584,6 +589,10 @@ api.scoreTask = { } } + if (task.type === 'daily') { + task.isDue = common.shouldDo(Date.now(), task, user.preferences); + } + let results = await Bluebird.all([ user.save(), task.save(), diff --git a/website/server/libs/cron.js b/website/server/libs/cron.js index ec7f5915d7..88a119c4f6 100644 --- a/website/server/libs/cron.js +++ b/website/server/libs/cron.js @@ -314,6 +314,7 @@ export function cron (options = {}) { value: task.value, }); task.completed = false; + task.isDue = common.shouldDo(Date.now(), task, user.preferences); if (completed || scheduleMisses > 0) { if (task.checklist) { diff --git a/website/server/libs/taskManager.js b/website/server/libs/taskManager.js index 163dbc452c..5c2de7d5c4 100644 --- a/website/server/libs/taskManager.js +++ b/website/server/libs/taskManager.js @@ -4,6 +4,7 @@ import { } from './errors'; import Bluebird from 'bluebird'; import _ from 'lodash'; +import shared from '../../common'; async function _validateTaskAlias (tasks, res) { let tasksWithAliases = tasks.filter(task => task.alias); @@ -63,6 +64,8 @@ export async function createTasks (req, res, options = {}) { newTask.userId = user._id; } + if (newTask.type === 'daily') newTask.isDue = shared.shouldDo(Date.now(), newTask, user.preferences); + // Validate that the task is valid and throw if it isn't // otherwise since we're saving user/challenge/group and task in parallel it could save the user/challenge/group with a tasksOrder that doens't match reality let validationErrors = newTask.validateSync(); diff --git a/website/server/models/task.js b/website/server/models/task.js index b28c8c08e1..182c174ce8 100644 --- a/website/server/models/task.js +++ b/website/server/models/task.js @@ -92,7 +92,7 @@ export let TaskSchema = new Schema({ }, discriminatorOptions)); TaskSchema.plugin(baseModel, { - noSet: ['challenge', 'userId', 'completed', 'history', 'dateCompleted', '_legacyId', 'group'], + noSet: ['challenge', 'userId', 'completed', 'history', 'dateCompleted', '_legacyId', 'group', 'isDue'], sanitizeTransform (taskObj) { if (taskObj.type && taskObj.type !== 'reward') { // value should be settable directly only for rewards delete taskObj.value; @@ -242,6 +242,7 @@ export let DailySchema = new Schema(_.defaults({ streak: {type: Number, default: 0}, daysOfMonth: {type: [Number], default: []}, // Days of the month that the daily should repeat on weeksOfMonth: {type: [Number], default: []}, // Weeks of the month that the daily should repeat on + isDue: {type: Boolean}, }, habitDailySchema(), dailyTodoSchema()), subDiscriminatorOptions); export let daily = Task.discriminator('daily', DailySchema);