SmartsCompareCardRecommenderHelper.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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. /*
  4. *+------------------------------------------------------------------------+
  5. *| Licensed Materials - Property of IBM
  6. *| IBM Cognos Products: Dashboard
  7. *| (C) Copyright IBM Corp. 2018, 2020
  8. *|
  9. *| US Government Users Restricted Rights - Use, duplication or disclosure
  10. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  11. *+------------------------------------------------------------------------+
  12. */
  13. define(['underscore', '../../lib/@waca/core-client/js/core-client/ui/core/Class', './IRUtils', '../../lib/@waca/dashboard-common/dist/core/UniqueHashIdBuilder', '../../filters/FilterSpecHelper'], function (_, Class, IRUtils, UniqueHashIdBuilder, FilterSpecHelper) {
  14. 'use strict';
  15. var _cntr = 0;
  16. var SLOT_TYPE_MAP = {
  17. 'fact': 'ordinal',
  18. 'attribute': 'category'
  19. };
  20. var SmartsCompareCardRecommenderHelper = Class.extend({
  21. // constructor
  22. init: function init(columnIds, requestOptions) {
  23. this.columnIds = columnIds;
  24. this.requestOptions = requestOptions;
  25. this.id = 'id_' + ++_cntr;
  26. SmartsCompareCardRecommenderHelper.inherited('init', this, arguments);
  27. },
  28. getRequestParameters: function getRequestParameters(bPartialTempModule) {
  29. var columnIds = this.columnIds,
  30. requestOptions = this.requestOptions;
  31. var assetId = requestOptions.assetId,
  32. numResults = requestOptions.numResults,
  33. sourceType = requestOptions.sourceType,
  34. module = requestOptions.module;
  35. var columns = this._getColumnsInfo(columnIds);
  36. var _columnIds = columns.map(function (column) {
  37. return column.id;
  38. }).reduce(function (acc, columnIds) {
  39. return acc.concat(columnIds);
  40. }, []);
  41. var tempModuleJSON = bPartialTempModule ? module.copyPartialMoserModuleJSON(_columnIds) : module.getTemporarySessionModuleJSON();
  42. var widgetSpec = requestOptions.widgetSpec;
  43. var visualization = requestOptions.visualization;
  44. var filters = this._toIRFilters(visualization, widgetSpec);
  45. var irContent = {
  46. id: this.id,
  47. topicId: '',
  48. combinations: [{
  49. columnCombinations: this._getColumnCombinationsForIR(visualization),
  50. filters: filters
  51. }]
  52. };
  53. var requestParameters = {
  54. sourceModuleId: assetId,
  55. sourceModule: JSON.stringify(tempModuleJSON),
  56. sourceType: sourceType,
  57. includedColumns: [],
  58. excludedColumns: [],
  59. shapedModule: !!module.getShapingId(), //module has shaping
  60. irContents: [irContent],
  61. maxCardsPerIR: numResults || 10
  62. };
  63. return module.saveTemporaryModule().then(function (shapingModuleId) {
  64. if (assetId !== shapingModuleId) {
  65. requestParameters.tempModuleId = shapingModuleId;
  66. }
  67. return requestParameters;
  68. });
  69. },
  70. _restoreHiddenCustomGroupCols: function _restoreHiddenCustomGroupCols(irContent) {
  71. var replacements = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  72. if (replacements.length === 0) {
  73. return;
  74. }
  75. Object.keys(irContent).forEach(function (key) {
  76. irContent[key].forEach(function (irContentItem) {
  77. (irContentItem.combinations || []).forEach(function (combination) {
  78. (combination.columnCombinations || []).forEach(function (columnCombination, i) {
  79. var replacement = replacements.find(function (item) {
  80. return item.index === i;
  81. });
  82. if (replacement && replacement.to === columnCombination.id) {
  83. columnCombination.id = replacement.from;
  84. }
  85. });
  86. });
  87. });
  88. });
  89. },
  90. _replaceHiddenCustomGroupCols: function _replaceHiddenCustomGroupCols(columns, options) {
  91. var _this = this;
  92. var visualization = options.visualization,
  93. moserDataSources = options.moserDataSources;
  94. var sourceId = visualization.getDataSource().getId();
  95. if (moserDataSources && moserDataSources.isConsumerGroupColumn) {
  96. columns.forEach(function (columnEntry, i) {
  97. var itemId = columnEntry.itemId;
  98. if (moserDataSources.isConsumerGroupColumn(sourceId, itemId)) {
  99. var customGroup = moserDataSources.getCustomGroupData(sourceId, itemId);
  100. if (customGroup && customGroup.basedOnMoserObjectId) {
  101. var from = columns[i].itemId;
  102. var to = customGroup.basedOnMoserObjectId;
  103. columns[i].itemId = to;
  104. if (!_this.requestOptions.hiddenCustomGroupColumnReplacements) {
  105. _this.requestOptions.hiddenCustomGroupColumnReplacements = [];
  106. }
  107. _this.requestOptions.hiddenCustomGroupColumnReplacements.push({
  108. index: i,
  109. from: from,
  110. to: to
  111. });
  112. }
  113. }
  114. });
  115. }
  116. },
  117. _getColumnCombinationsForIR: function _getColumnCombinationsForIR(visualization) {
  118. var _columnCombinations = this._getColumnCombinations(visualization);
  119. this._replaceHiddenCustomGroupCols(_columnCombinations, {
  120. visualization: visualization,
  121. moserDataSources: this.requestOptions.moserDataSources
  122. });
  123. var colComboForIR = _columnCombinations.map(function (entry) {
  124. var irEntry = {
  125. id: entry.itemId
  126. };
  127. if (entry.aggregation) {
  128. irEntry.aggregation = entry.aggregation;
  129. }
  130. if (entry.autoGrouping) {
  131. irEntry.autoGrouping = entry.autoGrouping;
  132. }
  133. return irEntry;
  134. });
  135. return colComboForIR;
  136. },
  137. _getColumnCombinations: function _getColumnCombinations(visualization) {
  138. var combinations = [];
  139. var slots = visualization.getSlots().getMappedSlotList();
  140. slots.forEach(function (slotAPI) {
  141. var dataItems = slotAPI.getDataItemList(true /* ignoreDefaultDataItemMapping */);
  142. dataItems.forEach(function (dataItem) {
  143. var slotAggType = dataItem.getAggregation();
  144. var id = dataItem.getId();
  145. var itemId = dataItem.getColumnId();
  146. var dataType = dataItem.getMetadataColumn().getType();
  147. var label = dataItem.getLabel();
  148. var finalSlotType = SLOT_TYPE_MAP[dataItem.getType()];
  149. if (itemId !== '_multiMeasuresSeries') {
  150. if (slotAggType && finalSlotType === 'ordinal') {
  151. combinations.push({
  152. id: id,
  153. itemId: itemId,
  154. dataType: dataType,
  155. finalSlotType: finalSlotType,
  156. label: label,
  157. aggregation: IRUtils.IRAggregateType(slotAggType) //TODO this is IR aggregation type, could keep it dashboard and transform to IR where used
  158. });
  159. } else {
  160. var autoGrouping = void 0;
  161. if (dataItem.getBinning()) {
  162. autoGrouping = {
  163. size: dataItem.getBinning().bins
  164. };
  165. }
  166. combinations.push({
  167. id: id,
  168. itemId: itemId,
  169. dataType: dataType,
  170. finalSlotType: finalSlotType,
  171. label: label,
  172. autoGrouping: autoGrouping
  173. });
  174. }
  175. }
  176. });
  177. });
  178. return combinations;
  179. },
  180. transformCompareRecommendationResponse: function transformCompareRecommendationResponse(module, widgetSpec, irContent) {
  181. //TODO , requestOptions.module, requestOptions.widgetSpec
  182. var requestOptions = this.requestOptions;
  183. var postSpec = this._fromIRContent(requestOptions, widgetSpec, irContent);
  184. return postSpec;
  185. },
  186. _getColumnsInfo: function _getColumnsInfo(columnIds) {
  187. return columnIds.map(function (columnId) {
  188. return { id: [columnId] };
  189. });
  190. },
  191. _toIRFilters: function _toIRFilters(visualization, widgetSpec) {
  192. var filters = this._getFilters(visualization);
  193. var topBottom = this._getTopBottom(widgetSpec);
  194. var filtersForRequest = [];
  195. IRUtils.localFilters2IRFilters(filters, filtersForRequest, visualization);
  196. IRUtils.topBottomToIRFilters(topBottom, filtersForRequest);
  197. return filtersForRequest;
  198. },
  199. //TODO: this file is getting big, breakup/move utility functions
  200. _getFilters: function _getFilters(visualization) {
  201. var _this2 = this;
  202. //NOT ideal, have to use local filter spec
  203. var localFilters = visualization.getLocalFilters().getFilterList();
  204. if (!localFilters) {
  205. localFilters = [];
  206. }
  207. var leafFilters = [];
  208. // We need to convert all the filters to leaf level filters (ones with columnId),
  209. // beacuse some cases, the localfilter is a datapoint filter, which is a compound filter (no columnId)
  210. localFilters.forEach(function (localFilter) {
  211. return _this2._collectLeafFilterEntries(localFilter, leafFilters);
  212. });
  213. return JSON.parse(JSON.stringify(leafFilters));
  214. },
  215. /**
  216. * Given a filter, collect its leaf level filters
  217. * @param {Object} filter the filter spec
  218. * @param {Array} leafFilterEntries an array used to collect the leaf level filters (ones with columnId)
  219. */
  220. _collectLeafFilterEntries: function _collectLeafFilterEntries(filter, leafFilterEntries) {
  221. var _this3 = this;
  222. if (FilterSpecHelper.isCompound(filter)) {
  223. filter.values.forEach(function (value) {
  224. if (value) {
  225. _this3._collectLeafFilterEntries(value, leafFilterEntries);
  226. }
  227. });
  228. } else {
  229. leafFilterEntries.push(filter);
  230. }
  231. },
  232. _getTopBottom: function _getTopBottom(orgWidgetSpec) {
  233. //NOT ideal, have to read spec
  234. var topBottom = [];
  235. var widgetSpec = JSON.parse(JSON.stringify(orgWidgetSpec));
  236. var dataViews = widgetSpec.data.dataViews || [];
  237. dataViews.forEach(function (viewItem) {
  238. var dataItems = viewItem.dataItems || [];
  239. dataItems.forEach(function (item) {
  240. var selectionItems = item.selection || [];
  241. selectionItems.forEach(function (selection) {
  242. if (selection && selection.topBottom) {
  243. topBottom.push({
  244. id: item.id,
  245. itemId: item.itemId,
  246. selection: selection
  247. });
  248. }
  249. });
  250. });
  251. });
  252. return topBottom;
  253. },
  254. /*
  255. sample IR response
  256. {
  257. "smartsStatus" : "OK",
  258. "ircontent" : {
  259. "id" : {
  260. "id" : "id",
  261. "topicId" : "Movies.Budget",
  262. "combinations" : [ {
  263. "columnCombinations" : [ "Movies.Genre", "Movies.Income", "Movies.Gross" ],
  264. "filters" : [ {
  265. "id" : "Movies.Budget",
  266. "filterId" : "0",
  267. "lowValue" : "5",
  268. "highValue" : "10",
  269. "invertFlag" : false,
  270. "type" : "NUMERIC",
  271. "numericOperator" : "LESS_THAN"
  272. }, {
  273. "id" : "Movies.Genre",
  274. "filterId" : "0",
  275. "topBottomType" : "TOP",
  276. "method" : "PERCENTAGE",
  277. "byColumn" : "Movies.Budget",
  278. "domainSize" : 10,
  279. "type" : "TOP_BOTTOM"
  280. }, {
  281. "id" : "Movies.Budget",
  282. "filterId" : "0",
  283. "label" : "Include",
  284. "values" : [ {
  285. "uniqueValue" : "Movies.Studio.Columbia",
  286. "displayValue" : "Columbia"
  287. } ],
  288. "type" : "INCLUSION_EXCLUSION",
  289. "include" : true
  290. } ],
  291. "score" : 0.0
  292. } ],
  293. "score" : 0.0
  294. }
  295. }
  296. }
  297. */
  298. _fromIRContent: function _fromIRContent(requestOptions, widgetSpec, irContent) {
  299. var _this4 = this;
  300. var visualization = requestOptions.visualization;
  301. var candidateWidgetSpecs = [];
  302. var currentColumnCombinations = this._getColumnCombinations(visualization);
  303. this._restoreHiddenCustomGroupCols(irContent, requestOptions.hiddenCustomGroupColumnReplacements);
  304. if (irContent && irContent[this.id]) {
  305. irContent[this.id].forEach(function (irContentItem) {
  306. var widgetSpecs = _this4._processIRContent(irContentItem, widgetSpec, currentColumnCombinations, irContentItem.details, visualization);
  307. candidateWidgetSpecs.push.apply(candidateWidgetSpecs, widgetSpecs);
  308. });
  309. }
  310. return candidateWidgetSpecs;
  311. },
  312. _processIRContent: function _processIRContent(irContent, widgetSpec, currentColumnCombinations, reason, visualization) {
  313. var _this5 = this;
  314. var combinations = irContent.combinations;
  315. var candidateWidgetSpecs = [];
  316. var details = {
  317. reason: reason
  318. };
  319. combinations.forEach(function (irCandidate) {
  320. var filters = {
  321. local: [],
  322. topBottom: []
  323. };
  324. var irFilters = irCandidate.filters || [];
  325. irFilters.forEach(function (irFilter) {
  326. if (irFilter.type === 'TOP_BOTTOM') {
  327. var filter = IRUtils.IRFilters2TopBottom(irFilter, widgetSpec, currentColumnCombinations, details, visualization);
  328. if (filter) {
  329. filters.topBottom.push(filter);
  330. }
  331. } else {
  332. var _filter = IRUtils.IRFilters2LocalFilter(irFilter, widgetSpec, currentColumnCombinations, details, visualization);
  333. if (_filter) {
  334. filters.local.push(_filter);
  335. }
  336. }
  337. });
  338. widgetSpec.titleHtml = undefined;
  339. widgetSpec.name = undefined;
  340. var dataItemIdsMap = {};
  341. var candidateWidgetSpec = _this5._transform2WidgetSpec(widgetSpec, currentColumnCombinations, irCandidate.columnCombinations, filters, details, visualization, dataItemIdsMap);
  342. if (candidateWidgetSpec) {
  343. candidateWidgetSpecs.push({
  344. spec: candidateWidgetSpec,
  345. details: details,
  346. score: irCandidate.score
  347. });
  348. }
  349. });
  350. return candidateWidgetSpecs;
  351. },
  352. _transform2WidgetSpec: function _transform2WidgetSpec(widgetSpec, currentColumnCombinations, columnCombinations, filters, details, visualization, dataItemIdsMap) {
  353. var spec = JSON.parse(JSON.stringify(widgetSpec));
  354. this._assimilateTopBottom(spec, filters.topBottom, currentColumnCombinations, dataItemIdsMap);
  355. this._assimilateFilters(spec, filters.local);
  356. this._assimilateComparableMeasures(spec, columnCombinations, currentColumnCombinations, details, visualization, dataItemIdsMap);
  357. return spec;
  358. },
  359. _replaceSlotId: function _replaceSlotId(spec, oldId, newId) {
  360. spec.slotmapping.slots.forEach(function (item, index) {
  361. var dataItemIndex = item.dataItems.indexOf(oldId);
  362. if (dataItemIndex !== -1) {
  363. spec.slotmapping.slots[index].dataItems[dataItemIndex] = newId;
  364. }
  365. });
  366. },
  367. _assimilateTopBottom: function _assimilateTopBottom(spec, topBottomItems, currentColumnCombinations, dataItemIdsMap) {
  368. var _this6 = this;
  369. topBottomItems.forEach(function (item) {
  370. _this6._assimilateTopBottomItem(spec, item, currentColumnCombinations, dataItemIdsMap);
  371. });
  372. },
  373. _findMatchingColumnEntry: function _findMatchingColumnEntry(topBottomSpec, currentColumnCombinations) {
  374. var currentColEntry = void 0;
  375. currentColumnCombinations.some(function (entry) {
  376. if (entry.id === topBottomSpec.id) {
  377. currentColEntry = entry;
  378. return true;
  379. }
  380. });
  381. if (!currentColEntry) {
  382. var isAppropriateSlotForTopBottom = function isAppropriateSlotForTopBottom(entry, topBottomSpec) {
  383. if (topBottomSpec.selection[0].context) {
  384. //is contextual, should apply on a category
  385. if (entry.finalSlotType !== 'ordinal') {
  386. return true;
  387. } else {
  388. return false;
  389. }
  390. }
  391. return true;
  392. };
  393. if (currentColumnCombinations.length === 1) {
  394. currentColEntry = currentColumnCombinations[0].itemId === topBottomSpec.itemId ? currentColumnCombinations[0] : undefined;
  395. } else {
  396. currentColumnCombinations.some(function (entry) {
  397. if (entry.itemId === topBottomSpec.itemId && isAppropriateSlotForTopBottom(entry, topBottomSpec)) {
  398. currentColEntry = entry;
  399. return true;
  400. }
  401. });
  402. }
  403. }
  404. return currentColEntry;
  405. },
  406. _assimilateTopBottomItem: function _assimilateTopBottomItem(spec, topBottomSpec, currentColumnCombinations, dataItemIdsMap) {
  407. var currentColEntry = this._findMatchingColumnEntry(topBottomSpec, currentColumnCombinations);
  408. if (!currentColEntry) {
  409. return;
  410. }
  411. var dataItemRef = IRUtils.findDataItem(spec, function (dataItem) {
  412. return dataItem.id === currentColEntry.id;
  413. });
  414. if (!dataItemRef) {
  415. // unable to find proper data item
  416. return;
  417. }
  418. var originalDataItem = spec.data.dataViews[dataItemRef.viewIndex].dataItems[dataItemRef.viewItemIndex];
  419. var oldId = originalDataItem.id;
  420. var newId = UniqueHashIdBuilder.createUniqueHashId(originalDataItem.itemId, dataItemIdsMap);
  421. dataItemIdsMap[newId] = newId;
  422. spec.data.dataViews[dataItemRef.viewIndex].dataItems[dataItemRef.viewItemIndex] = _extends({}, topBottomSpec, {
  423. id: newId
  424. });
  425. this._replaceSlotId(spec, oldId, newId);
  426. },
  427. _assimilateFilters: function _assimilateFilters(spec, localFilters) {
  428. if (localFilters.length === 0 && spec.localFilters === undefined) {
  429. return;
  430. }
  431. spec.localFilters = localFilters;
  432. },
  433. _assimilateComparableMeasures: function _assimilateComparableMeasures(spec, columnCombinations, currentColumnCombinations, details, visualization, dataItemIdsMap) {
  434. var _columnCombinations = columnCombinations.map(function (col) {
  435. return col.id;
  436. });
  437. var combo = currentColumnCombinations.map(function (item) {
  438. return item.itemId;
  439. });
  440. var _$difference = _.difference(combo, _columnCombinations),
  441. from = _$difference[0];
  442. var _$difference2 = _.difference(_columnCombinations, combo),
  443. to = _$difference2[0];
  444. if (!from && !!to) {
  445. from = combo[0]; //all columns in currentColumnCombinations are the same and is included in the IR _columnCombinations
  446. }
  447. var id = void 0;
  448. currentColumnCombinations.forEach(function (curr) {
  449. if (curr.itemId === from && curr.finalSlotType != 'category' && !id) {
  450. id = curr.id;
  451. }
  452. });
  453. if (!id) {
  454. currentColumnCombinations.forEach(function (curr) {
  455. if (curr.itemId === from && !id) {
  456. id = curr.id;
  457. }
  458. });
  459. }
  460. if (details.reason === 'REPLACE_COMPARABLE_MEASURE') {
  461. details.type = 'REPLACE_COMPARABLE_MEASURE';
  462. details.from = IRUtils.getColumnInfo(currentColumnCombinations, visualization, from);
  463. details.to = IRUtils.getColumnInfo(currentColumnCombinations, visualization, to);
  464. }
  465. this._assimilateComparableMeasure(spec, from, to, id, dataItemIdsMap);
  466. },
  467. _assimilateComparableMeasure: function _assimilateComparableMeasure(spec, from, to, id, dataItemIdsMap) {
  468. // - update data items with the new top bottom spec + replace old id with new
  469. var dataItemRef = IRUtils.findDataItem(spec, function (dataItem) {
  470. return dataItem.itemId === from && dataItem.id === id;
  471. });
  472. if (!dataItemRef) {
  473. return;
  474. }
  475. var originalDataItem = spec.data.dataViews[dataItemRef.viewIndex].dataItems[dataItemRef.viewItemIndex];
  476. var oldId = originalDataItem.id;
  477. var newId = UniqueHashIdBuilder.createUniqueHashId(originalDataItem.itemId, dataItemIdsMap);
  478. dataItemIdsMap[newId] = newId;
  479. var toParts = to.split('.');
  480. var itemLabel = toParts[toParts.length - 1];
  481. spec.data.dataViews[dataItemRef.viewIndex].dataItems[dataItemRef.viewItemIndex] = _extends({}, originalDataItem, {
  482. itemId: to,
  483. itemLabel: itemLabel,
  484. id: newId
  485. });
  486. this._replaceSlotId(spec, oldId, newId);
  487. }
  488. });
  489. return SmartsCompareCardRecommenderHelper;
  490. });
  491. //# sourceMappingURL=SmartsCompareCardRecommenderHelper.js.map