OlapHelper.v2.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. 'use strict';
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. /**
  4. * Licensed Materials - Property of IBM
  5. * IBM Business Analytics (C) Copyright IBM Corp. 2019, 2020
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. /**
  9. * @classdesc helper class that is used to deal with OLAP
  10. */
  11. define([], function () {
  12. var OlapHelperV2 = function () {
  13. function OlapHelperV2(dashboard, visualization) {
  14. _classCallCheck(this, OlapHelperV2);
  15. this.dashboard = dashboard;
  16. this.visualization = visualization;
  17. }
  18. /**
  19. * @desc Accepts olap column on slot
  20. * @param {String} targetSlotId - target slot id
  21. * @param {String} sourceSlotId - source slot id, it is undefined if dnd from metadata tree
  22. * @param {Number} indexInSourceSlot - slot item index in source slot, it is undefined if dnd from metadata tree
  23. * @param {Array} sourceColumns - metadata columns to be added into target slot
  24. * @param {Number} indexInTargetSlot - position to add
  25. * @param {Boolean} isReplace - if it is to replace the column in the position
  26. * @return {Boolean} return True if the two lists of columns can be in the same slot
  27. */
  28. OlapHelperV2.prototype.acceptsOlapV2 = function acceptsOlapV2(_ref) {
  29. var targetSlotId = _ref.targetSlotId,
  30. sourceSlotId = _ref.sourceSlotId,
  31. indexInSourceSlot = _ref.indexInSourceSlot,
  32. sourceColumns = _ref.sourceColumns,
  33. indexInTargetSlot = _ref.indexInTargetSlot,
  34. isReplace = _ref.isReplace;
  35. var expandCollapseFeatureFlag = !this.dashboard.getGlassCoreSvc('.FeatureChecker').checkValue('dashboard', 'expandCollapse', 'disabled');
  36. // There are no restrictions on OLAP columns on non-crosstab visualizations
  37. if (!expandCollapseFeatureFlag || this.visualization.getType() !== 'Crosstab') {
  38. return true;
  39. }
  40. var slotsAPI = this.visualization.getSlots();
  41. var targetSlotAPI = slotsAPI.getSlot(targetSlotId);
  42. var sourceSlotAPI = sourceSlotId ? slotsAPI.getSlot(sourceSlotId) : undefined;
  43. var sourceMetadataColumns = sourceSlotAPI ? sourceSlotAPI.getDataItemList().slice(indexInSourceSlot, indexInSourceSlot + 1).map(function (item) {
  44. return item.getMetadataColumn();
  45. }) : sourceColumns;
  46. if (targetSlotAPI.getDefinition().getType() !== 'category' && (!sourceSlotAPI || sourceSlotAPI.getDefinition().getType() !== 'category')) {
  47. // OLAP restrictions only apply to categorical slots
  48. return true;
  49. }
  50. var hasOlapColumn = false;
  51. var olapMeasureColumn = sourceMetadataColumns.find(function (col) {
  52. var isOlapColumn = col.isOlapColumn();
  53. hasOlapColumn = hasOlapColumn || isOlapColumn;
  54. return isOlapColumn && col.getType() === 'fact';
  55. });
  56. if (olapMeasureColumn) {
  57. // OLAP measure column is not allowed to be dropped to category slot. Aggregation over measure sets is not supported.
  58. return false;
  59. }
  60. if (!hasOlapColumn) {
  61. // Need at least one OLAP column for OLAP restrictions to apply
  62. return true;
  63. }
  64. var accepts = void 0;
  65. if (sourceSlotAPI) {
  66. accepts = this._acceptsSlotItem({ slotsAPI: slotsAPI, targetSlotAPI: targetSlotAPI, sourceSlotAPI: sourceSlotAPI, sourceMetadataColumns: sourceMetadataColumns, isReplace: isReplace, indexInTargetSlot: indexInTargetSlot, indexInSourceSlot: indexInSourceSlot });
  67. } else {
  68. accepts = this._acceptsMetadataColumns({ slotsAPI: slotsAPI, targetSlotAPI: targetSlotAPI, sourceMetadataColumns: sourceMetadataColumns });
  69. }
  70. var targetMetadataColumns = targetSlotAPI.getDataItemList().map(function (dataItem) {
  71. return dataItem.getMetadataColumn();
  72. });
  73. var isSameSlot = targetSlotId === sourceSlotId;
  74. return accepts && this._acceptsColumnsInSlot({ targetMetadataColumns: targetMetadataColumns, sourceMetadataColumns: sourceMetadataColumns, indexInTargetSlot: indexInTargetSlot, isReplace: isReplace, isSameSlot: isSameSlot, indexInSourceSlot: indexInSourceSlot });
  75. };
  76. /**
  77. * Gets OLAP hierarchy by a metadata column
  78. * @param {Object} metadata column
  79. * @return {Object} olap metadata hierarchy, return null if metadata column is not olap
  80. */
  81. OlapHelperV2.prototype._getHierarchy = function _getHierarchy(metadataColumn) {
  82. if (metadataColumn.isOlapColumn()) {
  83. if (metadataColumn.isHierarchy()) {
  84. return metadataColumn;
  85. } else if (metadataColumn.isLevel()) {
  86. return metadataColumn.getParent();
  87. } else if (metadataColumn.isProperty()) {
  88. var grandparent = metadataColumn.getParent().getParent();
  89. return grandparent && grandparent.isHierarchy() && grandparent;
  90. }
  91. }
  92. return null;
  93. };
  94. /**
  95. * Check if two olap metadata columns are in the same hierarchy
  96. * @param {Object} metadata column 1
  97. * @param {Object} metadata column 2
  98. * @return {Boolean} return true if the two metadata column are in the same hierarchy
  99. */
  100. OlapHelperV2.prototype._inSameHierarchy = function _inSameHierarchy(metadataColumn1, metadataColumn2) {
  101. if (metadataColumn1.isOlapColumn() && metadataColumn2.isOlapColumn()) {
  102. var h1 = this._getHierarchy(metadataColumn1);
  103. var h2 = this._getHierarchy(metadataColumn2);
  104. return h1 && h2 && h1.getId() === h2.getId();
  105. }
  106. return false;
  107. };
  108. OlapHelperV2.prototype._getHierarchies = function _getHierarchies(metadataColumns) {
  109. var _this = this;
  110. var hierarchies = [];
  111. metadataColumns.forEach(function (col) {
  112. var hier = col && _this._getHierarchy(col);
  113. if (hier) {
  114. if (!hierarchies.find(function (id) {
  115. return id === hier.getId();
  116. })) {
  117. hierarchies.push(hier.getId());
  118. }
  119. }
  120. });
  121. return hierarchies;
  122. };
  123. /**
  124. * @desc accepts a slot item
  125. * @param {Object} slotsAPI - slots api
  126. * @param {Object} targetSlotAPI - target slot api
  127. * @param {Object} sourceSlotAPI - source slot api
  128. * @param {Array} sourceMetadataColumns - metadata columns to be added into target slot
  129. * @param {Number} indexInSourceSlot - slot item index in source slot, it is undefined if dnd from metadata tree
  130. * @param {Number} indexInTargetSlot - position to add
  131. * @param {Boolean} isReplace - if it is to replace the column in the position
  132. * @return {Boolean} return True if a hierarchy is not on both col and row
  133. */
  134. OlapHelperV2.prototype._acceptsSlotItem = function _acceptsSlotItem(_ref2) {
  135. var _this2 = this;
  136. var slotsAPI = _ref2.slotsAPI,
  137. targetSlotAPI = _ref2.targetSlotAPI,
  138. sourceSlotAPI = _ref2.sourceSlotAPI,
  139. sourceMetadataColumns = _ref2.sourceMetadataColumns,
  140. isReplace = _ref2.isReplace,
  141. indexInTargetSlot = _ref2.indexInTargetSlot,
  142. indexInSourceSlot = _ref2.indexInSourceSlot;
  143. var metadataColumnsTargetSlot = targetSlotAPI.getDataItemList().map(function (dataItem) {
  144. return dataItem.getMetadataColumn();
  145. });
  146. var metadataColumnsSourceSlot = sourceSlotAPI.getDataItemList().map(function (dataItem) {
  147. return dataItem.getMetadataColumn();
  148. });
  149. var sourceMetadataColumn = sourceMetadataColumns[0]; //can only drag one slot item at a time
  150. if (isReplace) {
  151. var swapTarget = metadataColumnsTargetSlot[indexInTargetSlot];
  152. metadataColumnsTargetSlot[indexInTargetSlot] = sourceMetadataColumn;
  153. metadataColumnsSourceSlot[indexInSourceSlot] = swapTarget;
  154. } else {
  155. metadataColumnsTargetSlot.splice(indexInTargetSlot, 0, sourceMetadataColumn);
  156. metadataColumnsSourceSlot.splice(indexInSourceSlot, 1);
  157. }
  158. //get rest of category slot IDs
  159. var restSlotIDs = slotsAPI.getSlotList().filter(function (slot) {
  160. return slot.getDefinition().getId() !== targetSlotAPI.getDefinition().getId() && slot.getDefinition().getId() !== sourceSlotAPI.getDefinition().getId() && slot.getDefinition().getType() === 'category';
  161. }).map(function (s) {
  162. return s.getDefinition().getId();
  163. });
  164. var slotsColumns = restSlotIDs.map(function (id) {
  165. return slotsAPI.getSlot(id).getDataItemList().map(function (dataItem) {
  166. return dataItem.getMetadataColumn();
  167. });
  168. });
  169. var hierarchies = [];
  170. var isSameSlot = targetSlotAPI.getDefinition().getId() === sourceSlotAPI.getDefinition().getId();
  171. if (!isSameSlot) {
  172. if (targetSlotAPI.getDefinition().getType() === 'category') {
  173. slotsColumns.push(metadataColumnsTargetSlot);
  174. }
  175. if (sourceSlotAPI.getDefinition().getType() === 'category') {
  176. slotsColumns.push(metadataColumnsSourceSlot);
  177. }
  178. }
  179. return slotsColumns.every(function (columns) {
  180. var hierList = _this2._getHierarchies(columns);
  181. if (hierarchies.some(function (h) {
  182. return hierList.indexOf(h) !== -1;
  183. })) {
  184. return false;
  185. }
  186. hierarchies.push.apply(hierarchies, hierList);
  187. return true;
  188. });
  189. };
  190. /**
  191. * @desc accepts a list of metadata columns from metadata tree
  192. * @param {Object} slotsAPI - slots api
  193. * @param {Object} targetSlotAPI - target slot api
  194. * @param {Array} sourceMetadataColumns - metadata columns to be added into target slot
  195. * @return {Boolean} return True if a hierarchy is not on both col and row
  196. */
  197. OlapHelperV2.prototype._acceptsMetadataColumns = function _acceptsMetadataColumns(_ref3) {
  198. var _this3 = this;
  199. var slotsAPI = _ref3.slotsAPI,
  200. targetSlotAPI = _ref3.targetSlotAPI,
  201. sourceMetadataColumns = _ref3.sourceMetadataColumns;
  202. var categorySlots = slotsAPI.getSlotList().filter(function (slot) {
  203. return slot.getDefinition().getType() === 'category';
  204. });
  205. var dataItemList = [];
  206. categorySlots.forEach(function (slot) {
  207. var items = slot.getDataItemList().filter(function (dataItem) {
  208. return dataItem.getColumnId() !== '_multiMeasuresSeries';
  209. });
  210. dataItemList.push.apply(dataItemList, items);
  211. });
  212. var projectedMetadataColumns = dataItemList.map(function (dataItem) {
  213. return dataItem.getMetadataColumn();
  214. });
  215. var targetSlotDataItemList = targetSlotAPI.getDataItemList();
  216. var targetSlotMetadataColumnIds = targetSlotDataItemList.filter(function (dataItem) {
  217. return dataItem.getColumnId() !== '_multiMeasuresSeries';
  218. }).map(function (dataItem) {
  219. return dataItem.getColumnId();
  220. });
  221. var columnsNotInTargetSlot = projectedMetadataColumns.filter(function (column) {
  222. return targetSlotMetadataColumnIds.indexOf(column.getId()) === -1;
  223. });
  224. var hierarchyNotInTargetSlot = columnsNotInTargetSlot.some(function (column) {
  225. return sourceMetadataColumns.some(function (dragColumn) {
  226. return _this3._inSameHierarchy(dragColumn, column);
  227. });
  228. });
  229. // Find any columns which will be in different slots from the target slot,
  230. // and which are in the same hierarchy as the source columns
  231. if (hierarchyNotInTargetSlot) {
  232. return false;
  233. }
  234. return true;
  235. };
  236. /**
  237. * @desc Check if a list of metadata columns can be added into another list of columns in the same slot
  238. * @param {Array} targetMetadataColumns - metadata columns in target slot
  239. * @param {Array} sourceMetadataColumns - metadata columns to be added into target slot
  240. * @param {Number} indexInTargetSlot - index in target slot to add
  241. * @param {Number} indexInSourceSlot - slot item index in source slot, it is undefined if dnd from metadata tree
  242. * @param {Boolean} isReplace - if it is to replace the column in the position
  243. * @param {Boolean} IsSameSlot - if it is drag and drop in the same slot
  244. * @return {Boolean} return True if the two lists of columns can be in the same slot
  245. */
  246. OlapHelperV2.prototype._acceptsColumnsInSlot = function _acceptsColumnsInSlot(_ref4) {
  247. var targetMetadataColumns = _ref4.targetMetadataColumns,
  248. sourceMetadataColumns = _ref4.sourceMetadataColumns,
  249. indexInTargetSlot = _ref4.indexInTargetSlot,
  250. isReplace = _ref4.isReplace,
  251. isSameSlot = _ref4.isSameSlot,
  252. indexInSourceSlot = _ref4.indexInSourceSlot;
  253. //check if OLAP columns are in natural order and consecutive in slot
  254. if (!targetMetadataColumns || !sourceMetadataColumns || indexInTargetSlot === undefined || isReplace === undefined) {
  255. return false;
  256. }
  257. var targetSlotMetadataColumns = [].concat(targetMetadataColumns);
  258. if (indexInTargetSlot < 0) {
  259. indexInTargetSlot = targetSlotMetadataColumns.length;
  260. }
  261. if (isReplace) {
  262. if (isSameSlot) {
  263. //swap
  264. //can only drag one slot item, so actually sourceMetadataColumns.length is 1
  265. var items = targetSlotMetadataColumns.splice.apply(targetSlotMetadataColumns, [indexInTargetSlot, 1].concat(sourceMetadataColumns));
  266. targetSlotMetadataColumns.splice.apply(targetSlotMetadataColumns, [indexInSourceSlot, 1].concat(items));
  267. } else {
  268. targetSlotMetadataColumns.splice.apply(targetSlotMetadataColumns, [indexInTargetSlot, 1].concat(sourceMetadataColumns));
  269. }
  270. } else {
  271. if (isSameSlot) {
  272. //if dnd in the same slot, remove dragged item from target slot columns
  273. targetSlotMetadataColumns.splice(indexInSourceSlot, 1);
  274. if (indexInSourceSlot < indexInTargetSlot) {
  275. indexInTargetSlot--;
  276. }
  277. }
  278. targetSlotMetadataColumns.splice.apply(targetSlotMetadataColumns, [indexInTargetSlot, 0].concat(sourceMetadataColumns));
  279. }
  280. return this._isColumnOrderValidInSlot(targetSlotMetadataColumns);
  281. };
  282. OlapHelperV2.prototype._isColumnOrderValidInSlot = function _isColumnOrderValidInSlot(metadataColumns) {
  283. var _this4 = this;
  284. var hierarchyMap = {};
  285. metadataColumns.forEach(function (col, index) {
  286. var hierarchy = _this4._getHierarchy(col);
  287. if (hierarchy) {
  288. if (hierarchyMap[hierarchy.getId()]) {
  289. hierarchyMap[hierarchy.getId()].metadataColumns.push({
  290. metadataColumn: col,
  291. index: index
  292. });
  293. } else {
  294. hierarchyMap[hierarchy.getId()] = {
  295. metadataColumns: [{
  296. metadataColumn: col,
  297. index: index
  298. }],
  299. hierarchy: hierarchy
  300. };
  301. }
  302. }
  303. });
  304. return this._isHierarchyInNaturalOrderAndConsecutiveNestingLevels(hierarchyMap);
  305. };
  306. OlapHelperV2.prototype._isHierarchyInNaturalOrderAndConsecutiveNestingLevels = function _isHierarchyInNaturalOrderAndConsecutiveNestingLevels(hierarchyMap) {
  307. var _this5 = this;
  308. var hierarchies = Object.values(hierarchyMap);
  309. return !hierarchies.some(function (hierarchy) {
  310. var hierarchyItemIds = hierarchy.hierarchy.getHierarchyLevelIds();
  311. // Find natural order index for the column in each position
  312. var indexes = hierarchy.metadataColumns.map(function (col) {
  313. var metadataColumn = col.metadataColumn;
  314. if (metadataColumn.isHierarchy()) {
  315. return -1;
  316. } else if (metadataColumn.isProperty()) {
  317. metadataColumn = metadataColumn.getParent();
  318. }
  319. return hierarchyItemIds.findIndex(function (id) {
  320. return metadataColumn.getId() === id;
  321. });
  322. });
  323. if (!_this5._isDescSorted(indexes)) {
  324. return true;
  325. }
  326. indexes = hierarchy.metadataColumns.map(function (col) {
  327. return col.index;
  328. });
  329. if (!_this5._isHierarchyInConsecutiveNestingLevels(indexes)) {
  330. return true;
  331. }
  332. });
  333. };
  334. OlapHelperV2.prototype._isHierarchyInConsecutiveNestingLevels = function _isHierarchyInConsecutiveNestingLevels(indexes) {
  335. var consecutive = true;
  336. for (var i = 0; i <= indexes.length - 2; i++) {
  337. if (indexes[i] + 1 !== indexes[i + 1]) {
  338. consecutive = false;
  339. break;
  340. }
  341. }
  342. return consecutive;
  343. };
  344. OlapHelperV2.prototype._isDescSorted = function _isDescSorted(indexes) {
  345. var sorted = true;
  346. for (var i = 0; i < indexes.length - 1; i++) {
  347. if (indexes[i] > indexes[i + 1]) {
  348. sorted = false;
  349. break;
  350. }
  351. }
  352. return sorted;
  353. };
  354. return OlapHelperV2;
  355. }();
  356. return OlapHelperV2;
  357. });
  358. //# sourceMappingURL=OlapHelper.v2.js.map