123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- 'use strict';
- /*
- *+------------------------------------------------------------------------+
- *| Licensed Materials - Property of IBM
- *| IBM Cognos Products: BI Dashboard
- *| (C) Copyright IBM Corp. 2018, 2019
- *|
- *| US Government Users Restricted Rights - Use, duplication or disclosure
- *| restricted by GSA ADP Schedule Contract with IBM Corp.
- *+------------------------------------------------------------------------+
- */
- define(['../../../lib/@waca/core-client/js/core-client/ui/core/Class', '../../../apiHelpers/SlotAPIHelper', 'dashboard-analytics/widgets/livewidget/query/QueryResultData'], function (Class, SlotAPIHelper, QueryResultData) {
- 'use strict';
- /**
- * The ResultDataReader is a helper for the ResultsDataReaderTask but works at a single dataset level.
- * It is similar to a PostProcessor except that its results
- * are not fed into the main data stream for rendering.
- * Each feature that has the "ResultDataReaderPlugin" FeatureTag defined will be Called
- * at standard points through the data.... onDataStart, onDataRow, onDataDone.
- * (A feature can choose to implement one or all of those apis)
- **/
- var ResultDataReader = Class.extend({
- /**
- * @Constructor
- * @param {Object} widgetApi - The widget.
- * @param {Object} cbCreateQueryResultData (optional) - define a query result data API for testing etc.
- */
- init: function init(widgetApi, visualization, cbCreateQueryResultData) {
- ResultDataReader.inherited('init', this, arguments);
- this.visualization = visualization;
- this._createQueryResultData = cbCreateQueryResultData || this._createQueryResultData;
- //Get all available features that are identified as resultDataReaderPlugins.
- //Since we ask for this on every read of the data, we can limit it to enabled features.
- this.plugins = widgetApi.getMatchingFeatures('ResultDataReaderPlugin', /*bEnabledOnly*/true);
- },
- _createQueryResultData: function _createQueryResultData(resultData) {
- return new QueryResultData(resultData);
- },
- hasPlugins: function hasPlugins() {
- return this.plugins.length > 0;
- },
- onNewResults: function onNewResults(resultData) {
- this.plugins.forEach(function (plugin) {
- return plugin.onNewResults && plugin.onNewResults(resultData);
- });
- },
- /**
- * Process the supplied resultData (postProcessed query result for a single DSS query.)
- * @param queryResult - the resultData
- * @param dataViewId - the data view id (also known as the layerId)
- * @param renderContext - the renderContext (which flows through the renderSequnce)
- * @returns An array of 1 or more promises which is resolved when all work for all plugins is complete.
- */
- processData: function processData(queryResult, dataViewId, renderContext) {
- //Wrap the processed data rows in a promise.
- if (this.plugins.length) {
- return this._readResultData(queryResult, dataViewId, renderContext);
- }
- return [Promise.resolve({ hasPlugins: false })];
- },
- //TODO: queryResultParm is normally a queryResult API but can be a query result object if the new query api is disabled...
- _readResultData: function _readResultData(queryResultParm, dataViewId, renderContext) {
- var useNewQueryApi = renderContext && renderContext.useAPI;
- var queryResult = useNewQueryApi ? queryResultParm
- //TODO: queryResult can be the old QueryResultData object if the new query api is disabled...
- : queryResultParm._resultData && this._createQueryResultData(queryResultParm._resultData);
- var rowHeaders = this._buildRowHeaders(dataViewId);
- var rowCount = useNewQueryApi ? queryResult.getRowCount() : queryResult.getDatapointCount();
- this.plugins.forEach(function (plugin) {
- return plugin.onDataStart && plugin.onDataStart(queryResult, rowHeaders);
- });
- for (var rowIdx = 0; rowIdx < rowCount; rowIdx += rowHeaders.valueSlotMeasureCount) {
- this.plugins.forEach(function (plugin) {
- return plugin.onDataRow && plugin.onDataRow(rowIdx);
- });
- }
- var promises = [];
- this.plugins.forEach(function (plugin) {
- if (plugin.onDataDone) {
- //For a given plugin, onDataDone may or may not return a promise.
- //If any plugins do, we need to wait for them.
- var onDataDonePromise = plugin.onDataDone(renderContext);
- if (onDataDonePromise) {
- promises.push(onDataDonePromise);
- }
- }
- });
- return promises.length ? promises : [Promise.resolve({})];
- },
- _buildRowHeaders: function _buildRowHeaders(dataViewId) {
- var measureItemHeaders = []; //measure is helpful for understanding (its actually 'ordinal')
- var mappedSlots = this.visualization.getSlots().getMappedSlotList();
- var slotAPIs = dataViewId ? mappedSlots.filter(function (slotAPI) {
- return slotAPI.getDefinition().getDatasetIdList().indexOf(dataViewId) !== -1;
- }) : mappedSlots;
- var valueSlotMeasureCount = 1;
- //For any row of data, the categoryItemHeaders represents the set of categoryItemIds and the cell indexes where they are found.
- var categoryItemHeaders = {
- itemIds: [], //The itemIds of the categorical items.
- columnIndexes: [] //The column indexes where the categorical items occur (Note: when nested, 1 column index = 1 tuple)
- };
- //The first level result column index represents how a row of data is returned.
- //EXAMPLE
- //When a slot is stackable, all items in the slot are returned as a 'tuple' (as a sub-array of the firstLevelArray)
- //When a slot is not stackable, each item in the slot is returned under a separate first level array index.
- //slot0, slot1 is stackable: [[2004, Summer], [Fax, Wednesday], 300]
- //slot0 is not stackable, slot1 is stackable...returns the same data as [[2004], [Summer], [Fax, Wednesday], 300]
- var firstLevelResultColumnIndex = 0;
- slotAPIs.forEach(function (slotAPI) {
- var isMultiMeasure = SlotAPIHelper.isMultiMeasuresValueSlot(slotAPI);
- //This flag signifies that there are multiple items in this slot under the same first level column.
- var isSlotStackable = slotAPI.isStacked() || isMultiMeasure;
- if (slotAPI.getDefinition().getType() === 'ordinal' && slotAPI.getDataItemList().length > 1 && isMultiMeasure) {
- //If there's an ordinal slot with more than 1 item assigned to it, it is the "multi-measure value" slot.
- //For this case, the pre-processor will create a virtual category to represent the seriesIndex
- //(one value per measure name) and the value that pairs with that name will be reported on that row.
- //ie: Year, Revenue, Cost becomes Year SERIES Value
- // 2007 10000 500 2007 Revenue 10000
- // 2007 Cost 500
- //Note that slotAPIs match the post-processed structure (values would be at slot index 2 (and that dataitem index))
- valueSlotMeasureCount = slotAPI.getDataItemList().length;
- }
- slotAPI.getDataItemList().forEach(function (dataItemAPI, dataItemIndex) {
- var theMultiMeasureValueIndex = isMultiMeasure ? dataItemIndex : 0;
- if (dataItemAPI.getType() === 'fact') {
- //Set up a measureItemHeaders for each ordinal.
- measureItemHeaders.push({
- dataItemUniqueId: dataItemAPI.getId(),
- index: firstLevelResultColumnIndex,
- valueItemIndex: theMultiMeasureValueIndex, //This will only be non-zero for the stacked-measures case
- aggregate: dataItemAPI.getAggregation(),
- multiMeasuresValueItem: isMultiMeasure
- });
- } else {
- categoryItemHeaders.itemIds.push(dataItemAPI.getColumnId());
- if (isSlotStackable) {
- //When collecting categories, getCellValue returns a tuple for stacked items.
- //Because of this, we only want the value index of the parent cell.
- //Unless this item does not have stackable items.
- if (dataItemIndex === 0) {
- categoryItemHeaders.columnIndexes.push(firstLevelResultColumnIndex);
- }
- } else {
- categoryItemHeaders.columnIndexes.push(firstLevelResultColumnIndex);
- }
- }
- //If the slot is not stackable (ie: table or crosstab), increment the column index for each item in the slot.
- firstLevelResultColumnIndex += !isSlotStackable ? 1 : 0;
- });
- //If the slot is stackable, increment the column index once per slot.
- firstLevelResultColumnIndex += isSlotStackable ? 1 : 0;
- });
- return {
- layerId: dataViewId,
- categoryItemHeaders: categoryItemHeaders,
- measureItemHeaders: measureItemHeaders,
- valueSlotMeasureCount: valueSlotMeasureCount
- };
- }
- });
- return ResultDataReader;
- });
- //# sourceMappingURL=ResultDataReader.js.map
|