viz-lib/src/visualizations/cohort/prepareData.js (110 lines of code) (raw):

import _ from "lodash"; import moment from "moment"; const momentInterval = { weekly: "weeks", daily: "days", monthly: "months", }; function groupData(sortedData) { const result = {}; _.each(sortedData, item => { const date = moment(item.date); const groupKey = date.valueOf(); result[groupKey] = result[groupKey] || { date, total: parseInt(item.total, 10) || 0, values: {}, }; result[groupKey].values[item.stage] = parseInt(item.value, 10) || null; }); return _.values(result); } function prepareDiagonalData(sortedData, options) { const timeInterval = options.timeInterval; const grouped = groupData(sortedData); const firstStage = _.min(_.map(sortedData, i => i.stage)); const stageCount = moment(_.last(grouped).date).diff(_.first(grouped).date, momentInterval[timeInterval]); let lastStage = firstStage + stageCount; let previousDate = null; const data = []; _.each(grouped, group => { if (previousDate !== null) { let diff = Math.abs(previousDate.diff(group.date, momentInterval[timeInterval])); while (diff > 1) { const row = [0]; for (let stage = firstStage; stage <= lastStage; stage += 1) { row.push(group.values[stage] || 0); } data.push(row); // It should be diagonal, so decrease count of stages for each next row lastStage -= 1; diff -= 1; } } previousDate = group.date; const row = [group.total]; for (let stage = firstStage; stage <= lastStage; stage += 1) { row.push(group.values[stage] || 0); } // It should be diagonal, so decrease count of stages for each next row lastStage -= 1; data.push(row); }); return data; } function prepareSimpleData(sortedData, options) { const timeInterval = options.timeInterval; const grouped = groupData(sortedData); const stages = _.map(sortedData, i => i.stage); const firstStage = _.min(stages); const lastStage = _.max(stages); let previousDate = null; const data = []; _.each(grouped, group => { if (previousDate !== null) { let diff = Math.abs(previousDate.diff(group.date, momentInterval[timeInterval])); while (diff > 1) { data.push([0]); diff -= 1; } } previousDate = group.date; const row = [group.total]; for (let stage = firstStage; stage <= lastStage; stage += 1) { row.push(group.values[stage]); } data.push(row); }); return data; } function isDataValid(rawData, options) { const columnNames = _.map(rawData.columns, c => c.name); return ( rawData.rows.length > 0 && _.includes(columnNames, options.dateColumn) && _.includes(columnNames, options.stageColumn) && _.includes(columnNames, options.totalColumn) && _.includes(columnNames, options.valueColumn) ); } export default function prepareData(rawData, options) { if (!isDataValid(rawData, options)) { return { data: [], initialDate: null }; } rawData = _.map(rawData.rows, item => ({ date: item[options.dateColumn], stage: parseInt(item[options.stageColumn], 10), total: parseFloat(item[options.totalColumn]), value: parseFloat(item[options.valueColumn]), })); const sortedData = _.sortBy(rawData, r => r.date + r.stage); const initialDate = moment(sortedData[0].date).toDate(); let data; switch (options.mode) { case "simple": data = prepareSimpleData(sortedData, options); break; default: data = prepareDiagonalData(sortedData, options); break; } return { data, initialDate }; }