Skip to content

Instantly share code, notes, and snippets.

@maggiben
Created July 5, 2017 20:57
Show Gist options
  • Save maggiben/1eb38b1ff0b01603204fe1032d0e909c to your computer and use it in GitHub Desktop.
Save maggiben/1eb38b1ff0b01603204fe1032d0e909c to your computer and use it in GitHub Desktop.
Template combiner
/**
* @desc Manufacture KPI Parser /api/situationroom/posters/manufacture/ShipmentByPlan
* Author: Benjamin at [email protected]
* Version: 0.1.0
*/
'use strict'
const debug = require('debug')(`esa:situationroom2:models:transport:v4:KpiParser`)
const _ = require('lodash')
const type = require('type-detect')
const uuidv4 = require('uuid/v4')
const DEFAULT_TIME_RANGES = ['MONTH', 'WEEK', 'DAY']
const KIP_DOMAINS = ['site', 'range', 'product']
const HashMap = function(...args) {
const transformParams = function(...args) {
const [initializr, defaultValue = null] = args;
debug('HashMap from: ', type(initializr))
switch (type(initializr)) {
case 'Set':
return Array.from(initializr.values()).map(key => ([key, defaultValue]));
case 'Object':
return Object.keys(initializr).map((key) => ([key, initializr[key]]))
default:
return initializr;
}
}
const hashMap = new Map(transformParams(...args))
Object.defineProperties(hashMap, {
toObject: {
writable: false,
enumerable: true,
configurable: true,
value: function(calc) {
return Reflect.apply(function(hashMap, object) {
hashMap.forEach((value, key) => (Object.assign(object, {
[key]: value
})));
return object;
}, undefined, [this, {}]);
}
},
toArray: {
writable: false,
value: function() {
return Array.from(this.values());
}
},
pushKpi: {
writable: false,
value: function(key, value) {
return this.set(key, Array.from(this.get(key) || 0).concat(value));
}
},
getKpi: {
writable: false,
value(key) {
return this.get(key);
}
}
})
this.hashMap = hashMap;
return this.hashMap;
}
const Formula = Object.create(Math);
Formula.flatten = function(...args) { return Array.prototype.concat(...args); };
Formula.sum = function(...args) { return Array.prototype.reduce.call(Formula.flatten(...args), (acc, curr) => (acc + curr)); };
Formula.avg = function(...args) { return Array.prototype.reduce.call([Formula.sum(...args)], (size, sum) => (sum / size), Formula.flatten(...args).length); };
Formula.sort = function(...args) { return Array.prototype.sort.apply(Formula.flatten(...args), [(a, b) => a - b]); };
const kpiFormulas = {
goal: {
value: [],
typeCheck: n => !isNaN(parseFloat(n)) && isFinite(n),
defaultValue: 0,
formula: (newValue, oldValue) => oldValue.concat(newValue || 0),
post: Formula.avg
},
value: {
value: [],
defaultValue: 0,
typeCheck: n => !isNaN(parseFloat(n)) && isFinite(n),
formula: (newValue, oldValue) => oldValue.concat(newValue || 0),
post: Formula.avg
},
totalValue: {
value: [],
defaultValue: 0,
typeCheck: n => !isNaN(parseFloat(n)) && isFinite(n),
formula: (newValue, oldValue) => oldValue.concat(newValue || 0),
post: Formula.avg
},
previouslyShipped: {
value: [],
defaultValue: 0,
typeCheck: n => !isNaN(parseFloat(n)) && isFinite(n),
formula: (newValue, oldValue) => oldValue.concat(newValue || 0),
post: Formula.avg
},
kpiPercentage: {
value: [],
defaultValue: 0,
typeCheck: n => !isNaN(parseFloat(n)) && isFinite(n),
formula: (newValue, oldValue) => oldValue.concat(newValue || 0),
post: (...args) => Math.floor(Formula.avg(args))
}
}
class KpiParser {
constructor () {
// Make a local copy
debug('KpiParser instance created')
}
buildSlides (collection, siteLables) {
const slides = []
siteLables = siteLables.reduce((hash, site) => Object.assign({}, hash, { [site.siteBid]: site.label }), {})
const rangeBucket = this.groupBy(collection, 'range', DEFAULT_TIME_RANGES);
for (let [range, kpis] of rangeBucket) {
const siteBuket = this.groupBy(kpis, 'site');
const combination = this.combine(siteBuket);
rangeBucket.set(range, combination.toObject());
// do the slides
const siteMap = combination.toObject();
slides.push({
range,
sites: Object.keys(siteMap).map(site => Object.assign({}, siteMap[site], { label: siteLables[site] }))
})
}
return slides;
}
combine (collection) {
debug(collection)
const combination = new HashMap(collection);
for (let [key, value] of combination) {
const template = this.buildKpiTemplate(kpiFormulas);
if(value.length > 1) {
let summary = this.runKpiFormulas(value, kpiFormulas, template);
combination.set(key, summary);
} else {
let summary = this.cleanSummary(value.pop(), kpiFormulas, template);
combination.set(key, summary);
}
}
return combination;
}
groupBy (collection, property, domains) {
// Transform to Set to ensure uniqueness
debug(collection, property, domains)
domains = Array.isArray(domains) ? new Set(domains) : new Set(collection.map(element => element[property]));
// const hashMap = new HashMap.create(Array.from(domains).map((v,i)=>([v,[]])));
// const hashMap = new HashMap(Array.from(domains).map((v,i)=>([v,[]])));
const hashMap = new HashMap(domains, [])
return collection.reduce(function (group, element) {
const key = element[property];
group.pushKpi(key, element);
return group;
}, hashMap)
}
runKpiFormulas (collection, formulas, template) {
const keys = Object.keys(formulas)
const kpis = Object.assign({}, ...keys.map(key => ({[key]: []})));
const extract = collection.reduce((hash, element) => {
debug('FROMULA FOR:', hash)
return Object.assign({}, hash, ...keys.map(function (key) {
const { formula, post } = formulas[key];
return {
[key]: formula.apply(this, [element[key], hash[key]])
}
}));
}, kpis);
const summary = this.cleanSummary(extract, formulas, template);
return Object.assign({}, [...collection].pop(), summary);
}
cleanSummary (siteKpi, formulas, template) {
const keys = Object.keys(formulas)
return keys.reduce((template, key) => {
return Object.assign({}, template, ...keys.map(function (key) {
const { formula, post, typeCheck, defaultValue } = formulas[key];
return {
[key]: typeCheck(siteKpi[key]) ? post.apply(this, [siteKpi[key]]) : defaultValue
}
}));
}, template);
}
buildKpiTemplate (formulas) {
return Object.keys(formulas).reduce(function (template, key) {
return Object.assign(template, { [key]: formulas[key].value });
}, {});
}
emptyCopy (object, formulas) {
return Object.keys(object).reduce(function(hash, key) {
const value = formulas[key] ? formulas[key].value : object[key];
return Object.assign(hash, { [key]: value });
}, {});
}
}
module.exports = KpiParser
const KPITEMPLATE = function (...args) {
const interceptor = function () {
return new Proxy(this, {
preventExtensions: function(target) {
return true;
},
defineProperty(target, prop, descriptor) {
console.log(descriptor);
return Reflect.defineProperty(target, prop, descriptor);
},
get: function (target, property, receiver) {
const { handler, value, formula } = target[property];
return handler.get(value);
},
set: function(target, property, newValue, receiver) {
const { handler, value, formula } = target[property];
return target[property].value = handler.set(newValue, value);
}
})
}
const props = {
goal: 'avg',
value: 'avg',
totalValue: 'avg',
previouslyShipped: 'avg',
kpiPercentage: 'avg',
};
const recipeFactory = function (property) {
const recipe = props[property]
const recipes = {
avg: {
stack: [0],
default: 0,
type: Number,
typeCheck: n => !isNaN(parseFloat(n)) && isFinite(n),
id: Array.from({length: 4}, (v,i) => (parseInt(Math.random() * Number.MAX_SAFE_INTEGER)).toString(16).slice(0,6).toUpperCase()).join('-'),
descriptor: {
enumerable: true,
configurable: true,
get: function (...args) {
let { stack, id } = recipes[recipe];
return stack.reduce((acc, curr) => acc + curr, 0 ) / stack.length
},
set: function (newValue) {
let { stack, id } = recipes[recipe];
stack.push(newValue || 0);
return true;
}
}
}
}
return recipes[recipe];
}
const recipes = Object.keys(props).reduce((hash, key) => {
return Object.assign(hash, {
[key]: recipeFactory(key).descriptor
})
}, {});
const buildKpiTemplate = function (formulas) {
return Object.keys(formulas).reduce(function (template, key) {
return Object.assign(template, { [key]: formulas[key].value });
}, {});
}
const clean = function (object, preserve = true) {
return Object.keys(object).reduce(function(hash, key) {
if (key in props) {
return Object.assign(hash, { [key]: recipeFactory(key).default });
} else {
return Object.assign(hash, ( preserve ? { [key]: object[key] } : {}));
}
}, {});
}
const combine = function (...args) {
const template = new KPITEMPLATE();
args.forEach(element => {
Object.keys(props).forEach(prop => {
template[prop] = element[prop];
console.log('x:', props)
})
})
return template;
}
const template = (...args) => Object.assign(this, ...args)
Object.defineProperties(this, recipes);
// return this.interceptor();
const [ initial ] = args;
console.log(initial)
if(args.length < 2 && typeof args === 'object') {
// console.log(clean(args))
return Object.assign(this, clean(args))
}
return this;
}
module.exports = KPITEMPLATE;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment