diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/auth.js similarity index 100% rename from website/src/controllers/api-v3/user.js rename to website/src/controllers/api-v3/auth.js diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website/src/models/task.js b/website/src/models/task.js index de488a3d86..e7760642bf 100644 --- a/website/src/models/task.js +++ b/website/src/models/task.js @@ -1,5 +1,6 @@ import mongoose from 'mongoose'; import shared from '../../../common'; +import validator from 'validator'; import moment from 'moment'; import baseModel from '../libs/api-v3/baseModel'; import _ from 'lodash'; @@ -15,10 +16,10 @@ let subDiscriminatorOptions = _.defaults(_.cloneDeep(discriminatorOptions), {_id // Task model the subclasses are not applied - check twice export let TaskSchema = new Schema({ - type: {type: String, enum: ['habit', 'todo', 'daily', 'reward'], required: true, default: 'habit'}, - text: String, + type: {type: String, enum: ['Habit', 'Todo', 'Daily', 'Reward'], required: true, default: 'Habit'}, + text: {type: String, required: true}, notes: {type: String, default: ''}, - tags: {type: Schema.Types.Mixed, default: {}}, // TODO dictionary? { "4ddf03d9-54bd-41a3-b011-ca1f1d2e9371" : true }, + tags: {type: Schema.Types.Mixed, default: {}}, // TODO dictionary? { "4ddf03d9-54bd-41a3-b011-ca1f1d2e9371" : true }, validate value: {type: Number, default: 0}, // redness priority: {type: Number, default: 1}, attribute: {type: String, default: 'str', enum: ['str', 'con', 'int', 'per']}, @@ -36,12 +37,12 @@ export let TaskSchema = new Schema({ }, discriminatorOptions)); TaskSchema.plugin(baseModel, { - noSet: [], + noSet: ['challenge', 'userId', 'value', 'completed', 'history', 'streak', 'dateCompleted'], // TODO checklist fields editable? private: [], timestamps: true, }); -export let Task = mongoose.model('Task', TaskSchema); +export let TaskModel = mongoose.model('Task', TaskSchema); // TODO discriminators: it's very important to check that the options and plugins of the parent schema are used in the sub-schemas too @@ -58,18 +59,19 @@ let dailyTodoSchema = () => { collapseChecklist: {type: Boolean, default: false}, checklist: [{ completed: {type: Boolean, default: false}, - text: String, - _id: {type: String, default: shared.uuid}, + text: {type: String, required: true}, + _id: {type: String, default: shared.uuid, validate: [validator.isUUID, 'Invalid uuid.']}, }], }; }; -export let Habit = Task.discriminator('Habit', new Schema(_.defaults({ +export let HabitSchema = new Schema(_.defaults({ up: {type: Boolean, default: true}, down: {type: Boolean, default: true}, -}, habitDailySchema()), subDiscriminatorOptions)); +}, habitDailySchema()), subDiscriminatorOptions); +export let HabitModel = TaskModel.discriminator('Habit', HabitSchema); -export let Daily = Task.discriminator('Daily', new Schema(_.defaults({ +export let DailySchema = new Schema(_.defaults({ frequency: {type: String, default: 'weekly', enum: ['daily', 'weekly']}, everyX: {type: Number, default: 1}, // e.g. once every X weeks startDate: { @@ -88,13 +90,16 @@ export let Daily = Task.discriminator('Daily', new Schema(_.defaults({ su: {type: Boolean, default: true}, }, streak: {type: Number, default: 0}, -}, habitDailySchema(), dailyTodoSchema()), subDiscriminatorOptions)); +}, habitDailySchema(), dailyTodoSchema()), subDiscriminatorOptions); +export let DailyModel = TaskModel.discriminator('Daily', DailySchema); -export let Todo = Task.discriminator('Todo', new Schema(_.defaults({ +export let TodoSchema = new Schema(_.defaults({ dateCompleted: Date, // FIXME we're getting parse errors, people have stored as "today" and "3/13". Need to run a migration & put this back to type: Date // TODO change field name date: String, // due date for todos -}, dailyTodoSchema()), subDiscriminatorOptions)); +}, dailyTodoSchema()), subDiscriminatorOptions); +export let TodoModel = TaskModel.discriminator('Todo', TodoSchema); -export let Reward = Task.discriminator('Reward', new Schema({}, subDiscriminatorOptions)); +export let RewardSchema = new Schema({}, subDiscriminatorOptions); +export let RewardModel = TaskModel.discriminator('Reward', RewardSchema); diff --git a/website/src/models/user.js b/website/src/models/user.js index 04c49d036f..8c45fb1b2b 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -3,7 +3,8 @@ import shared from '../../../common'; import _ from 'lodash'; import validator from 'validator'; import moment from 'moment'; -import { model as Task } from './task'; +import * as Tasks from './task'; +import Q from 'q'; import baseModel from '../libs/api-v3/baseModel'; // import {model as Challenge} from './challenge'; @@ -491,7 +492,9 @@ schema.post('init', function postInitUser (doc) { }); function _populateDefaultTasks (user, taskTypes) { - if ('tags' in taskTypes) { + let tagsI = taskTypes.indexOf('tags'); + + if (tagsI !== -1) { user.tags = _.map(shared.content.userDefaults.tags, (tag) => { let newTag = _.cloneDeep(tag); @@ -505,9 +508,10 @@ function _populateDefaultTasks (user, taskTypes) { let tasksToCreate = []; + taskTypes = tagsI !== -1 ? _.clone(taskTypes).slice(tagsI, 1) : taskTypes; _.each(taskTypes, (taskType) => { let tasksOfType = _.map(shared.content.userDefaults[taskType], (taskDefaults) => { - let newTask = new Task(taskDefaults); + let newTask = new Tasks[taskType.charAt(0).toUpperCase() + taskType.slice(1)](taskDefaults); newTask.userId = user._id; newTask.text = newTask.text(user.preferences.language); @@ -518,12 +522,19 @@ function _populateDefaultTasks (user, taskTypes) { return checklistItem; }); } + + return newTask.save(); }); - tasksToCreate.push(...tasksOfType); + tasksToCreate.push(...tasksOfType); // TODO find better way since this creates each task individually }); - return Task.create(tasksToCreate); + return Q.all(tasksToCreate) + .then((tasksCreated) => { + _.each(tasksCreated, (task) => { + user.tasksOrder[`${task.type.toLowerCase()}s`].push(task._id); + }); + }); } function _populateDefaultsForNewUser (user) { @@ -559,7 +570,7 @@ function _setProfileName (user) { return localUsername || facebookUsername || anonymous; } -schema.pre('save', function postSaveUser (next, done) { +schema.pre('save', true, function preSaveUser (next, done) { next(); // TODO remove all unnecessary checks