DataBehindTheVis.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. 'use strict';
  2. var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  3. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  4. /**
  5. * Licensed Materials - Property of IBM
  6. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2020
  7. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  8. */
  9. define(['underscore', '../../../widgets/livewidget/nls/StringResources', '../../../lib/@waca/dashboard-common/dist/core/APIFactory', './api/DataBehindTheVisAPI', '../../../lib/@waca/dashboard-common/dist/utils/ContentUtil', '../../../apiHelpers/SlotAPIHelper', '../../../features/content/dataQueryExecution/DataQueryUtils', '../../../dataSources/nls/StringResources'], function (_, StringResources, APIFactory, DataBehindTheVisAPI, ContentUtil, SlotAPIHelper, DataQueryUtils, DSStringResources) {
  10. var DataBehindTheVis = function () {
  11. function DataBehindTheVis(_ref) {
  12. var content = _ref.content,
  13. features = _ref.features;
  14. _classCallCheck(this, DataBehindTheVis);
  15. this.content = content;
  16. this.dashboard = features['Dashboard.API'];
  17. this.csvExport = features['Dashboard.CsvExport'];
  18. this.queryService = features['Dashboard.QueryService'];
  19. this.globalFilters = features['Dashboard.GlobalFilters'];
  20. this.eventGroups = features['Dashboard.EventGroups'];
  21. this.pageContextService = features['Dashboard.PageContextService'];
  22. this.visualization = features.Visualization;
  23. this.queryExec = features.DataQueryExecution;
  24. }
  25. DataBehindTheVis.prototype.getAPI = function getAPI() {
  26. if (!this._api) {
  27. this._api = APIFactory.createAPI(this, [DataBehindTheVisAPI]);
  28. }
  29. return this._api;
  30. };
  31. DataBehindTheVis.prototype.export = function _export() {
  32. var _this = this;
  33. var doAggregate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
  34. return this.getData(doAggregate).then(function (data) {
  35. return _this.csvExport.export(data.title, [data.headers], data.cells);
  36. });
  37. };
  38. DataBehindTheVis.prototype.destroy = function destroy() {
  39. this.dashboardApi = null;
  40. };
  41. DataBehindTheVis.prototype.getData = function getData() {
  42. var _this2 = this;
  43. var doAggregate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
  44. return this._queryForData(doAggregate).then(function (result) {
  45. var data = _this2._filterDataWithSelections(result);
  46. return _extends({
  47. title: _this2._getTitle(),
  48. isAggregated: !!doAggregate
  49. }, data);
  50. });
  51. };
  52. DataBehindTheVis.prototype._queryForData = function _queryForData() {
  53. var _this3 = this;
  54. var doAggregate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
  55. var datasource = this.visualization.getDataSource();
  56. if (datasource) {
  57. doAggregate = !!doAggregate;
  58. var extraColumnIds = this._getColumnIdsFromUnprojectedFilters();
  59. var hasMultiMeasures = this._isMultiMeasureVisualization(this.visualization);
  60. var hasExtraColumns = !hasMultiMeasures && extraColumnIds && extraColumnIds.length > 0;
  61. var dataset = this._getQueryDataset();
  62. var mappingComplete = this.visualization.getSlots().isMappingComplete();
  63. var execute = hasExtraColumns ? this._getDataFromQuery(dataset, extraColumnIds, doAggregate) : this._getDataFromExecution(dataset, doAggregate, mappingComplete);
  64. return execute.then(function (result) {
  65. return _this3._getProcessedHeadersAndCells(result);
  66. });
  67. }
  68. // resolve with empty data
  69. return Promise.resolve({
  70. headers: [],
  71. cells: []
  72. });
  73. };
  74. DataBehindTheVis.prototype._getTitle = function _getTitle() {
  75. return StringResources.get('visExportFileName');
  76. };
  77. /**
  78. * filtered data down to the vis data selection if there is any
  79. */
  80. DataBehindTheVis.prototype._filterDataWithSelections = function _filterDataWithSelections(data) {
  81. if (data.cells) {
  82. var filteredDataCells = [];
  83. var selections = this._getSelections();
  84. if (selections && selections.length) {
  85. _.each(data.cells, function (cell) {
  86. _.each(selections, function (selection) {
  87. var isSelectionInTheRow = void 0;
  88. var isTupleSetInCell = function isTupleSetInCell(valueFromTupleSet) {
  89. return _.some(cell, function (cellValue) {
  90. // [249167] selections come in decorated as strings from vipr tuples
  91. // u is usually a string
  92. return (cellValue.value && cellValue.value.toString()) === valueFromTupleSet.u;
  93. });
  94. };
  95. if (selection.dataPoints) {
  96. // data points filters
  97. isSelectionInTheRow = _.some(selection.dataPoints, function (dataPoint) {
  98. return _.every(dataPoint, function (valueFromTupleSet) {
  99. return isTupleSetInCell(valueFromTupleSet);
  100. });
  101. });
  102. } else {
  103. // edge filters
  104. isSelectionInTheRow = _.some(selection.edges, function (valueFromTupleSet) {
  105. return isTupleSetInCell(valueFromTupleSet);
  106. });
  107. }
  108. // remove checking if entry already exists when live-widget changes for
  109. // brushing behaviour for table and indented list has been updated
  110. if (isSelectionInTheRow && !(filteredDataCells.indexOf(cell) !== -1)) {
  111. filteredDataCells.push(cell);
  112. }
  113. });
  114. });
  115. data.cells = filteredDataCells;
  116. }
  117. }
  118. return data;
  119. };
  120. /**
  121. * Get the list of columnIds from unprojected local filters
  122. * @returns {String[]}
  123. */
  124. DataBehindTheVis.prototype._getColumnIdsFromUnprojectedFilters = function _getColumnIdsFromUnprojectedFilters() {
  125. var _this4 = this;
  126. var projectedDataItems = this._getVisibleDataItems();
  127. var columnIds = [];
  128. var dataSource = this.visualization.getDataSource();
  129. _.each(this._getFilterInfo(), function (filterEntries) {
  130. _.each(filterEntries, function (filterEntry) {
  131. // Get itemId if exists; Otherwise get columnId from filter entry
  132. // Edge filter entry does not have itemId; it has only columnId that serves the same purpurse as itemId
  133. // Data point filter has itemId but it does not have columnId.
  134. // see more _decoratePageContext function in visFilterSupport
  135. _this4._getUnProjectedItemsFromFilterEntry(_.pluck(projectedDataItems, 'itemId'), filterEntry, columnIds, dataSource);
  136. });
  137. });
  138. return columnIds;
  139. };
  140. DataBehindTheVis.prototype._isMultiMeasureVisualization = function _isMultiMeasureVisualization() {
  141. return !!_.find(this.visualization.getSlots().getMappedSlotList(), function (slot) {
  142. return SlotAPIHelper.isMultiMeasuresSeriesSlot(slot);
  143. });
  144. };
  145. DataBehindTheVis.prototype._getQueryDataset = function _getQueryDataset() {
  146. var definition = this.visualization.getDefinition();
  147. var datasetList = definition.getDatasetList();
  148. // If there is only 1 data set then use it's id, otherwise, return the default id
  149. return datasetList && datasetList.length === 1 ? datasetList[0].id : definition.getDefaultDatasetId();
  150. };
  151. DataBehindTheVis.prototype._getDataFromQuery = function _getDataFromQuery(dataset, extraColumnIds, doAggregate) {
  152. var datasource = this.visualization.getDataSource();
  153. var queryService = this.dashboard.getFeature('QueryService');
  154. var queryType = doAggregate ? queryService.TYPE.DEFAULT : queryService.TYPE.DETAIL;
  155. var query = queryService.createQuery(queryType);
  156. // setup datasource and projected data items
  157. query.setDataSource(datasource.getId());
  158. query.addDataItems(this._getDataItems(dataset));
  159. // append extra data items
  160. query.addColumns(extraColumnIds);
  161. // apply all filters
  162. var filterInfo = DataQueryUtils.removeForecastFilters(this._getFilterInfo());
  163. var filterList = filterInfo.localFilters;
  164. filterList.push.apply(filterList, filterInfo.selectionFilters.concat(filterInfo.globalFilters));
  165. query.addFilters(filterList);
  166. return query.executeQuery();
  167. };
  168. DataBehindTheVis.prototype._getDataFromExecution = function _getDataFromExecution(dataset, doAggregate, mappingComplete) {
  169. if (doAggregate) {
  170. var queryResults = this.queryExec.getCurrentQueryResults();
  171. if (!mappingComplete || _.isEmpty(queryResults)) {
  172. return Promise.resolve(false);
  173. }
  174. // @todo use new query results api
  175. if (queryResults.getImplType && queryResults.getInterfaceType) {
  176. return Promise.resolve(queryResults.getResult(dataset));
  177. }
  178. if (dataset) {
  179. return Promise.resolve(queryResults.getQueryResultByDataViewId(dataset).getQueryResult());
  180. }
  181. return Promise.resolve(queryResults.getDefaultQueryResult().getQueryResult());
  182. } else {
  183. // submit a detail query to obtain the disaggregated result
  184. var results = this.queryExec.executeQueries('detail');
  185. //return results.getResult(dataset);
  186. return results.then(function (_results) {
  187. return _results.getResult(dataset);
  188. });
  189. }
  190. };
  191. DataBehindTheVis.prototype._getVisibleDataItems = function _getVisibleDataItems() {
  192. var dataItems = [];
  193. var nestList = [];
  194. var slots = this.visualization.getSlots();
  195. _.each(slots.getMappedSlotList(), function (slot) {
  196. _.each(slot.getDataItemList(), function (dataItem, index) {
  197. if (!SlotAPIHelper.isMultiMeasuresSeriesOrValue(slot, index)) {
  198. dataItems.push({
  199. id: dataItem.getId(),
  200. itemId: dataItem.getColumnId()
  201. });
  202. //keep track of the list of dataItems assigned to a slot.
  203. //If a slot has multiple items and the slot is type stacked,
  204. //use this list as the nestList of a stacked dataItem (see below)
  205. nestList.push(dataItem.getId());
  206. }
  207. }.bind(this));
  208. if (nestList.length > 1 && slot.isStacked()) {
  209. //If the slot is stacked, produce a dataItem whose name matches the id
  210. //with nested items whose names match the dataItems.
  211. var datasetIds = slot.getDefinition().getDatasetIdList();
  212. datasetIds.forEach(function (datasetId) {
  213. dataItems.push({
  214. id: slot.getId(),
  215. nest: nestList,
  216. layerId: datasetId
  217. });
  218. });
  219. }
  220. });
  221. return dataItems;
  222. };
  223. DataBehindTheVis.prototype._getUnProjectedItemsFromFilterEntry = function _getUnProjectedItemsFromFilterEntry(projectedDataItemIDs, filterEntry, aUnProjectedItemsFromFilterEntry, dataSource) {
  224. var filterEntryItemId = filterEntry.itemId || filterEntry.columnId;
  225. var column = dataSource.getMetadataColumn(filterEntryItemId);
  226. // Skip model filter columns
  227. if (!column || column.getObjectType() !== 'Filter') {
  228. if (filterEntryItemId) {
  229. if (projectedDataItemIDs.indexOf(filterEntryItemId) === -1 && aUnProjectedItemsFromFilterEntry.indexOf(filterEntryItemId) === -1) {
  230. aUnProjectedItemsFromFilterEntry.push(filterEntryItemId);
  231. }
  232. } else {
  233. _.each(filterEntry.values, function (valueExpr) {
  234. this._getUnProjectedItemsFromFilterEntry(projectedDataItemIDs, valueExpr, aUnProjectedItemsFromFilterEntry, dataSource);
  235. }.bind(this));
  236. }
  237. }
  238. };
  239. DataBehindTheVis.prototype._getFilterInfo = function _getFilterInfo() {
  240. var datasource = this.visualization.getDataSource();
  241. var localFilters = this.visualization.getLocalFilters().getFilterList();
  242. var type = this.dashboard.getAppConfig('pageContainerType');
  243. var globalFilters = this.globalFilters.getFilterList({
  244. origin: 'filter',
  245. sourceId: datasource.getId(),
  246. scope: ContentUtil.getPageContent(this.content, type).getId(),
  247. eventGroupId: this.eventGroups.getGroupId(this.content.getId())
  248. });
  249. var selectionFilters = this.globalFilters.getFilterList({
  250. origin: 'visualization',
  251. sourceId: datasource.getId(),
  252. scope: ContentUtil.getPageContent(this.content, type).getId(),
  253. eventGroupId: this.eventGroups.getGroupId(this.content.getId()),
  254. eventSourceId: this.content.getId()
  255. });
  256. return { localFilters: localFilters, globalFilters: globalFilters, selectionFilters: selectionFilters };
  257. };
  258. DataBehindTheVis.prototype._getProcessedHeadersAndCells = function _getProcessedHeadersAndCells(queryResult) {
  259. var _this5 = this;
  260. var dataHeaders = [];
  261. var dataCells = [];
  262. var usedMetadataColumns = this._getMetadataColumnList();
  263. if (queryResult) {
  264. (function () {
  265. /** when the widget is in not rendered state (eg. missing required slots), removing a slot does not do another query
  266. * the cached data will have one or more data row(s) extra
  267. * Hence we filter down to the only used meta data columns */
  268. var usedIds = _this5._getUsedMetadataColumnsIncluldingFiltersIds(usedMetadataColumns);
  269. var resultItemList = queryResult.getResultItemList();
  270. var unusedColumnIndices = [];
  271. _.each(resultItemList, function (item) {
  272. _.each(item.getDataItemList(), function (dataItem, index) {
  273. if (usedIds.indexOf(dataItem.getColumnId()) !== -1) {
  274. dataHeaders.push(dataItem.getLabel());
  275. } else {
  276. unusedColumnIndices.push(index);
  277. }
  278. });
  279. }.bind(_this5));
  280. var firstRowLevels = [];
  281. var columnCount = _this5._getColumnCount(resultItemList);
  282. for (var iRow = 0; iRow < queryResult.getRowCount(); iRow++) {
  283. var skipRow = false;
  284. var dataRow = [];
  285. var _loop = function _loop(iCol) {
  286. if (unusedColumnIndices.indexOf(iCol) === -1) {
  287. var value = queryResult.getValue(iRow, iCol);
  288. if (Array.isArray(value)) {
  289. dataRow.push.apply(dataRow, value.map(function (dataCellObj) {
  290. dataCellObj.toString = _this5._getCellValue.bind(_this5, dataCellObj);
  291. return dataCellObj;
  292. }));
  293. if (iRow === 0) {
  294. // capture the level from the first row including all nested values
  295. firstRowLevels[iCol] = _.pluck(value, 'ln');
  296. } else {
  297. // skip the row if at least one nested value level is deeper than the first row - expanded row
  298. skipRow = skipRow || _.some(value, function (v, offset) {
  299. return firstRowLevels[iCol][offset] < v.ln;
  300. });
  301. }
  302. } else {
  303. value.toString = _this5._getCellValue.bind(_this5, value);
  304. dataRow.push(value);
  305. }
  306. // skip thw row if the value is a summary type
  307. skipRow = skipRow || value.type === (queryResult.VALUE_TYPE && queryResult.VALUE_TYPE.SUMMARY);
  308. }
  309. };
  310. for (var iCol = 0; iCol < columnCount; iCol++) {
  311. _loop(iCol);
  312. }
  313. if (!skipRow) {
  314. dataCells.push(dataRow);
  315. }
  316. }
  317. })();
  318. }
  319. return {
  320. headers: dataHeaders,
  321. cells: dataCells
  322. };
  323. };
  324. /** get an array of used meta data column, filters ids */
  325. DataBehindTheVis.prototype._getUsedMetadataColumnsIncluldingFiltersIds = function _getUsedMetadataColumnsIncluldingFiltersIds(usedMetadataColumns) {
  326. var ids = [];
  327. _.each(usedMetadataColumns, function (dataColumn) {
  328. if (dataColumn && !dataColumn.isMissing()) {
  329. ids.push(dataColumn.getId());
  330. }
  331. });
  332. var extraColumnIdsFromNonProjectedFilters = this._getColumnIdsFromUnprojectedFilters();
  333. ids.push.apply(ids, extraColumnIdsFromNonProjectedFilters);
  334. // add extra virtual meta data column resulted by multi measure post processing
  335. var hasMultiMeasures = this._isMultiMeasureVisualization(this.visualization);
  336. if (hasMultiMeasures) {
  337. ids.push.apply(ids, ['_multiMeasuresValue', '_multiMeasuresSeries']);
  338. }
  339. return ids;
  340. };
  341. DataBehindTheVis.prototype._getMetadataColumnList = function _getMetadataColumnList() {
  342. var slots = this.visualization.getSlots();
  343. return _.map(slots.getDataItemList(), function (dataItem) {
  344. return dataItem.getMetadataColumn();
  345. });
  346. };
  347. DataBehindTheVis.prototype._getColumnCount = function _getColumnCount(queryResultItemList) {
  348. return _.chain(queryResultItemList).map(function (resultItem) {
  349. var isMeasure = _.every(resultItem.getDataItemList(), function (dataItem) {
  350. return dataItem.getAggregation() && dataItem.getType() === 'fact';
  351. });
  352. // when multiple measures are mapped to a slot, V5 result item will represent the measures as a single set
  353. var isMultiMeasure = isMeasure && resultItem.getDataItemList().length > resultItem.getValue(0).length;
  354. return isMultiMeasure ? resultItem.getDataItemList() : resultItem;
  355. }).flatten().value().length;
  356. };
  357. DataBehindTheVis.prototype._getCellValue = function _getCellValue(dataCell) {
  358. var isMeasure = this._isCellValueMeasure(dataCell);
  359. var value = isMeasure ? dataCell.value : dataCell.label;
  360. if (value === null) {
  361. return DSStringResources.get('nullValueText');
  362. } else if (isMeasure) {
  363. // round numbers types to at most 2 digits
  364. return Math.round(value * 100) / 100;
  365. } else {
  366. return value;
  367. }
  368. };
  369. DataBehindTheVis.prototype._isCellValueMeasure = function _isCellValueMeasure(dataCell) {
  370. return dataCell && _.isUndefined(dataCell.label);
  371. };
  372. DataBehindTheVis.prototype._getDataItems = function _getDataItems(dataset) {
  373. var slots = this.visualization.getSlots();
  374. if (dataset) {
  375. var slotMapping = slots.getMappingInfoList();
  376. var dataItems = [];
  377. slotMapping.forEach(function (item) {
  378. if (item.slot.getDefinition().getDatasetIdList().indexOf(dataset) !== -1) {
  379. dataItems.push(item.dataItem);
  380. }
  381. });
  382. return dataItems;
  383. } else {
  384. return slots.getDataItemList();
  385. }
  386. };
  387. DataBehindTheVis.prototype._getSelections = function _getSelections() {
  388. var selections = [];
  389. var pageContext = this.pageContextService.getPageContext();
  390. _.each(pageContext && pageContext.getPageContextItems(this._getItemContext()), function (pageContextItem) {
  391. if (pageContextItem.getDataPoints() && pageContextItem.getDataPoints().length) {
  392. selections.push({
  393. dataPoints: pageContextItem.getDataPoints()
  394. });
  395. } else if (pageContextItem.getValues() && pageContextItem.getValues().length) {
  396. selections.push({
  397. edges: pageContextItem.getValues()
  398. });
  399. }
  400. });
  401. return selections;
  402. };
  403. DataBehindTheVis.prototype._getItemContext = function _getItemContext() {
  404. var type = this.dashboard.getAppConfig('pageContainerType');
  405. if (!this.visualization) {
  406. return null;
  407. }
  408. return {
  409. origin: 'visualization',
  410. sourceId: this.visualization.getDataSource() && this.visualization.getDataSource().getId(),
  411. scope: ContentUtil.getPageContent(this.content, type).getId(),
  412. eventGroupId: this.eventGroups.getGroupId(this.content.getId()),
  413. //use the 'strict match option' and event source to only select brushing entries
  414. //that originate from this visualization.
  415. eventSourceId: this.content.getId(),
  416. _strictMatch: true
  417. };
  418. };
  419. return DataBehindTheVis;
  420. }();
  421. return DataBehindTheVis;
  422. });
  423. //# sourceMappingURL=DataBehindTheVis.js.map