Files
PLSmartPivot/src/initialize-transformed.js
Albert Backenhof a5bc3ecd1b Moved design dimension to Appearance->Table format
-According to the documentation it should only be
 possible to set two dimensions. Previously you
 were able to set three, where the third one was
 used for styling. This wasn't obvious to the user
 though. Now, the design field is set under
 Appearance -> Table format.

Issue: DEB-156
2019-04-10 12:12:08 +02:00

345 lines
10 KiB
JavaScript

import { distinctArray } from './utilities';
export const HEADER_FONT_SIZE = {
SMALL: -1,
MEDIUM: 1
};
function getAlignment (option) {
const alignmentOptions = {
1: 'left',
2: 'center',
3: 'right'
};
return alignmentOptions[option] || 'left';
}
function getFontSizeAdjustment (option) {
const fontSizeAdjustmentOptions = {
1: HEADER_FONT_SIZE.SMALL,
2: HEADER_FONT_SIZE.MEDIUM,
3: 2
};
return fontSizeAdjustmentOptions[option] || 0;
}
function getCellSuffix (option) {
const cellSuffixOptions = {
1: '-s',
3: '-l'
};
return cellSuffixOptions[option] || '';
}
function getMeasurementFormat (measurement) {
if (measurement.qNumFormat.qType === 'U' || measurement.qNumFormat.qFmt === '##############') {
return '#.##0';
} else if (measurement.qNumFormat.qType === 'R') {
return measurement.qNumFormat.qFmt.replace(/(|)/gi, '');
}
return measurement.qNumFormat.qFmt;
}
function getMagnitudeLabelSuffix (magnitudeOption) {
const magnitudeLabelSuffixOptions = {
'k': ' (k)',
'm': ' (m)'
};
return magnitudeLabelSuffixOptions[magnitudeOption] || '';
}
function generateMeasurements (information) {
return information.map(measurement => {
const format = getMeasurementFormat(measurement);
const formatMagnitude = format.substr(format.length - 1).toLowerCase();
const transformedMeasurement = {
format,
magnitudeLabelSuffix: getMagnitudeLabelSuffix(formatMagnitude),
name: measurement.qFallbackTitle
};
return transformedMeasurement;
});
}
function generateDimensionEntry (information, data) {
return {
displayValue: data.qText,
elementNumber: data.qElemNumber,
name: information.qFallbackTitle,
value: data.qNum
};
}
function generateMatrixCell ({ cell, dimension1Information, dimension2Information, measurementInformation }) {
const matrixCell = {
displayValue: cell.qText,
format: measurementInformation.format,
magnitude: measurementInformation.magnitudeLabelSuffix.substring(
measurementInformation.magnitudeLabelSuffix.length - 2,
measurementInformation.magnitudeLabelSuffix.length - 1
),
magnitudeLabelSuffix: measurementInformation.magnitudeLabelSuffix,
name: measurementInformation.name,
parents: {
dimension1: {
elementNumber: dimension1Information.qElemNumber,
header: dimension1Information.qText
},
measurement: {
header: measurementInformation.name
}
},
value: cell.qNum
};
if (dimension2Information) {
matrixCell.parents.dimension2 = {
elementNumber: dimension2Information.qElemNumber
};
}
return matrixCell;
}
let lastRow = 0;
function generateDataSet (
component, dimensionsInformation, measurementsInformation, dataCube) {
const measurements = generateMeasurements(measurementsInformation);
let dimension1 = [];
let dimension2 = [];
let matrix = [];
const hasSecondDimension = dimensionsInformation.length > 1;
dataCube.forEach(row => {
lastRow += 1;
const dimension1Entry = generateDimensionEntry(dimensionsInformation[0], row[0]);
dimension1.push(dimension1Entry);
let dimension2Entry;
let firstDataCell = 1;
if (hasSecondDimension) {
dimension2Entry = generateDimensionEntry(dimensionsInformation[1], row[1]);
dimension2.push(dimension2Entry);
firstDataCell = 2;
}
let matrixRow = row
.slice(firstDataCell, row.length)
.map((cell, cellIndex) => {
const measurementInformation = measurements[cellIndex];
const dimension1Information = row[0]; // eslint-disable-line prefer-destructuring
const dimension2Information = hasSecondDimension ? row[1] : null;
const generatedCell = generateMatrixCell({
cell,
dimension1Information,
dimension2Information,
measurementInformation
});
return generatedCell;
});
let appendToRowIndex = matrix.length;
if (hasSecondDimension) {
// See if there already is a row for the current dim1
for (let i = 0; i < matrix.length; i++) {
if (matrix[i][0].parents.dimension1.header === matrixRow[0].parents.dimension1.header) {
appendToRowIndex = i;
matrixRow = matrix[i].concat(matrixRow);
}
}
}
matrix[appendToRowIndex] = matrixRow;
});
// filter header dimensions to only have distinct values
dimension1 = distinctArray(dimension1);
dimension2 = distinctArray(dimension2);
// Make sure all rows are saturated, otherwise data risks being displayed in the wrong column
matrix = matrix.map((row, rowIndex) => {
if (row.length == dimension2.length) {
// Row is saturated
return row;
}
// Row is not saturated, so must add empty cells to fill the gaps
let newRow = [];
let cellIndex = 0;
dimension2.forEach(dim => {
measurements.forEach(measurement => {
if (cellIndex < row.length
&& row[cellIndex].parents.dimension2.elementNumber === dim.elementNumber
&& row[cellIndex].parents.measurement.header === measurement.name) {
newRow.push(row[cellIndex]);
cellIndex++;
} else {
newRow.push({
displayValue: '',
parents: {
dimension1: { elementNumber: rowIndex },
dimension2: { elementNumber: dim.elementNumber },
measurement: { header: measurement.name }
}
});
}
});
});
return newRow;
});
return {
dimension1: dimension1,
dimension2: dimension2,
matrix,
measurements
};
}
function initializeTransformed ({ $element, component, dataCube, designList, layout }) {
const dimensionsInformation = component.backendApi.getDimensionInfos();
const measurementsInformation = component.backendApi.getMeasureInfos();
const dimensionCount = layout.qHyperCube.qDimensionInfo.length;
const rowCount = component.backendApi.getRowCount();
const maxLoops = layout.maxloops;
const {
dimension1,
dimension2,
measurements,
matrix
} = generateDataSet(component, dimensionsInformation, measurementsInformation, dataCube);
const customSchemaBasic = [];
const customSchemaFull = [];
let customHeadersCount = 0;
if (designList && designList.length > 0) {
const headers = designList[0].split(';');
customHeadersCount = headers.length;
for (let lineNumber = 0; lineNumber < designList.length; lineNumber += 1) {
customSchemaFull[lineNumber] = new Array(headers.length);
const data = designList[lineNumber].split(';');
if (data.length === headers.length) {
for (let headerIndex = 0; headerIndex < headers.length; headerIndex += 1) {
[customSchemaBasic[lineNumber]] = data;
customSchemaFull[lineNumber][headerIndex] = data[headerIndex];
}
}
}
}
// top level properties could be reducers and then components connect to grab what they want,
// possibly with reselect for some presentational transforms (moving some of the presentational logic like formatting and such)
const transformedProperties = {
data: {
headers: {
dimension1, // column headers
dimension2, // parent row headers if exists
measurements // row headers, looped for each dimension2 if exists
},
matrix, // 2d array of all rows/cells to render in body of datatable
meta: {
dimensionCount: dimensionsInformation.length
}
},
general: {
allowExcelExport: layout.allowexportxls,
allowFilteringByClick: layout.filteroncellclick,
cellSuffix: getCellSuffix(layout.columnwidthslider), // TOOD: move to matrix cells or is it headers.measurements?
errorMessage: layout.errormessage,
footnote: layout.footnote,
maxLoops,
subtitle: layout.subtitle,
title: layout.title
},
selection: {
dimensionSelectionCounts: dimensionsInformation.map(dimensionInfo => dimensionInfo.qStateCounts.qSelected)
},
styling: {
customCSV: {
basic: customSchemaBasic,
count: customHeadersCount,
full: customSchemaFull
},
hasCustomFileStyle: Boolean(designList),
headerOptions: {
alignment: getAlignment(layout.HeaderAlign),
colorSchema: layout.HeaderColorSchema.color,
fontSizeAdjustment: getFontSizeAdjustment(layout.lettersizeheader),
textColor: layout.HeaderTextColorSchema.color
},
options: {
backgroundColor: layout.rowEvenBGColor,
backgroundColorOdd: layout.rowOddBGColor,
color: layout.BodyTextColorSchema,
fontFamily: layout.FontFamily,
fontSizeAdjustment: getFontSizeAdjustment(layout.lettersize),
textAlignment: layout.cellTextAlignment
},
semaphoreColors: {
fieldsToApplyTo: {
applyToAll: layout.allsemaphores,
applyToMetric: layout.allmetrics,
specificFields: [
layout.conceptsemaphore1,
layout.conceptsemaphore2,
layout.conceptsemaphore3,
layout.conceptsemaphore4,
layout.conceptsemaphore5,
layout.conceptsemaphore6,
layout.conceptsemaphore7,
layout.conceptsemaphore9,
layout.conceptsemaphore10
],
metricsSpecificFields: layout.metricssemaphore.split(',').map(entry => Number(entry))
},
status: {
critical: layout.metricsstatus1,
medium: layout.metricsstatus2
},
statusColors: {
critical: {
backgroundColor: layout.colorstatus1.color,
color: layout.colorstatus1text.color
},
medium: {
backgroundColor: layout.colorstatus2.color,
color: layout.colorstatus2text.color
},
normal: {
backgroundColor: layout.colorstatus3.color,
color: layout.colorstatus3text.color
}
}
},
symbolForNulls: layout.symbolfornulls,
usePadding: layout.indentbool,
useSeparatorColumns: dimensionCount === 1 ? false : layout.separatorcols
}
};
if (rowCount > lastRow && rowCount <= (maxLoops * 1000)) {
const requestPage = [
{
qHeight: Math.min(1000, rowCount - lastRow),
qLeft: 0,
qTop: matrix.length,
qWidth: 10 // should be # of columns
}
];
component.backendApi.getData(requestPage).then(() => {
component.paint($element, layout);
});
}
return transformedProperties;
}
export default initializeTransformed;