openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
219 lines (180 loc) • 8.75 kB
text/coffeescript
EmailTemplate = require('email-templates').EmailTemplate
logger = require 'winston'
moment = require "moment"
path = require('path')
Q = require 'q'
authorisation = require './api/authorisation'
Channel = require('./model/channels').Channel
config = require "./config/config"
config.reports = config.get('reports')
contact = require './contact'
metrics = require './metrics'
User = require('./model/users').User
utils = require './utils'
# Function Sends the reports
sendReports = (job, flag, done) ->
reportMap = {}
channelReportMap = {}
channelMap = {}
if flag == 'dailyReport'
from = moment().subtract(1, 'days').startOf('day').toDate()
to = moment().subtract(1, 'days').endOf('day').toDate()
else
from = moment().subtract(1, 'days').startOf('isoWeek').toDate()
to = moment().subtract(1, 'days').endOf('isoWeek').toDate()
# Select the right subscribers for the report
if flag == 'dailyReport'
fetchUsers = fetchDailySubscribers
if flag == 'weeklyReport'
fetchUsers = fetchWeeklySubscribers
fetchUsers (err, users) ->
promises = []
userKey = ''
userIndex = 0
usersArray = []
for user in users
do (user) ->
deferred = Q.defer()
userKey = user.email
authorisation.getUserViewableChannels user
.then (channels) ->
usersArray[userIndex] = user
usersArray[userIndex].allowedChannels = channels
for channel in usersArray[userIndex].allowedChannels
channelMap[channel._id] =
user: user
channel: channel
userIndex++
deferred.resolve()
promises.push deferred.promise
# Loop through the enriched user array
innerPromises = []
(Q.all promises).then ->
# Pre-Fetch report data into Channel Map
for key , obj of channelMap
innerDeferred = Q.defer()
do (innerDeferred, key, obj) ->
fetchChannelReport obj.channel, obj.user, flag, from, to, (err, item) ->
channelReportMap[key] = item
innerDeferred.resolve()
innerPromises.push innerDeferred.promise
(Q.all innerPromises).then ->
for user in usersArray
userKey = user.email
for channel in user.allowedChannels
do (channel) ->
if (reportMap[userKey])
# Do nothing since object already exists
else
# Create the object
reportMap[userKey] =
email: user.email
data: []
# If report has been fetched get it from the map
if channelReportMap[channel._id]
data = channelReportMap[channel._id]
# add report - always add if the channel is enabled (treating undefined status as enabled), otherwise only if there is data
if not data.channel.status? or data.channel.status is 'enabled' or data.data.length isnt 0
reportMap[userKey].data.push data
else
logger.error 'should never be here since channels have been pre-fetched'
# Iterate over reports and send the emails
for key, report of reportMap
if flag == 'dailyReport'
report.isDaily = true
else
report.isDaily = false
report.instance = config.alerts.himInstance
report.consoleURL = config.alerts.consoleURL
report.from = moment(from).format 'YYYY-MM-DD'
report.to = moment(to).format 'YYYY-MM-DD'
try
for data, i in report.data
colorGrey = 'color: grey;'
rowColor = 'background-color: #d9ead3'
if i % 2
rowColor = 'background-color: #b6d7a8;'
report.data[i].total = (if data.data[0]?.total? then data.data[0].total else 0)
report.data[i].avgResp = (if data.data[0]?.avgResp? then Math.round(data.data[0].avgResp) else 0)
report.data[i].failed = (if data.data[0]?.failed? then data.data[0].failed else 0)
report.data[i].successful = (if data.data[0]?.successful? then data.data[0].successful else 0)
report.data[i].processing = (if data.data[0]?.processing? then data.data[0].processing else 0)
report.data[i].completed = (if data.data[0]?.completed? then data.data[0].completed else 0)
report.data[i].completedWErrors = (if data.data[0]?.completedWErrors? then data.data[0].completedWErrors else 0)
report.data[i].totalStyle = (if report.data[i].total > 0 then '' else colorGrey)
report.data[i].avgRespStyle = (if report.data[i].avgResp > 0 then '' else colorGrey)
report.data[i].failedStyle = (if report.data[i].failed > 0 then 'color: red;' else colorGrey)
report.data[i].successfulStyle = (if report.data[i].successful > 0 then '' else colorGrey)
report.data[i].processingStyle = (if report.data[i].processing > 0 then '' else colorGrey)
report.data[i].completedStyle = (if report.data[i].completed > 0 then 'color: orange;' else colorGrey)
report.data[i].completedWErrorsStyle = (if report.data[i].completedWErrors > 0 then 'color: orangered;' else colorGrey)
report.data[i].rowColor = rowColor
sendUserEmail report
catch err
logger.error err
job.fail "Failed to send report reason: #{err}"
done()
sendUserEmail = (report) ->
report.date = new Date().toString()
renderTemplate 'report', report, (reportHtml) ->
contact.contactUser 'email', report.email, report.type + ' report for: ' + report.instance, plainTemplate(report), reportHtml, afterEmail
fetchChannelReport = (channel, user, flag, from, to, callback) ->
if flag == 'dailyReport'
period = 'day'
else
period = 'week'
item = {}
logger.info 'fetching ' + flag + ' for #' + channel.name + ' ' + user.email + ' ' + channel._id
metrics.calculateMetrics from, to, null, [channel._id], period
.then (data) ->
item.channel = channel
item.data = data
callback null, item
.catch (err) ->
logger.error 'Error calculating metrics: ', err
callback err
fetchDailySubscribers = (callback) ->
User.find { dailyReport: true }, callback
fetchWeeklySubscribers = (callback) ->
User.find { weeklyReport: true }, callback
plainTemplate = (report) ->
text = "Generated on: #{new Date().toString()}"
for data in report.data
do (data) ->
text += " \r\n \r\n <---------- Start Channel #{data.channel.name} ---------------------------> \r\n \r\n
Channel Name: #{data.channel.name} \r\n
Channel total: #{ if data.data[0]?.total? then data.data[0].total else 0} transactions \r\n
Ave response time: #{ if data.data[0]?.avgResp? then data.data[0].avgResp else 0 } \r\n
Failed: #{ if data.data[0]?.failed? then data.data[0].failed else 0 } \r\n
Successful: #{ if data.data[0]?.successful? then data.data[0].successful else 0 } \r\n
Processing: #{ if data.data[0]?.processing? then data.data[0].processing else 0 } \r\n
Completed: #{ if data.data[0]?.completed? then data.data[0].completed else 0 } \r\n
Completed with errors: #{ if data.data[0]?.completedWErrors? then data.data[0].completedWErrors else 0 } \r\n \r\n
<---------- End Channel -------------------------------------------------> \r\n \r\n
\r\n
\r\n
"
text
renderTemplate = (templateName, templateData, callback) ->
templateDir = "#{appRoot}/templates/#{templateName}"
template = new EmailTemplate(templateDir)
template.render templateData, (err, result) ->
if err
logger.err err
callback result.html.toString()
afterEmail = (callback) ->
logger.info 'email sent..'
setupAgenda = (agenda) ->
agenda.define 'send weekly channel metrics', (job, done) ->
sendReports job, 'weeklyReport', done
agenda.define 'send daily channel metrics', (job, done) ->
sendReports job, 'dailyReport', done
agenda.every config.reports.weeklyReportAt, 'send weekly channel metrics', null, { timezone: utils.serverTimezone() }
agenda.every config.reports.dailyReportAt, 'send daily channel metrics', null, { timezone: utils.serverTimezone() }
exports.setupAgenda = setupAgenda
if process.env.NODE_ENV == "test"
exports.sendReports = sendReports
exports.fetchDailySubscribers = fetchDailySubscribers
exports.fetchWeeklySubscribers = fetchWeeklySubscribers
exports.fetchChannelReport = fetchChannelReport
exports.sendUserEmail = sendUserEmail