@codetanzania/ewea-reports
Version:
Common reports for EWEA
544 lines (503 loc) • 13.5 kB
JavaScript
import {
PREDEFINE_NAMESPACE_ADMINISTRATIVELEVEL,
PREDEFINE_NAMESPACE_ADMINISTRATIVEAREA,
PREDEFINE_NAMESPACE_PARTYGROUP,
PREDEFINE_NAMESPACE_PARTYROLE,
} from '@codetanzania/ewea-internals';
import { isFunction } from 'lodash';
import { waterfall } from 'async';
import { safeMergeObjects } from '@lykmapipo/common';
import { Party } from '@codetanzania/emis-stakeholder';
import { DEFAULT_PREDEFINE_RELATION } from '@codetanzania/ewea-common';
// start: constants
// order: base to specific
const PARTY_TYPE_FOCAL = 'Focal';
const DEFAULT_RELATION_LEVEL = safeMergeObjects(DEFAULT_PREDEFINE_RELATION, {
namespace: PREDEFINE_NAMESPACE_ADMINISTRATIVELEVEL,
});
const DEFAULT_RELATION_AREA = safeMergeObjects(DEFAULT_PREDEFINE_RELATION, {
namespace: PREDEFINE_NAMESPACE_ADMINISTRATIVEAREA,
});
const DEFAULT_RELATION_GROUP = safeMergeObjects(DEFAULT_PREDEFINE_RELATION, {
namespace: PREDEFINE_NAMESPACE_PARTYGROUP,
});
const DEFAULT_RELATION_ROLE = safeMergeObjects(DEFAULT_PREDEFINE_RELATION, {
namespace: PREDEFINE_NAMESPACE_PARTYROLE,
});
// start: extra metric fields
// order: base to specific
/**
* @constant
* @name PARTY_BASE_METRIC_FIELDS
* @description Adds new metric fields to the next stage in the pipeline.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.1.0
*/
export const PARTY_BASE_METRIC_FIELDS = {
metrics: {
focal: {
$cond: { if: { $eq: ['$type', PARTY_TYPE_FOCAL] }, then: 1, else: 0 },
},
agency: {
$cond: { if: { $ne: ['$type', PARTY_TYPE_FOCAL] }, then: 1, else: 0 },
},
level: {
$cond: { if: { $not: '$level' }, then: 0, else: 1 },
},
area: {
$cond: { if: { $not: '$area' }, then: 0, else: 1 },
},
group: {
$cond: { if: { $not: '$group' }, then: 0, else: 1 },
},
role: {
$cond: { if: { $not: '$role' }, then: 0, else: 1 },
},
active: {
$cond: { if: { $not: '$deletedAt' }, then: 1, else: 0 },
},
inactive: {
$cond: { if: { $not: '$deletedAt' }, then: 0, else: 1 },
},
},
};
// start: projections
// order: base to specific
/**
* @constant
* @name PARTY_BASE_LEVEL_PROJECTION
* @description Party level fields passed to the next stage in the pipeline.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.1.0
*/
export const PARTY_BASE_LEVEL_PROJECTION = {
_id: 1,
namespace: 1,
name: '$level.strings.name',
abbreviation: '$level.strings.abbreviation',
color: '$level.strings.color',
weight: '$level.numbers.weight',
};
/**
* @constant
* @name PARTY_BASE_AREA_PROJECTION
* @description Party area fields passed to the next stage in the pipeline.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.1.0
*/
export const PARTY_BASE_AREA_PROJECTION = {
_id: 1,
namespace: 1,
name: '$area.strings.name',
abbreviation: '$area.strings.abbreviation',
color: '$area.strings.color',
weight: '$area.numbers.weight', // TODO: ensure from level
};
/**
* @constant
* @name PARTY_BASE_GROUP_PROJECTION
* @description Party group fields passed to the next stage in the pipeline.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.1.0
*/
export const PARTY_BASE_GROUP_PROJECTION = {
_id: 1,
namespace: 1,
name: '$group.strings.name',
abbreviation: '$group.strings.abbreviation',
color: '$group.strings.color',
weight: '$group.numbers.weight',
};
/**
* @constant
* @name PARTY_BASE_ROLE_PROJECTION
* @description Party role fields passed to the next stage in the pipeline.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.1.0
*/
export const PARTY_BASE_ROLE_PROJECTION = {
_id: 1,
namespace: 1,
name: '$role.strings.name',
abbreviation: '$role.strings.abbreviation',
color: '$role.strings.color',
weight: '$role.numbers.weight',
};
/**
* @constant
* @name PARTY_DEFAULT_PROJECTION
* @description Normalize Party fields with defaults before pass to
* the next stage in the pipeline.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.3.0
* @version 0.1.0
*/
export const PARTY_DEFAULT_PROJECTION = {
_id: 1,
type: 1,
createdAt: 1,
updatedAt: 1,
metrics: 1,
level: { $ifNull: ['$level', DEFAULT_RELATION_LEVEL] },
area: { $ifNull: ['$area', DEFAULT_RELATION_AREA] },
group: { $ifNull: ['$group', DEFAULT_RELATION_GROUP] },
role: { $ifNull: ['$role', DEFAULT_RELATION_ROLE] },
};
/**
* @constant
* @name PARTY_BASE_PROJECTION
* @description Party fields passed to the next stage in the pipeline.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.1.0
*/
export const PARTY_BASE_PROJECTION = {
_id: 1,
type: 1,
createdAt: 1,
updatedAt: 1,
metrics: 1,
level: PARTY_BASE_LEVEL_PROJECTION,
area: PARTY_BASE_AREA_PROJECTION,
group: PARTY_BASE_GROUP_PROJECTION,
role: PARTY_BASE_ROLE_PROJECTION,
};
// start: facets
// order: base, overall to specific
/**
* @constant
* @name PARTY_FACET_OVERVIEW
* @description General `Party` overview facet.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.1.0
*/
export const PARTY_FACET_OVERVIEW = {
overview: [
{
$group: {
_id: null,
total: { $sum: 1 },
agency: { $sum: '$metrics.agency' },
focal: { $sum: '$metrics.focal' },
level: { $sum: '$metrics.level' },
area: { $sum: '$metrics.area' },
group: { $sum: '$metrics.group' },
role: { $sum: '$metrics.role' },
active: { $sum: '$metrics.active' },
inactive: { $sum: '$metrics.inactive' },
},
},
{
$project: {
_id: 0,
},
},
],
};
/**
* @constant
* @name PARTY_FACET_OVERALL_LEVEL
* @description Overall `Party` breakdown by administrative level facet.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.3.0
* @version 0.1.0
*/
export const PARTY_FACET_OVERALL_LEVEL = {
levels: [
{
$group: {
_id: '$level._id',
total: { $sum: 1 },
agency: { $sum: '$metrics.agency' },
focal: { $sum: '$metrics.focal' },
active: { $sum: '$metrics.active' },
inactive: { $sum: '$metrics.inactive' },
namespace: { $first: '$level.namespace' },
name: { $first: '$level.name' },
abbreviation: { $first: '$level.abbreviation' },
color: { $first: '$level.color' },
weight: { $first: '$level.weight' },
},
},
{
$sort: {
weight: 1,
},
},
],
};
/**
* @constant
* @name PARTY_FACET_OVERALL_AREA
* @description Overall `Party` breakdown by administrative area facet.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.3.0
* @version 0.1.0
*/
export const PARTY_FACET_OVERALL_AREA = {
areas: [
{
$group: {
_id: '$area._id',
total: { $sum: 1 },
agency: { $sum: '$metrics.agency' },
focal: { $sum: '$metrics.focal' },
active: { $sum: '$metrics.active' },
inactive: { $sum: '$metrics.inactive' },
namespace: { $first: '$area.namespace' },
name: { $first: '$area.name' },
abbreviation: { $first: '$area.abbreviation' },
color: { $first: '$area.color' },
weight: { $first: '$area.weight' },
},
},
{
$sort: {
weight: 1,
},
},
],
};
/**
* @constant
* @name PARTY_FACET_OVERALL_GROUP
* @description Overall `Party` breakdown by group facet.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.3.0
* @version 0.1.0
*/
export const PARTY_FACET_OVERALL_GROUP = {
groups: [
{
$group: {
_id: '$group._id',
total: { $sum: 1 },
agency: { $sum: '$metrics.agency' },
focal: { $sum: '$metrics.focal' },
active: { $sum: '$metrics.active' },
inactive: { $sum: '$metrics.inactive' },
namespace: { $first: '$group.namespace' },
name: { $first: '$group.name' },
abbreviation: { $first: '$group.abbreviation' },
color: { $first: '$group.color' },
weight: { $first: '$group.weight' },
},
},
{
$sort: {
weight: 1,
},
},
],
};
/**
* @constant
* @name PARTY_FACET_OVERALL_ROLE
* @description Overall `Party` breakdown by role facet.
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.3.0
* @version 0.1.0
*/
export const PARTY_FACET_OVERALL_ROLE = {
roles: [
{
$group: {
_id: '$role._id',
total: { $sum: 1 },
agency: { $sum: '$metrics.agency' },
focal: { $sum: '$metrics.focal' },
active: { $sum: '$metrics.active' },
inactive: { $sum: '$metrics.inactive' },
namespace: { $first: '$role.namespace' },
name: { $first: '$role.name' },
abbreviation: { $first: '$role.abbreviation' },
color: { $first: '$role.color' },
weight: { $first: '$role.weight' },
},
},
{
$sort: {
weight: 1,
},
},
],
};
// start: aggregations
// order: base to specific
/**
* @function getPartyBaseAggregation
* @name getPartyBaseAggregation
* @description Create base `Party` aggregations with all fields looked up and
* un-winded for further stages in the pipeline.
* @param {object} [criteria={}] conditions which will be applied in first
* aggregation stage.
* @returns {object|Error} valid base aggregation for party or error
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.1.0
* @version 0.2.0
* @static
* @public
* @example
*
* getPartyBaseAggregation({ ... });
* //=> Aggregation{ ... }
*
*/
export const getPartyBaseAggregation = (criteria = {}) => {
// initialize party base aggregation
const base = Party.lookup(criteria);
// project default on relations
base.project(PARTY_DEFAULT_PROJECTION);
// add base projection
base.project(PARTY_BASE_PROJECTION);
// TODO: project per relations before add metrics
// add extra metric fields
base.addFields(PARTY_BASE_METRIC_FIELDS);
// return party base aggregation
return base;
};
/**
* @function getPartyOverview
* @name getPartyOverview
* @description Create `Party` overview analysis.
* @param {object} [criteria={}] conditions which will be applied in analysis
* @param {Function} done callback to invoke on success or error
* @returns {object|Error} valid party overview analysis or error
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.4.0
* @version 0.1.0
* @static
* @public
* @example
*
* getPartyOverview({ ... });
* //=> { total: 7, ... }
*
*/
export const getPartyOverview = (criteria, done) => {
// normalize arguments
const filter = isFunction(criteria) ? {} : criteria;
const cb = isFunction(criteria) ? criteria : done;
// obtain party base aggregation
const base = getPartyBaseAggregation(filter);
// add facets
const facets = {
...PARTY_FACET_OVERVIEW,
};
base.facet(facets);
// run aggregation
const aggregate = (next) => base.exec(next);
const normalize = (result, next) => {
// ensure data
const { overview } = safeMergeObjects(...result);
// normalize result
const data = safeMergeObjects(...overview);
// return normalize result
return next(null, data);
};
// return
const tasks = [aggregate, normalize];
return waterfall(tasks, cb);
};
/**
* @function getPartyAnalysis
* @name getPartyAnalysis
* @description Create `Party` analysis.
* @param {object} [criteria={}] conditions which will be applied in analysis
* @param {Function} done callback to invoke on success or error
* @returns {object|Error} valid party analysis or error
*
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.2.0
* @version 0.2.0
* @static
* @public
* @example
*
* getPartyAnalysis({ ... });
* //=> { data: { overview: { ... } }, ... }
*
*/
export const getPartyAnalysis = (criteria, done) => {
// normalize arguments
const filter = isFunction(criteria) ? {} : criteria;
const cb = isFunction(criteria) ? criteria : done;
// obtain party base aggregation
const base = getPartyBaseAggregation(filter);
// add facets
const facets = {
...PARTY_FACET_OVERVIEW,
...PARTY_FACET_OVERALL_LEVEL,
...PARTY_FACET_OVERALL_AREA,
...PARTY_FACET_OVERALL_GROUP,
...PARTY_FACET_OVERALL_ROLE,
};
base.facet(facets);
// run aggregation
const aggregate = (next) => base.exec(next);
const normalize = (result, next) => {
// TODO: extract to utils
// ensure data
const { overview, levels, areas, groups, roles } = safeMergeObjects(
...result
);
// normalize result
const data = safeMergeObjects({
overview: safeMergeObjects(...overview),
overall: { levels, areas, groups, roles },
});
// return normalize result
return next(null, { data });
};
// return
const tasks = [aggregate, normalize];
return waterfall(tasks, cb);
};
// TODO: dataset only aggregation; getPartyDatasets