Skip to content
This repository has been archived by the owner on May 16, 2023. It is now read-only.

Commit

Permalink
forecast notification provider #14
Browse files Browse the repository at this point in the history
  • Loading branch information
gacek85 committed Apr 26, 2016
1 parent 7fd7571 commit 655b94b
Show file tree
Hide file tree
Showing 8 changed files with 498 additions and 12 deletions.
6 changes: 5 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ var
notifier = require('./app/services/notifier'),
reportNotifier = require('./app/services/report')(slack, harvest),
slackNotifier = require('./app/services/slack/notifier')(slack, harvest),
slackForecastNotifier = require('./app/services/slack/notifier/forecast')(slack, harvest),
slackReminder = require('./app/services/slack/notifier/remind')(slack, harvest),
i18n = require('i18n'),
forecast = require('./app/services/forecast')('default', config.forecast),
server
;


i18n.configure({
locales : ['en'],
directory: __dirname + '/locales',
Expand All @@ -41,8 +44,9 @@ i18n.configure({
harvest.setUsers(config.users);
slack.setUsers(config.users);

// Defining two notification channels
// Defining three notification channels
notifier.addNotifier('users', slackNotifier);
notifier.addNotifier('forecast', slackForecastNotifier);
notifier.addNotifier('reminder', slackReminder);
notifier.addNotifier('management', reportNotifier);

Expand Down
35 changes: 27 additions & 8 deletions app/services/cronjobs/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
/*jshint node: true*/
'use strict';

var cron = require('cron'),
notifier = require('./../notifier'),
harvest = require('./../harvest')('default'),
_ = require('lodash'),
tools = require('./../tools.js'),
logger = require('./../logger.js')('default');
var
jobs = require('./lib/jobs.js'),
walk = require('walk')
;



module.exports = function (app, config) {

var cronJobs = require('./lib/jobs.js')(config);
cronJobs.run();
var walker,
cronJobs = jobs(config)
;

walker = walk.walk(__dirname + '/jobs', {
followLinks : false
});

walker.on('file', function (root, stat, next) {
var file = __dirname + '/jobs/' + stat.name,
baseName = stat.name.substr(0, stat.name.length - 3),
conf = config[baseName] || {},
job = require(file)
;

cronJobs.addJob(job, conf);

next();
});

walker.on('end', function () {
cronJobs.run();
});
};
83 changes: 83 additions & 0 deletions app/services/cronjobs/jobs/forecast.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*jshint node: true*/
'use strict';

var
i18n = require('i18n'),
notifier = require('./../../notifier/index.js')
consts = require('./../../../../consts.json'),
forecast = require('./../../forecast')('default'),
logger = require('./../../logger.js')('default'),
moment = require('moment'),
job = {

/**
* Returns the job function
*
* @param {Object} config
* @returns {Function}
*/
getJob : function (config)
{
return function ()
{
var options = {
startDate : moment().startOf('day'),
endDate : moment().endOf('day')
};

forecast.assignments(options, function (error, assignments) {
if (error) {
logger.error(i18n.__('Failed loading forecast schedule.', {}));
} else {
notifier.notify('forecast', {
assignments : assignments
});
}
});
};
},

/**
* Formats the cron time according to given config
*
* @param {Object} config
* @returns {String} The cron time format string
*/
getCronTime : function (config)
{
var cronTime;
if (!!config.cronTime) {
cronTime = config.cronTime;
} else if (!!config.minutes && !!config.hour) {
cronTime = '00 ' + config.minutes + ' ' + config.hour + ' * * 1-5';
}

return !!cronTime ? cronTime : consts.forecast.CRON_TIME; // by default every midday of working day;
},

/**
* Defines if given job should be ran independently from setting it up
* with cron
*
* @returns {Boolean}
*/
shouldRunNow : function (config)
{
return true;
},


/**
* Returns description of the task
*
* @returns {String}
*/
getDescription : function ()
{
return i18n.__('User notifications for their forecast schedule.');
}
}
;


module.exports = job;
8 changes: 6 additions & 2 deletions app/services/cronjobs/lib/jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

var notifier = require('./../../notifier/index.js'),
harvest = require('./../../harvest')('default'),
forecast = require('./../../forecast')('default'),
_ = require('lodash'),
tools = require('./../../tools.js'),
logger = require('./../../logger.js')('default'),
Expand Down Expand Up @@ -167,6 +168,8 @@ var defaultJobs = {
harvest.doGetProjects();
logger.info(i18n.__('Loading clients from Harvest API...'), {});
harvest.doGetClients();
logger.info(i18n.__('Loading forecast clients/projects/people from Forecast API...'));
forecast.preload(true);
};
},

Expand Down Expand Up @@ -331,8 +334,9 @@ var defaultJobs = {

module.exports = function (config, additionalJobs)
{
var jobsHolder = new JobsHolder();
var jobs = _.assign(defaultJobs, additionalJobs);
var jobsHolder = new JobsHolder(),
jobs = _.assign(defaultJobs, additionalJobs)
;
_.each(config, function (configValues, jobName) {
var job = jobs[jobName];
if (!!job) {
Expand Down
180 changes: 180 additions & 0 deletions app/services/forecast/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*jshint node: true*/
'use strict';

var Forecast = require('forecast-api'),
_ = require('lodash'),
Q = require('q'),
logger = require('./../logger.js')('default'),
i18n = require('i18n'),
instances = {}
;


/**
* Explodes the resources array by id
*
* @param {Array} resources
* @return {Object}
*/
function byId (resources)
{
var results = {};
_.each(resources, function (resourceObject) {
var id = resourceObject.id;
results[id] = resourceObject;
});

return results;
}


function ForecastWrapper (config)
{
this.config = config;
this.forecast = new Forecast(config);
}


ForecastWrapper.prototype = {

caches : {
projects : null,
clients : null,
people: null
},

/**
* Wrapper function for assignments API call
*
* @param {Object} options
* @param {Function} callback
* @returns {undefined}
*/
assignments : function (options, callback) {
var that = this;
this.forecast.assignments(options, function (err, assignments) {
if (err) {
callback(err, assignments);
} else {
that.mergeAssignments(assignments, callback);
}
});
},

/**
*
*/
mergeAssignments : function (assignments, callback) {
var that = this;
this.preload(false, function () {
that.doMerge(assignments, callback);
});
},


/**
* Preloading clients, projects and people
*
* @param {Boolean} force
* @param {Function} callback
* @returns {undefined}
*/
preload : function (force, callback) {
var promises = [],
that = this
;
_.each(this.caches, function (values, key) {
var def = Q.defer();

if ((values === null) || force) {
that.doPreload(key, function (valuesFromApi) {
that.caches[key] = valuesFromApi;
def.resolve(valuesFromApi);
});
} else {
def.resolve(values);

}
promises.push(def.promise);
});

Q.all(promises).then(function (items) {
if (callback) {
callback();
}
}, function (err) {
console.log(err);
}).catch(function () {
console.log(Array.prototype.slice.call(arguments));
});
},


doPreload : function (key, callback) {
var that = this;
this.forecast[key].call(that.forecast, function (err, items) {
if (err) {
logger.log(i18n.__('Not able to load forecast resource for method {{methodName}}', {
methodName : key
}), err, {});
callback(null);
} else {
callback(items);
}
});
},


doMerge : function (assignments, callback) {
var projects = this.caches.projects,
clientsById = byId(this.caches.clients),
projectsById,
peopleById = byId(this.caches.people)
;

_.each(projects, function (project) {
var clientId = project.client_id,
client = clientsById[clientId] || null
;
project.client = client;
});

projectsById = byId(projects);


_.each(assignments, function (assignmentObject) {
var projectId = assignmentObject.project_id,
personId = assignmentObject.person_id
;
assignmentObject.project = projectsById[projectId] || null;
assignmentObject.person = peopleById[personId] || null;
});

callback(null, assignments);
}
};


ForecastWrapper.prototype.constructor = ForecastWrapper;

/**
* Creates a new instance if such instance does not exist. If exists, returns
* the existing one.
*
* @param {String} key
* @param {Object} config
* @returns {Harvest}
*/
module.exports = function (key, config)
{
if (!!instances[key]) {
return instances[key];
} else {
if (!!config && !!config.accountId && config.authorization) {
instances[key] = new ForecastWrapper(config);
return instances[key];
}

return null;
}
};
Loading

0 comments on commit 655b94b

Please sign in to comment.