diff --git a/src/initialize-transformed.js b/src/initialize-transformed.js new file mode 100644 index 0000000..35fcd5a --- /dev/null +++ b/src/initialize-transformed.js @@ -0,0 +1,319 @@ +import jQuery from 'jquery'; +import { distinctArray } from './utilities'; + +// TODO: rename colors +function initializeColors ({ layout }) { + console.log(layout); + return { + vColLibBlue: layout.collibblue, + vColLibBlueP: layout.collibbluep, + vColLibClean: layout.collibclean, + vColLibCleanP: layout.collibcleanp, + vColLibCustom: layout.collibcustom, + vColLibCustomP: layout.collibcustomp, + vColLibDark: layout.collibdark, + vColLibDarkP: layout.collibdarkp, + vColLibGreen: layout.collibgreen, + vColLibGreenP: layout.collibgreenp, + vColLibNight: layout.collibnight, + vColLibNightP: layout.collibnightp, + vColLibOrange: layout.colliborange, + vColLibOrangeP: layout.colliborangep, + vColLibRed: layout.collibred, + vColLibRedP: layout.collibredp, + vColLibSoft: layout.collibsoft, + vColLibSoftP: layout.collibsoftp, + vColLibViolete: layout.collibviolete, + vColLibVioleteP: layout.collibvioletep + }; +} + +function getAlignment (option) { + const alignmentOptions = { + 1: 'left', + 2: 'center', + 3: 'right' + }; + + return alignmentOptions[option] || 'left'; +} + +function getFontSizeAdjustment (option) { + const fontSizeAdjustmentOptions = { + 1: -2, + 2: 0, + 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, + name: information.qFallbackTitle, + value: data.qNum + }; +} + +function generateMatrixCell (information, data) { + return { + displayValue: data.qText, + elementNumber: data.qElemNumber, + format: information.format, + magnitude: information.magnitudeLabelSuffix.substring( + information.magnitudeLabelSuffix.length - 2, + information.magnitudeLabelSuffix.length - 1 + ), + magnitudeLabelSuffix: information.magnitudeLabelSuffix, + name: information.name, + value: data.qNum + }; +} + +let lastRow = 0; +function generateDataSet (component, dimensionsInformation, measurementsInformation) { + const dimension1 = []; + const dimension2 = []; + const measurements = generateMeasurements(measurementsInformation); + let matrix = []; + + let previousDim1Entry; + const hasSecondDimension = dimensionsInformation.length > 1; + component.backendApi.eachDataRow((rowIndex, 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; + } + const matrixRow = row + .slice(firstDataCell, row.length) + .map((cell, cellIndex) => { + const measurementInformation = measurements[cellIndex]; + const generatedCell = generateMatrixCell(measurementInformation, cell); + + return generatedCell; + }); + + if (hasSecondDimension) { + // console.log(row[0]); + const currentDim1Entry = row[0].qText; + const isSameDimension1AsPrevious = currentDim1Entry === previousDim1Entry; + if (isSameDimension1AsPrevious) { + const updatedRow = matrix[matrix.length - 1].concat(matrixRow); + + matrix = [ + ...matrix.slice(0, matrix.length - 1), + updatedRow + ]; + } else { + matrix[matrix.length] = matrixRow; + } + previousDim1Entry = currentDim1Entry; + } else { + matrix[matrix.length] = matrixRow; + } + }); + + // filter header dimensions to only have distinct values + + return { + dimension1: distinctArray(dimension1), + dimension2: distinctArray(dimension2), + matrix, + measurements + }; +} + +async function initializeTransformed ({ $element, layout, component }) { + const colors = initializeColors({ 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); + + const customSchemaBasic = []; + const customSchemaFull = []; + let customHeadersCount = 0; + + function readCustomSchema () { + const url = `/Extensions/qlik-smart-pivot/${layout.customfile}`; + + return jQuery.get(url).then(response => { + const allTextLines = response.split(/\r\n|\n/); + const headers = allTextLines[0].split(';'); + customHeadersCount = headers.length; + for (let lineNumber = 0; lineNumber < allTextLines.length; lineNumber += 1) { + customSchemaFull[lineNumber] = new Array(headers.length); + const data = allTextLines[lineNumber].split(';'); + + if (data.length === headers.length) { + for (let headerIndex = 0; headerIndex < headers.length; headerIndex += 1) { + customSchemaBasic[lineNumber] = data[0]; + customSchemaFull[lineNumber][headerIndex] = data[headerIndex]; + } + } + } + }); + } + + const hasCustomSchema = (layout.customfilebool && layout.customfile.length > 4); + const schemaPromise = hasCustomSchema ? readCustomSchema() : Promise.resolve(); + await schemaPromise; + + // 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, + cellSuffix: getCellSuffix(layout.columnwidthslider), // TOOD: move to matrix cells or is it headers.measurements? + errorMessage: layout.errormessage, + maxLoops + }, + selection: { + dimensionSelectionCounts: dimensionsInformation.map(dimensionInfo => dimensionInfo.qStateCounts.qSelected) + }, + styling: { + colors, + customCSV: { + basic: customSchemaBasic, + count: customHeadersCount, + full: customSchemaFull + }, + hasCustomFileStyle: layout.customfilebool, + headerOptions: { + alignment: getAlignment(layout.HeaderAlign), + colorSchema: colors[`vColLib${layout.HeaderColorSchema}`], + fontSizeAdjustment: getFontSizeAdjustment(layout.lettersizeheader), + textColor: layout.HeaderTextColorSchema + }, + options: { + backgroundColor: colors[`vColLib${layout.ColorSchema}`], + backgroundColorOdd: colors[`vColLib${layout.ColorSchemaP}`], + color: layout.BodyTextColorSchema, + fontFamily: layout.FontFamily, + fontSizeAdjustment: getFontSizeAdjustment(layout.lettersize) + }, + semaphoreColors: { + fieldsToApplyTo: { + applyToAll: layout.allsemaphores, + specificFields: [ + layout.conceptsemaphore1, + layout.conceptsemaphore2, + layout.conceptsemaphore3, + layout.conceptsemaphore4, + layout.conceptsemaphore5, + layout.conceptsemaphore6, + layout.conceptsemaphore7, + layout.conceptsemaphore9, + layout.conceptsemaphore10 + ] + }, + status: { + critical: layout.metricstatus1, + medium: layout.metricstatus2 + }, + 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; diff --git a/src/store.js b/src/store.js index 602df3d..7f4b3f3 100644 --- a/src/store.js +++ b/src/store.js @@ -1,96 +1,30 @@ import { onlyUnique } from './utilities'; +import initializeTransformed from './initialize-transformed'; -function initialize ({ $element, layout, component }) { - const colors = { - vColLibClean: layout.collibclean, - vColLibSoft: layout.collibsoft, - vColLibDark: layout.collibdark, - vColLibNight: layout.collibnight, - vColLibRed: layout.collibred, - vColLibOrange: layout.colliborange, - vColLibBlue: layout.collibblue, - vColLibGreen: layout.collibgreen, - vColLibViolete: layout.collibviolete, - vColLibCustom: layout.collibcustom, - vColLibCleanP: layout.collibcleanp, - vColLibSoftP: layout.collibsoftp, - vColLibDarkP: layout.collibdarkp, - vColLibNightP: layout.collibnightp, - vColLibRedP: layout.collibredp, - vColLibOrangeP: layout.colliborangep, - vColLibBlueP: layout.collibbluep, - vColLibGreenP: layout.collibgreenp, - vColLibVioleteP: layout.collibvioletep, - vColLibCustomP: layout.collibcustomp - }; +async function initialize ({ $element, layout, component }) { + const transformedProperties = await initializeTransformed({ + $element, + component, + layout + }); - const nMeasAux = 0; + // TODO: remove everything from here to return statement once jquery parts in paint has been refactored const vMaxLoops = layout.maxloops; const vErrorMessage = layout.errormessage; - const vDynamicColorHeader = `vColLib${layout.HeaderColorSchema}`; - const vHeaderColorSchema = colors[vDynamicColorHeader]; - const vExportToExcel = layout.allowexportxls; - const vHeaderColorText = layout.HeaderTextColorSchema; - const vHeaderAlign = layout.HeaderAlign; - let vHeaderAlignText = ''; - switch (vHeaderAlign) { - case 1: - vHeaderAlignText = 'left'; - break; - case 2: - vHeaderAlignText = 'center'; - break; - case 3: - vHeaderAlignText = 'right'; - break; - } - let vLetterSizeHeader = 0; - switch (layout.lettersizeheader) { - case 1: - vLetterSizeHeader = -2; - break; - case 2: - vLetterSizeHeader = 0; - break; - case 3: - vLetterSizeHeader = 2; - break; - } let vDimName = ''; const ConceptMatrixFirst = new Array(); const ConceptMatrixSecond = new Array(); - let SecondHeaderLength = 0; const LabelsArray = new Array(); - const ExtraLabelsArray = new Array(); - let vExtraLabel = ''; - const vExcelButtonCode = ''; const ArrayGetSelectedCount = new Array(); let vNumDims = 0; let vNumMeasures = 0; - let vNumMeasures2 = 0; const MeasuresFormat = new Array(); - let sufixCells = ''; - switch (layout.columnwidthslider) { - case 1: - sufixCells += '-s'; - break; - case 2: - sufixCells = String(sufixCells); - break; - case 3: - sufixCells += '-l'; - break; - default: - sufixCells = String(sufixCells); - break; - } const dim_count = layout.qHyperCube.qDimensionInfo.length; const measure_count = layout.qHyperCube.qMeasureInfo.length; let vSeparatorCols = layout.separatorcols; if (dim_count == 1) { vSeparatorCols = false; } - const vFontFamily = layout.FontFamily; let lastrow = 0; const ConceptMatrix = new Array(); let ConceptMatrixRowElem = new Array(); @@ -98,18 +32,6 @@ function initialize ({ $element, layout, component }) { const ConceptMatrixColElemTable = new Array(); const ConceptMatrixPivot = new Array(); let ConceptMatrixFirstClean = new Array(); - let vLetterSize = 0; - switch (layout.lettersize) { - case 1: - vLetterSize = -2; - break; - case 2: - vLetterSize = -1; - break; - case 3: - vLetterSize = 2; - break; - } const nRows = component.backendApi.getRowCount(); const dimensionInfos = component.backendApi.getDimensionInfos(); @@ -134,20 +56,6 @@ function initialize ({ $element, layout, component }) { MeasuresFormat.push(mfor); - switch (mfor.substr(mfor.length - 1).toLowerCase()) { - case 'm': - vExtraLabel = ' (M)'; - ExtraLabelsArray.push(' (M)'); - break; - case 'k': - vExtraLabel = ' (k)'; - ExtraLabelsArray.push(' (k)'); - break; - default: - vExtraLabel = ''; - ExtraLabelsArray.push(''); - break; - } vNumMeasures++; }); @@ -203,9 +111,6 @@ function initialize ({ $element, layout, component }) { } } - SecondHeaderLength = SecondHeader.length; - vNumMeasures2 = vNumMeasures * SecondHeaderLength; - let ConceptPos = 0; let nMeas3 = 0; let vHeaderIndex = 0; @@ -224,75 +129,15 @@ function initialize ({ $element, layout, component }) { } } - if (nRows > (lastrow + 1) && nRows <= (vMaxLoops * 1000)) { - const requestPage = [ - { - qTop: lastrow + 1, - qLeft: 0, - qWidth: 10, // should be # of columns - qHeight: Math.min(1000, nRows - lastrow) - } - ]; - component.backendApi.getData(requestPage).then(() => { - component.paint($element); - }); - } - const properties = { - ArrayGetSelectedCount, - ConceptMatrix, ConceptMatrixColElem, ConceptMatrixColElemTable, - ConceptMatrixRowElem, - ConceptMatrixFirstClean, - ConceptMatrixPivot, - vHeaderColorText, - vFontFamily, - vHeaderColorSchema, - vExportToExcel, - vNumDims, - nMeasAux, - dimensionInfos, - vLetterSizeHeader, - vHeaderAlignText, - MeasuresFormat, - measure_count, - vExcelButtonCode, - sufixCells, - LabelsArray, - SecondHeader, - vSeparatorCols, - nSecond: SecondHeaderLength - 1, - nSecond2: SecondHeaderLength - 1, - vLetterSize, - ExtraLabelsArray, - dim_count, - vExtraLabel, - vNumMeasures, - vNumMeasures2, - lastrow, - measureInfos - }; - - // TODO: figure out a reasonable datastructure and use these for component - const transformedProperties = { - dimensions: [], - headers: LabelsArray, - headerOptions: { - colorSchema: vHeaderColorSchema, - textColor: vHeaderColorText - }, - options: { - fontFamily: vFontFamily - } + ConceptMatrixRowElem }; return { - colors, - dimensionInfos, - measureInfos, properties, - transformedProperties + ...transformedProperties }; }