diff --git a/es/index.js b/es/index.js index fc0a1ca..4ad4875 100644 --- a/es/index.js +++ b/es/index.js @@ -1,5 +1,5 @@ import { pkg } from '@lykmapipo/common'; -import { head, map, isNumber, merge } from 'lodash'; +import { head, map, upperFirst, merge, isNumber } from 'lodash'; import { Router } from '@lykmapipo/express-common'; import { getString } from '@lykmapipo/env'; import { model } from '@lykmapipo/mongoose-common'; @@ -32,47 +32,6 @@ const getBaseAggregation = criteria => { const ServiceRequest = model('ServiceRequest'); return ServiceRequest.lookup(criteria) - .addFields({ - /** - * Time difference between expected time to resolve the service request - * and today. - * - * This time will indicate if the service request is late or not base on - * the SLA(Service Level Agreement) time set per service request nature - */ - lateTime: { $subtract: ['$expectedAt', new Date()] }, - - /** - * This is the time for a confirmed service request to be assigned to - * a responsible party - */ - assignTime: { $subtract: ['$assignedAt', '$confirmedAt'] }, - - /** - * This is the time for a assigned service request to be attended - */ - attendTime: { $subtract: ['$attendedAt', '$assignedAt'] }, - - /** - * This is the time for a attended service request to be completed - */ - completeTime: { $subtract: ['$completedAt', '$attendedAt'] }, - - /** - * This is the time for a completed service request to be verified - */ - verifyTime: { $subtract: ['$verifiedAt', '$completedAt'] }, - - /** - * This is the time for a verified service request to be approved - */ - approveTime: { $subtract: ['$approvedAt', '$verifiedAt'] }, - - /** - * This is the time for an approved service request to be marked as resolved - */ - resolveTime: { $subtract: ['$resolvedAt', '$createdAt'] }, - }) .addFields({ /** * Flag for unconfirmed service request. This shows all service requests @@ -234,7 +193,7 @@ const getBaseAggregation = criteria => { }, /** - * Flag for resovled service request i.e service request which is resolved + * Flag for resolved service request i.e service request which is resolved * * A service request is flagged as resolved service request when it * has been resolved. @@ -247,7 +206,7 @@ const getBaseAggregation = criteria => { * Flag for reopened service request i.e service request which have been * reopened after been resolved * - * A service request is flagged as reopend service request when it + * A service request is flagged as reopened service request when it * has been confirmed and reopened. */ reopened: { @@ -257,6 +216,98 @@ const getBaseAggregation = criteria => { else: 0, }, }, + + /** + * Flag for late service request i.e service request passed it's SLA without + * being resolved + * + * Service request is flagged as late service request when it has been either + * resolved pass it's SLA or not resolved and it's pass it's SLA + */ + late: { + $cond: { + if: { + $or: [ + { + $and: [ + { $not: '$resolvedAt' }, + '$expectedAt', + { $gt: [new Date(), '$expectedAt'] }, + ], + }, + { + $and: ['$expectedAt', { $gt: ['$resolvedAt', '$expectedAt'] }], + }, + ], + }, + then: 1, + else: 0, + }, + }, + }) + .addFields({ + /** + * Time difference between when service request was reported and when it was + * confirmed by an operator or responsible party. + * + * This metric calculate how much time does it take for an organization + * to confirm/respond to issues which have been reported via channels + * which doesn't involve operator intervention. i.e USSD, Mobile App, Bot + * and e.t.c + */ + confirmTime: { $subtract: ['$confirmedAt', '$createdAt'] }, + + /** + * Time difference between expected time to resolve the service request + * and current date if not resolved or resolvedAt if resolved pass it SLA. + * + * This time will indicate if the service request is late or not base on + * the SLA(Service Level Agreement) time set per service request nature + */ + lateTime: { + $cond: { + if: { $eq: ['$late', 1] }, + then: { + $cond: { + if: '$resolvedAt', + then: { $subtract: ['$resolvedAt', '$expectedAt'] }, + else: { $subtract: [new Date(), '$expectedAt'] }, + }, + }, + else: null, + }, + }, + + /** + * This is the time for a confirmed service request to be assigned to + * a responsible party + */ + assignTime: { $subtract: ['$assignedAt', '$confirmedAt'] }, + + /** + * This is the time for a assigned service request to be attended + */ + attendTime: { $subtract: ['$attendedAt', '$assignedAt'] }, + + /** + * This is the time for a attended service request to be completed + */ + completeTime: { $subtract: ['$completedAt', '$attendedAt'] }, + + /** + * This is the time for a completed service request to be verified + */ + verifyTime: { $subtract: ['$verifiedAt', '$completedAt'] }, + + /** + * This is the time for a verified service request to be approved + */ + approveTime: { $subtract: ['$approvedAt', '$verifiedAt'] }, + + /** + * This is the time for an approved service request to be marked as resolved + */ + resolveTime: { $subtract: ['$resolvedAt', '$createdAt'] }, }); }; @@ -291,6 +342,47 @@ const OVERALL_FACET = { ], }; +/** + * @namespace TIME_FACET + * @description Facet for service request time calculation summary + * + * @version 0.1.0 + * @since 0.5.0 + */ +const TIME_FACET = { + time: [ + { + $group: { + _id: null, + maximumAssignTime: { $max: '$assignTime' }, + minimumAssignTime: { $min: '$assignTime' }, + averageAssignTime: { $avg: '$assignTime' }, + maximumAttendTime: { $max: '$attendTime' }, + minimumAttendTime: { $min: '$attendTime' }, + averageAttendTime: { $avg: '$attendTime' }, + maximumCompleteTime: { $max: '$completeTime' }, + minimumCompleteTime: { $min: '$completeTime' }, + averageCompleteTime: { $avg: '$completeTime' }, + maximumVerifyTime: { $max: '$verifyTime' }, + minimumVerifyTime: { $min: '$verifyTime' }, + averageVerifyTime: { $avg: '$verifyTime' }, + maximumApproveTime: { $max: '$approveTime' }, + minimumApproveTime: { $min: '$approveTime' }, + averageApproveTime: { $avg: '$approveTime' }, + maximumResolveTime: { $max: '$resolveTime' }, + minimumResolveTime: { $min: '$resolveTime' }, + averageResolveTime: { $avg: '$resolveTime' }, + maximumLateTime: { $max: '$lateTime' }, + minimumLateTime: { $min: '$lateTime' }, + averageLateTime: { $avg: '$lateTime' }, + maximumConfirmTime: { $max: '$confirmTime' }, + minimumConfirmTime: { $min: '$confirmTime' }, + averageConfirmTime: { $avg: '$confirmTime' }, + }, + }, + ], +}; + /** * @namespace PRIORITY_FACET * @description Facet for service requests breakdown based on their priorities @@ -667,6 +759,7 @@ const OPERATOR_LEADERSBOARD_FACET = { const OVERVIEW_FACET = { ...OVERALL_FACET, + ...TIME_FACET, ...JURISDICTION_FACET, ...STATUS_FACET, ...PRIORITY_FACET, @@ -847,7 +940,7 @@ const normalizeObjectTimes = item => { * @param {object} results Aggregation results * @returns {object} Normalized response object * - * @version 0.1.0 + * @version 0.2.0 * @since 0.2.0 */ const prepareReportResponse = results => { @@ -859,10 +952,39 @@ const prepareReportResponse = results => { data.overall = head(data.overall); + data.time = head(data.time); + if (data.overall) { data.overall = normalizeObjectTimes(data.overall); } + if (data.time) { + // const times = {}; + + const keys = [ + 'confirmTime', + 'assignTime', + 'attendTime', + 'completeTime', + 'verifyTime', + 'approveTime', + 'resolveTime', + 'lateTime', + ]; + + const times = map(keys, key => ({ + [key]: { + minimum: normalizeTime(data.time[`minimum${upperFirst(key)}`]), + maximum: normalizeTime(data.time[`maximum${upperFirst(key)}`]), + average: normalizeTime(data.time[`average${upperFirst(key)}`]), + }, + })); + + data.overall = merge({}, data.overall, ...times); + + delete data.time; + } + if (data.jurisdictions) { data.jurisdictions = map(data.jurisdictions, normalizeObjectTimes); } diff --git a/lib/index.js b/lib/index.js index deb9a50..c43ad13 100644 --- a/lib/index.js +++ b/lib/index.js @@ -34,47 +34,6 @@ const getBaseAggregation = criteria => { const ServiceRequest = mongooseCommon.model('ServiceRequest'); return ServiceRequest.lookup(criteria) - .addFields({ - /** - * Time difference between expected time to resolve the service request - * and today. - * - * This time will indicate if the service request is late or not base on - * the SLA(Service Level Agreement) time set per service request nature - */ - lateTime: { $subtract: ['$expectedAt', new Date()] }, - - /** - * This is the time for a confirmed service request to be assigned to - * a responsible party - */ - assignTime: { $subtract: ['$assignedAt', '$confirmedAt'] }, - - /** - * This is the time for a assigned service request to be attended - */ - attendTime: { $subtract: ['$attendedAt', '$assignedAt'] }, - - /** - * This is the time for a attended service request to be completed - */ - completeTime: { $subtract: ['$completedAt', '$attendedAt'] }, - - /** - * This is the time for a completed service request to be verified - */ - verifyTime: { $subtract: ['$verifiedAt', '$completedAt'] }, - - /** - * This is the time for a verified service request to be approved - */ - approveTime: { $subtract: ['$approvedAt', '$verifiedAt'] }, - - /** - * This is the time for an approved service request to be marked as resolved - */ - resolveTime: { $subtract: ['$resolvedAt', '$createdAt'] }, - }) .addFields({ /** * Flag for unconfirmed service request. This shows all service requests @@ -236,7 +195,7 @@ const getBaseAggregation = criteria => { }, /** - * Flag for resovled service request i.e service request which is resolved + * Flag for resolved service request i.e service request which is resolved * * A service request is flagged as resolved service request when it * has been resolved. @@ -249,7 +208,7 @@ const getBaseAggregation = criteria => { * Flag for reopened service request i.e service request which have been * reopened after been resolved * - * A service request is flagged as reopend service request when it + * A service request is flagged as reopened service request when it * has been confirmed and reopened. */ reopened: { @@ -259,6 +218,98 @@ const getBaseAggregation = criteria => { else: 0, }, }, + + /** + * Flag for late service request i.e service request passed it's SLA without + * being resolved + * + * Service request is flagged as late service request when it has been either + * resolved pass it's SLA or not resolved and it's pass it's SLA + */ + late: { + $cond: { + if: { + $or: [ + { + $and: [ + { $not: '$resolvedAt' }, + '$expectedAt', + { $gt: [new Date(), '$expectedAt'] }, + ], + }, + { + $and: ['$expectedAt', { $gt: ['$resolvedAt', '$expectedAt'] }], + }, + ], + }, + then: 1, + else: 0, + }, + }, + }) + .addFields({ + /** + * Time difference between when service request was reported and when it was + * confirmed by an operator or responsible party. + * + * This metric calculate how much time does it take for an organization + * to confirm/respond to issues which have been reported via channels + * which doesn't involve operator intervention. i.e USSD, Mobile App, Bot + * and e.t.c + */ + confirmTime: { $subtract: ['$confirmedAt', '$createdAt'] }, + + /** + * Time difference between expected time to resolve the service request + * and current date if not resolved or resolvedAt if resolved pass it SLA. + * + * This time will indicate if the service request is late or not base on + * the SLA(Service Level Agreement) time set per service request nature + */ + lateTime: { + $cond: { + if: { $eq: ['$late', 1] }, + then: { + $cond: { + if: '$resolvedAt', + then: { $subtract: ['$resolvedAt', '$expectedAt'] }, + else: { $subtract: [new Date(), '$expectedAt'] }, + }, + }, + else: null, + }, + }, + + /** + * This is the time for a confirmed service request to be assigned to + * a responsible party + */ + assignTime: { $subtract: ['$assignedAt', '$confirmedAt'] }, + + /** + * This is the time for a assigned service request to be attended + */ + attendTime: { $subtract: ['$attendedAt', '$assignedAt'] }, + + /** + * This is the time for a attended service request to be completed + */ + completeTime: { $subtract: ['$completedAt', '$attendedAt'] }, + + /** + * This is the time for a completed service request to be verified + */ + verifyTime: { $subtract: ['$verifiedAt', '$completedAt'] }, + + /** + * This is the time for a verified service request to be approved + */ + approveTime: { $subtract: ['$approvedAt', '$verifiedAt'] }, + + /** + * This is the time for an approved service request to be marked as resolved + */ + resolveTime: { $subtract: ['$resolvedAt', '$createdAt'] }, }); }; @@ -293,6 +344,47 @@ const OVERALL_FACET = { ], }; +/** + * @namespace TIME_FACET + * @description Facet for service request time calculation summary + * + * @version 0.1.0 + * @since 0.5.0 + */ +const TIME_FACET = { + time: [ + { + $group: { + _id: null, + maximumAssignTime: { $max: '$assignTime' }, + minimumAssignTime: { $min: '$assignTime' }, + averageAssignTime: { $avg: '$assignTime' }, + maximumAttendTime: { $max: '$attendTime' }, + minimumAttendTime: { $min: '$attendTime' }, + averageAttendTime: { $avg: '$attendTime' }, + maximumCompleteTime: { $max: '$completeTime' }, + minimumCompleteTime: { $min: '$completeTime' }, + averageCompleteTime: { $avg: '$completeTime' }, + maximumVerifyTime: { $max: '$verifyTime' }, + minimumVerifyTime: { $min: '$verifyTime' }, + averageVerifyTime: { $avg: '$verifyTime' }, + maximumApproveTime: { $max: '$approveTime' }, + minimumApproveTime: { $min: '$approveTime' }, + averageApproveTime: { $avg: '$approveTime' }, + maximumResolveTime: { $max: '$resolveTime' }, + minimumResolveTime: { $min: '$resolveTime' }, + averageResolveTime: { $avg: '$resolveTime' }, + maximumLateTime: { $max: '$lateTime' }, + minimumLateTime: { $min: '$lateTime' }, + averageLateTime: { $avg: '$lateTime' }, + maximumConfirmTime: { $max: '$confirmTime' }, + minimumConfirmTime: { $min: '$confirmTime' }, + averageConfirmTime: { $avg: '$confirmTime' }, + }, + }, + ], +}; + /** * @namespace PRIORITY_FACET * @description Facet for service requests breakdown based on their priorities @@ -669,6 +761,7 @@ const OPERATOR_LEADERSBOARD_FACET = { const OVERVIEW_FACET = { ...OVERALL_FACET, + ...TIME_FACET, ...JURISDICTION_FACET, ...STATUS_FACET, ...PRIORITY_FACET, @@ -849,7 +942,7 @@ const normalizeObjectTimes = item => { * @param {object} results Aggregation results * @returns {object} Normalized response object * - * @version 0.1.0 + * @version 0.2.0 * @since 0.2.0 */ const prepareReportResponse = results => { @@ -861,10 +954,39 @@ const prepareReportResponse = results => { data.overall = lodash.head(data.overall); + data.time = lodash.head(data.time); + if (data.overall) { data.overall = normalizeObjectTimes(data.overall); } + if (data.time) { + // const times = {}; + + const keys = [ + 'confirmTime', + 'assignTime', + 'attendTime', + 'completeTime', + 'verifyTime', + 'approveTime', + 'resolveTime', + 'lateTime', + ]; + + const times = lodash.map(keys, key => ({ + [key]: { + minimum: normalizeTime(data.time[`minimum${lodash.upperFirst(key)}`]), + maximum: normalizeTime(data.time[`maximum${lodash.upperFirst(key)}`]), + average: normalizeTime(data.time[`average${lodash.upperFirst(key)}`]), + }, + })); + + data.overall = lodash.merge({}, data.overall, ...times); + + delete data.time; + } + if (data.jurisdictions) { data.jurisdictions = lodash.map(data.jurisdictions, normalizeObjectTimes); }