RepetitionHelper.js

var async = require("async");
var dbCollections = require('../../../config/db/dbCollections');
var DateUtility = require('../../../helpers/DateUtility');

var paramInterface = {
    username: 'String',
    fromDate: 'Number',
    toDate: 'Number',
    templates: 'Array'
};

/**
 * Get all the habit & calendar items that repeats more than once
 * @param  {Object} DB  Database object
 * @param  {Date} date  Get items starting from this date
 * @return {Array}      List of habit & calendar items that repeats
 */
function getAllRepeatingItems(DB, params, callback) {
    params.toDate = new Date().getTime();

    var timestampQuery = {};
    var username = params.username;
    var end = params.toDate;

    var query = [
        {
            $match: {
                'user.username': username,
                'type': { $in: ['HABT','CLDR'] },
                'data.repetition.repeats': {
                    $in: ['DALY', 'WEEK', 'MTLY', 'ANLY']
                },
                'isLatest': true
            }
        },
        {
            $project: {
                _id: 0,
                document: "$$ROOT"
            }
        }
    ];

    console.log('Template query:');
    console.dir(JSON.stringify(query));

    var templates = [];

    function done(err, result) {
        if (result !== null) {
            templates.push(result.document);
        } else {
            params.repetitions = templates;
            console.log('No. of repeating templates: ' + templates.length);
            createAllRepetingItems(DB, params, templates, callback);
        }
    }

    if(DB) {
        DB.TemplateClln.aggregate(query).each(done);
    }
}

/**
 * Create all the habit & calendar items entries till today
 * @param  {Array} items List of habit and calendar items
 * @return {[type]}       Update database
 */
function createAllRepetingItems(DB, params, items, callback) {
    console.log('No of items being processed: ' + items.length);
    var toDate   = params.toDate;
    var username = params.username;
    var templates = [];
    var updates = [];

    for(var index in items) {
        var item = items[index];
        var repeats = item.data.repetition.repeats;
        var timestamp = item.timestamp;
        var isLatest = false;

        // CREATE REPETITIONS FOR CURRENT ITEM
        var next = {};
        /**
         * Create next day date incrementing only date,
         * keeping the time part untouched
         */
        timestamp = getNextDate(repeats, timestamp);
        if(timestamp === null) continue;

        // Is repetition to be created for this item
        if(isRepetitionApplicable(timestamp, toDate)) {
            var id = item._id;
            // using IIFE to pass correct id
            (function(id) {
                // Build an array of updates for document whose 'isLatest' is to be updated
                updates.push(
                    function (_callback) {
                        setLatestRepetitionItem(DB, id, _callback);
                    }
                );
            })(id);
        }

        /**
         * Check if repetition being created is till today(toDate).
         * Since the toDate changes as per time of the day,
         * check day without time.
         */
        while(isRepetitionApplicable(timestamp, toDate)) {
            // clone item details
            next = JSON.parse(JSON.stringify(item));
            next.timestamp = timestamp;
            next.latertime = timestamp;
            next.humantime = DateUtility.getTimeStr(timestamp);
            next.humandate = DateUtility.getDateStr(timestamp);
            next.user = {'username': params.username};
            next.data.isChecked = false;

            // Create next day date without time
            timestamp = getNextDate(repeats, timestamp);
            // Set 'isLatest' as true for the last repetition item
            next.isLatest = isRepetitionApplicable(timestamp, toDate) ? false : true;
            templates.push(next);
        }
    }

    console.log('No of templates to be created: ' + templates.length);

    if(templates.length > 0) {
        // Update 'isLatest' to false for all original item used
        async.parallel(updates, function(){
            console.log('Inserting all templates: ');
            insertAllRepetition(DB, templates, callback);
        });
    }
    else {
        // COMPLETED PROCESSING
        callback();
    }
}

/**
 * Get next date based upon the repepition interval
 * @param  {[type]} repetitionType [description]
 * @param  {[type]} timestamp      [description]
 * @return {[type]}                [description]
 */
function getNextDate(repetitionType, timestamp) {
    /**
     * Timestamp of next date
     * @type {Number}
     */
    var nextdate = null;
    switch (repetitionType) {
        case 'DALY':
            nextdate = DateUtility.addDays(timestamp, 1).getTime();
            break;
        case 'WEEK':
            nextdate = DateUtility.addDays(timestamp, 7).getTime();
            break;
        case 'MTLY':
            nextdate = DateUtility.addMonths(timestamp, 1).getTime();
            break;
        case 'ANLY':
            nextdate = DateUtility.addYears(timestamp, 1).getTime();
    }
    return nextdate;
}

/**
 * Is repepition being created is for day till today
 * @param  {Number} timestamp timestamp of current repetition
 * @param  {Number} toDate    timestamp for current date object
 * @return {Boolean}          Is repetition falls till today
 */
function isRepetitionApplicable(timestamp, toDate) {
    var firstDate = DateUtility.getDateOnly(timestamp).getTime();
    var secondDate = DateUtility.getDateOnly(toDate).getTime();
    var isApplicable = firstDate <= secondDate;
    if(!isApplicable) {
        console.log(firstDate + ' <= ' + secondDate + ': ' + false);
    }
    return isApplicable;
}

/**
 * Update 'isLatest' to false for original item used
 * for creating the repetition
 * @param {Object} DB Database object
 * @param {Number} id Unique id of the repetition document
 * @return {[type]}    Update database
 */
function setLatestRepetitionItem(DB, id, _callback) {
    DB.TemplateClln.update({_id: id}, {$set: {isLatest: false, modifiedAt: new Date().getTime()}},
        function(err, result) {
            if(err) {
                console.error(err);
            }
            else {
                console.log("Updated item in TemplateClln for: " + id);
                _callback();
            }
        });
}

/**
 * Insert all types of repetitions [DALY, WEEK, MTLY, ANLY] to DB
 * @param  {Object} DB Database object
 * @param  {Array} templates Database object array
 * @return {[type]}    Update database
 */
function insertAllRepetition(DB, templates, callback) {
    if(templates.length === 0) {
        // COMPLETED PROCESSING
        callback();
        return;
    }

    // remove all the id before deletion,
    // else the current id would be used by mongodb
    // and would result into error
    var newtemplates = [];
    for(var index in templates) {
        var item = templates[index];
        // Save parent id, else if new save id
        item.parentid = item.parentid ? item.parentid : item._id;
        delete item._id;
        newtemplates.push(item);
    }

    DB.TemplateClln.insert(newtemplates, function (err, data) {
        if (err) {
            console.error(err);
        } else {
            console.log(data);
            // COMPLETED PROCESSING
            callback();
        }
    });
}

/**
 * Build repetition based templates
 * @return {[type]} Update database based on repetition of templates
 */
function createRepetitionTemplates(DB, params, callback) {
    // BUILD HABIT & CALENDAR TILL DATE BEFORE RETRIEVING TEMPLATES
    // Get all the habit & calendar items that repeats more than 'ONCE'
    getAllRepeatingItems(DB, params, callback);
}

module.exports = {
    createRepetitionTemplates: createRepetitionTemplates
};