var mongoose = require('mongoose'); const _ = require('lodash'); const tools = require('../tools/general'); var { Project } = require('./project'); var server_constants = require('../tools/server_constants'); mongoose.Promise = global.Promise; mongoose.level = "F"; // Resolving error Unknown modifier: $pushAll mongoose.plugin(schema => { schema.options.usePushEach = true }); mongoose.set('debug', process.env.DEBUG); var TodoSchema = new mongoose.Schema({ userId: { type: String, }, pos: { type: Number, }, category: mongoose.Schema.Types.ObjectId, descr: { type: String, }, priority: { type: Number, }, statustodo: { type: Number, default: 0 }, created_at: { type: Date }, modify_at: { type: Date }, start_date: { type: Date, }, completed_at: { type: Date }, expiring_at: { type: Date, }, enableExpiring: { type: Boolean, default: false }, id_prev: mongoose.Schema.Types.ObjectId, progress: { type: Number, }, phase: { type: Number, }, assigned_to_userId: { type: String, }, hoursplanned: { type: Number, }, hoursworked: { type: Number, }, themecolor: { type: String, }, themebgcolor: { type: String, default: '', }, modified: { type: Boolean, }, }); TodoSchema.methods.toJSON = function () { var todo = this; var todoObject = todo.toObject(); // console.log(todoObject); return _.pick(todoObject, tools.allfieldTodoWithId()); }; TodoSchema.statics.findByUserIdAndIdParent = function (userId, category, phase = '') { var Todo = this; let tofind = { 'category': category, }; if (userId !== '') { tofind['userId'] = userId; } if (!!phase) { tofind['phase'] = phase; } return Todo.find(tofind); }; // User.find({ admin: true }).where('created_at').gt(monthAgo).exec(function(err, users) { // if (err) throw err; function getQueryFilterTodo(userId) { myobj = [{ privacyread: server_constants.Privacy.all }, { userId: userId }]; return myobj; } function getQueryTodo(filterMatchBefore = {}, userId) { let filterMatchAfter = { $or: getQueryFilterTodo(userId) }; let myobjField = getobjFieldTodo(true); const query = [ { $match: filterMatchBefore }, { $lookup: { from: "projects", localField: "category", // field in the Todo collection foreignField: "_id", // field in the projects collection as: "fromItems" } }, { $replaceRoot: { newRoot: { $mergeObjects: [{ $arrayElemAt: ["$fromItems", 0] }, "$$ROOT"] } } }, { $match: filterMatchAfter }, { $project: myobjField }]; return query; } TodoSchema.statics.findAllByUserIdAndCat = function (userId) { var Todo = this; const query = getQueryTodo({}, userId); return Todo.aggregate(query) .then(ris => { // console.log('ris TODO:', ris); //return tools.mapSort(ris) return ris }); }; TodoSchema.statics.getArrIdParentInTable = function (userId) { var Todo = this; return Todo.find(getQueryFilterTodo(userId)).distinct("category") .then(arrcategory => { return arrcategory }) }; function getobjFieldTodo(withprojectfield) { let objfield = {}; for (const field of tools.allfieldTodo()) { objfield[field] = 1; } if (withprojectfield) { objfield["privacyread"] = 1; objfield["privacywrite"] = 1; } // console.log("getobjFieldTodo", objfield); return objfield } TodoSchema.statics.enabletoModify = async function (userId, idTodo) { var Todo = this; let params = {}; params['_id'] = ObjectId(idTodo); const query = getQueryTodo(params, userId); return Todo.aggregate(query) .then(ris => { return (!!ris); }) .catch(err => { console.log("ERR: ".err); return false; }); }; TodoSchema.statics.getAllTodo = async function (userId) { var Todo = this; const arralltodo = await Todo.findAllByUserIdAndCat(userId); // console.log('arralltodo', arralltodo); let obj = []; obj.arrcategories = await Todo.getArrIdParentInTable(userId); // console.log('obj.arrcategories', obj.arrcategories); let arrtodos = []; if (obj.arrcategories.length > 0) { for (const mycat of obj.arrcategories) { if (!!arralltodo) { const arrfiltrato = arralltodo.filter(item => item.category.toString() === mycat.toString()); if (arrfiltrato.length > 0) { const arrmap = tools.mapSort(arrfiltrato); arrtodos.push(arrmap); // console.log('AGGIUNGI RECORDS TODO! cat: ', mycat, 'da aggiungere:', arrfiltrato.length, 'attuali', arrtodos.length); // console.log(arrtodos) } else { arrtodos.push([]); } } } } obj.arrtodos = arrtodos; // console.log('obj.arrtodos:'); // logtodo(obj.arrtodos); return obj; }; function getelem(item) { return item.descr + ' ' + item.category; } function logtodo(myarr) { console.log(getarrlog(myarr)); } function getarrlog(myarr) { let mystr = ""; for (const indcat in myarr) { mystr += indcat + ': \n'; for (const indelem in myarr[indcat]) { mystr += ' [' + indelem + '] ' + getelem(myarr[indcat][indelem]) + '\n'; } } return mystr } class CalcTodo { constructor(phase) { this.mydata = { phase: phase, numitem: 0 }; this.clean() } clean() { this.mydata.hoursleft = 0; this.mydata.hoursplanned = 0; this.mydata.hoursworked = 0; this.mydata.progressCalc = 0; } addDataProj(datain) { if (!!datain) { if (datain.actualphase === this.mydata.phase) { CalcTodo.addFields(this.mydata, datain, true); } } } addDataTodo(datain) { if (!!datain) { if (datain.phase === this.mydata.phase) { CalcTodo.addFields(this.mydata, datain, false); } } } static addFields(recout, recin, isproj) { // console.log('addFields', recin); recout.hoursworked += recin.hoursworked; recout.hoursplanned += recin.hoursplanned; let hoursleft = (recin.hoursplanned - recin.hoursworked); if (hoursleft < 0) hoursleft = 0; recout.hoursleft += hoursleft; if (recin.progress === undefined) { recout.progressCalc += recin.progressCalc; } else { recout.progressCalc += recin.progress; } recout.numitem++; } static copyFields(recout, recin) { recout.hoursworked = recin.hoursworked; recout.hoursplanned = recin.hoursplanned; recout.hoursleft = recin.hoursleft; if (recin.progress === undefined) recout.progressCalc = recin.progressCalc; else { if (!!recin.progressCalc) recout.progressCalc = recin.progressCalc; else recout.progressCalc = 0; } } setValuesToRecord(objout) { CalcTodo.copyFields(objout, this.mydata) } endDataCalc() { if (this.mydata.numitem > 0) { this.mydata.progressCalc = Math.round(this.mydata.progressCalc / this.mydata.numitem); } console.log('this.mydata.progressCalc', this.mydata.progressCalc) } getData() { // return tools.jsonCopy(this.mydata); return { ...this.mydata } } } TodoSchema.statics.calculateTreeTodo = async function (actualphase, userId, idproj, calcalsoUpper, masterproj, nocalcDown) { // console.log('calculateTreeTodo', 'actualphase', actualphase, idproj); const myrecproj = await Project.findProjectByUserId(userId, idproj); // const id_parent = await Project.getIdParentByIdProj(idproj); let objdata = new CalcTodo(actualphase); let promiseChain = Promise.resolve(); return await Project.findByUserIdAndIdParent(userId, idproj) .then(arrsubproj => { // console.log(' ', arrsubproj.length, 'SubProjects trovati'); // console.log('arrsubproj', 'userId', userId, 'idproj', idproj, arrsubproj.length); if (!nocalcDown) { // 1) Calculate the SubProjects of this project Main for (const subproj of arrsubproj) { if (!calcalsoUpper) { // not include the first Project because it was already calculated before promiseChain = promiseChain.then(() => { return Todo.calculateTreeTodo(actualphase, userId, subproj._id, calcalsoUpper, masterproj, true) .then((subobjdata) => { objdata.addDataProj(subobjdata); }); }); } else { promiseChain = promiseChain.then(() => { objdata.addDataProj(subproj); }); } } } return promiseChain; }) .then(() => { // console.log('objdata', objdata); // 2) Calculate the Todos of this project if (!!myrecproj) return Todo.calculateTodoHoursAndProgress(userId, idproj, myrecproj.actualphase); else return null }) .then((objdatatodos) => { if (!!objdatatodos) { if (!!myrecproj) { if (myrecproj.actualphase === actualphase) { // console.log('objdatatodos', objdatatodos); objdata.addDataTodo(objdatatodos); // End Calculate objdata.endDataCalc(); // Update into the DB: return Project.updateCalc(userId, idproj, objdata, null) .then((ris) => { if (ris) return objdata.getData(); else return null; }); } } } return null; }) .then((ris) => { if (calcalsoUpper) { if (!!myrecproj) { if (!!myrecproj.id_parent) { // Calculate also the upper Projects ! return new Promise((resolve, reject) => { Todo.calculateTreeTodo(actualphase, userId, myrecproj.id_parent, true, masterproj, false); resolve(ris) }); } else { return new Promise((resolve, reject) => { resolve() }); } } } return new Promise((resolve, reject) => { resolve() }); }) }; TodoSchema.statics.calculateTodoHoursAndProgress = async function (userId, idproj, actualphase) { var Todo = this; let objdata = new CalcTodo(actualphase); return await Todo.findByUserIdAndIdParent(userId, idproj, actualphase) .then(arrtodo => { // console.log(' calculateTodo *', arrtodo.length, '* FOUND'); for (let itemtodo of arrtodo) { objdata.addDataTodo(itemtodo); } objdata.endDataCalc(); return objdata.getData(); }) .catch(e => { console.log('Error: ', e); return null; }); }; TodoSchema.pre('save', function (next) { // var todo = this; // console.log('todo.expiring_at', todo.expiring_at); next(); }); var Todo = mongoose.model('Todos', TodoSchema); module.exports = { Todo };