rxviz/lib/data-parser.js
2017-07-07 21:21:06 +03:00

234 lines
5.7 KiB
JavaScript

import get from 'lodash.get';
import set from 'lodash.set';
import omit from 'lodash.omit';
import isUndefined from 'lodash.isundefined';
import isPlainObject from 'lodash.isplainobject';
import isFunction from 'lodash.isfunction';
import cloneDeep from 'lodash.clonedeep';
import TinyQueue from 'tinyqueue';
import {
defaultMainColor,
defaultObservableColor,
defaultShapeColor
} from './colors';
const isObservable = value =>
isPlainObject(value) &&
value.type === 'observable' &&
Array.isArray(value.values);
const isError = value => isPlainObject(value) && !isUndefined(value.error);
const isCompleted = value => isPlainObject(value) && value.completed === true;
const isMeta = value => isPlainObject(value) && value.meta === true;
export const isTimeout = value => isMeta(value) && value.timeout === true;
const colorizeItem = ({ isObservable, item }) => {
if (typeof item.color === 'string') {
return item;
}
return {
...item,
color: isObservable ? defaultObservableColor : defaultShapeColor
};
};
const prepareItem = ({
isObservable,
isError,
item,
observableIndex,
valueIndex,
renderer
}) => {
if (isFunction(renderer)) {
const { value, ...rest } = item;
const renderedItem = renderer({
isObservable,
isError,
value: isError ? item.error : value,
observableIndex,
valueIndex
});
const color = isError
? {}
: { color: isObservable ? defaultObservableColor : defaultShapeColor };
return {
...color,
...rest,
...renderedItem
};
}
return isError ? omit(item, 'error') : colorizeItem({ isObservable, item });
};
export const updateData = (observable, path, value) => {
const result = cloneDeep(observable);
const dataPath = path.reduce((acc, index) => acc.concat(index, 'values'), [
'values'
]);
return set(result, dataPath, get(result, dataPath).concat(value));
};
const getTextForMergedTooltip = value => {
const { tooltip } = value;
if (tooltip && tooltip.text) {
return tooltip.text;
}
return value.text;
};
const calcMergedTooltip = mergedTooltip => ({
text: mergedTooltip.join(', ')
});
export const getModel = ({
data,
renderer,
inheritMainColor = true,
mergeThreshold
}) => {
let result = {
observables: [],
connectors: []
};
if (!isObservable(data)) {
return result;
}
let queue = new TinyQueue(
[
{
observable: data,
startTime: 0,
mainColor: defaultMainColor
}
],
(obs1, obs2) => obs1.startTime - obs2.startTime
);
while (queue.length > 0) {
const { observable, startTime, mainColor, fromIndex } = queue.pop();
const observableIndex = result.observables.length;
if (!isUndefined(fromIndex)) {
result.connectors.push({
time: startTime,
fromIndex,
toIndex: observableIndex,
color: mainColor
});
}
let resultObservable = {
values: [],
startTime: startTime,
mainColor: mainColor
};
for (let i = 0, len = observable.values.length; i < len; i++) {
const item = observable.values[i];
const valueIndex = resultObservable.values.length;
if (isObservable(item)) {
const { time } = item;
const value = prepareItem({
isObservable: true,
isError: false,
item: {
...omit(item, 'type', 'values'),
isObservable: true,
text: ''
},
observableIndex,
valueIndex,
renderer
});
const { color } = value;
resultObservable.values.push(value);
queue.push({
observable: item,
startTime: time,
mainColor: inheritMainColor ? color : defaultMainColor,
fromIndex: observableIndex
});
} else if (isError(item)) {
resultObservable.endTime = item.time;
resultObservable.error = prepareItem({
isObservable: false,
isError: true,
item,
observableIndex,
valueIndex,
renderer
});
} else if (isCompleted(item)) {
resultObservable.endTime = item.time;
resultObservable.completed = {
time: item.time
};
const valuesCount = resultObservable.values.length;
if (valuesCount > 0) {
resultObservable.completed.lastValueBeforeCompletedTime =
resultObservable.values[valuesCount - 1].time;
}
} else {
const value = prepareItem({
isObservable: false,
isError: false,
item,
observableIndex,
valueIndex,
renderer
});
const lastValue =
valueIndex === 0 ? null : resultObservable.values[valueIndex - 1];
if (
!mergeThreshold ||
!lastValue ||
value.time - lastValue.time > mergeThreshold
) {
resultObservable.values.push(value);
} else {
if (typeof lastValue.count === 'number') {
lastValue.count += 1;
lastValue.mergedTooltip.push(getTextForMergedTooltip(value));
} else {
/* First merge */
lastValue.mergedTooltip = [
getTextForMergedTooltip(lastValue),
getTextForMergedTooltip(value)
];
lastValue.text = '...'; // Note that we change the text AFTER it's passed to lastValue.mergedTooltip
lastValue.count = 2;
}
}
}
}
resultObservable.values.forEach(value => {
if (value.mergedTooltip) {
value.tooltip = calcMergedTooltip(value.mergedTooltip);
delete value.mergedTooltip;
delete value.textStyle;
}
});
result.observables.push(resultObservable);
}
return result;
};