BaseSmartsVisRecommenderWrapper.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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(['../../lib/@waca/core-client/js/core-client/ui/core/Class', './SmartsVisRecommenderService', './SmartsToVizMapping', './VisToSmartsMapping', './IRUtils', './SmartsSupportedChartTypes', 'underscore', './SmartsCompareCardRecommenderHelper', '../../apiHelpers/SlotAPIHelper'], function (Class, SmartsVisRecommenderService, SmartsToVizMapping, VisToSmartsMapping, IRUtils, SmartsSupportedChartTypes, _, SmartsCompareCardRecommenderHelper, SlotAPIHelper) {
  14. 'use strict';
  15. var UnsupportedSmartsVis = ['com.ibm.vis.rave2network'];
  16. var BaseSmartsVisRecommenderWrapper = Class.extend({
  17. // constructor
  18. init: function init(_ref) {
  19. var moserDataSources = _ref.moserDataSources,
  20. ajaxSvc = _ref.ajaxSvc,
  21. logger = _ref.logger,
  22. glassOptions = _ref.glassOptions,
  23. dashboardApi = _ref.dashboardApi,
  24. dataSources = _ref.dataSources;
  25. BaseSmartsVisRecommenderWrapper.inherited('init', this, arguments);
  26. this.moserDataSources = moserDataSources;
  27. this._recommenderService = new SmartsVisRecommenderService({
  28. ajaxSvc: ajaxSvc,
  29. logger: logger
  30. });
  31. this.dataSources = dataSources;
  32. this._glassOptions = glassOptions;
  33. this._smartsToVisInfoMap = SmartsToVizMapping;
  34. this._visToSmartsInfoMap = VisToSmartsMapping;
  35. this._supportedVisIds = Object.keys(this._smartsToVisInfoMap);
  36. this._recommenderApi = dashboardApi.getFeature('Recommender.internal');
  37. },
  38. destroy: function destroy() {
  39. BaseSmartsVisRecommenderWrapper.inherited('destroy', this, arguments);
  40. if (this._recommenderService) {
  41. this._recommenderService.destroy();
  42. this._recommenderService = null;
  43. }
  44. this.dataSources = null;
  45. this._glassOptions = null;
  46. this._smartsToVisInfoMap = null;
  47. this._visToSmartsInfoMap = null;
  48. this._supportedVisIds = null;
  49. this._recommenderApi = null;
  50. },
  51. /**
  52. * Returns alternate visualizations recommendation for the given columnIds
  53. * @params - columnIds
  54. * @params - requestOptions
  55. * {
  56. * assetId, (mandatory) storeId of embeddedModule, uploadedFile or module)
  57. * sourceType, (mandatory) 'uploadedFile', 'dataSet2' or 'module'
  58. * oneChartPerType, (optional) true or false, default is true - returns one recommendation per chart type.
  59. * numResult, (optional) number of recommendations, default is 10
  60. * }
  61. * @returns - [recommendations] where recommendation is
  62. * {
  63. * visId: <visId>
  64. * slots : {slotId_1 : [columnIds],
  65. * slotId_2: [columnIds],
  66. * :
  67. * }
  68. * label: <label>
  69. * description: <description>
  70. * title: <title>
  71. * }
  72. */
  73. recommendAlternateVisualizations: function recommendAlternateVisualizations() {
  74. var columnIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  75. var requestOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  76. return this._getRequestParametersForRecommendVisualizations(columnIds, requestOptions, true /*partialTempModule*/).then(this._recommenderService.recommendAlternateVisualizationsOrBindings.bind(this._recommenderService)).then(this._transformRecommendationResponse.bind(this, requestOptions.module.getSourceId())).then(this._stripDuplicates.bind(this));
  77. },
  78. /**
  79. * Returns a list of recommended related visualizations based on given columnIds.
  80. * @params - columnIds
  81. * @params - requestOptions
  82. * {
  83. * assetId, (mandatory) storeId of embeddedModule, uploadedFile or module)
  84. * sourceType, (mandatory) 'uploadedFile', 'dataSet2' or 'module'
  85. * oneChartPerType, (optional) true or false, default is true - returns one recommendation per chart type.
  86. * numResult, (optional) number of recommendations, default is 10
  87. * }
  88. * @returns - [recommendations] where recommendation is
  89. * {
  90. * visId: <visId>
  91. * slots : {slotId_1 : [columnIds],
  92. * slotId_2: [columnIds],
  93. * :
  94. * }
  95. * label: <label>
  96. * description: <description>
  97. * title: <title>
  98. * }
  99. */
  100. recommendRelatedVisualizations: function recommendRelatedVisualizations() {
  101. var columnIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  102. var requestOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  103. return this._getRequestParametersForRecommendVisualizations(columnIds, requestOptions).then(this._recommenderService.recommendRelatedVisualizations.bind(this._recommenderService)).then(this._transformRecommendationResponse.bind(this, requestOptions.module.getSourceId()));
  104. },
  105. recommendCompareVisualizations: function recommendCompareVisualizations() {
  106. var _this = this;
  107. var columnIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  108. var requestOptions = arguments[1];
  109. var helper = new SmartsCompareCardRecommenderHelper(columnIds, requestOptions);
  110. return helper.getRequestParameters().then(function (params) {
  111. return _this._recommenderService.recommendCompareVisualizations(params).then(helper.transformCompareRecommendationResponse.bind(helper, requestOptions.module, requestOptions.widgetSpec));
  112. });
  113. },
  114. _getIncludedCharts: function _getIncludedCharts() {
  115. return SmartsSupportedChartTypes.get(this._glassOptions, this._recommenderApi.getChartTypeList());
  116. },
  117. _getRequestParametersForRecommendVisualizations: function _getRequestParametersForRecommendVisualizations(columnIds, requestOptions, bPartialTempModule) {
  118. var _this2 = this;
  119. var visualization = requestOptions.visualization,
  120. moserDataSources = requestOptions.moserDataSources;
  121. if (visualization && moserDataSources && moserDataSources.isConsumerGroupColumn) {
  122. var sourceId = visualization.getDataSource().getId();
  123. columnIds.forEach(function (columnId, i) {
  124. if (moserDataSources.isConsumerGroupColumn(sourceId, columnId)) {
  125. var customGroup = moserDataSources.getCustomGroupData(sourceId, columnId);
  126. if (customGroup && customGroup.basedOnMoserObjectId) {
  127. columnIds[i] = customGroup.basedOnMoserObjectId;
  128. }
  129. }
  130. });
  131. }
  132. var columns = this._getColumnsInfo(columnIds);
  133. var requestParameters = {
  134. columns: columns
  135. };
  136. if (requestOptions.includeBestVisForTargetFields) {
  137. requestParameters.includeBestVisForTargetFields = true;
  138. }
  139. return this._getIncludedCharts().then(function (supporteChartTypes) {
  140. if (supporteChartTypes) {
  141. requestParameters.includedCharts = supporteChartTypes;
  142. }
  143. return _this2._addCommonRequestParameters(requestParameters, requestOptions, bPartialTempModule);
  144. });
  145. },
  146. /**
  147. * Given a visualization type, recommends the binding for the given columns
  148. * @param visId - visId
  149. * @param columnsToBind - array of columnIds, these are columns that need to be bound
  150. * @param boundColumns - array of bound column info --> [{slotId : <slotId>
  151. * columnIds: <[columnIds]> }
  152. */
  153. recommendBindings: function recommendBindings(_ref2) {
  154. var _this3 = this;
  155. var visId = _ref2.visId,
  156. columnsToBind = _ref2.columnsToBind,
  157. requestOptions = _ref2.requestOptions,
  158. boundColumnsInfo = _ref2.boundColumnsInfo,
  159. originalVisId = _ref2.originalVisId;
  160. var columns = [];
  161. if (columnsToBind) {
  162. columns = columns.concat(this._getColumnsInfo(columnsToBind));
  163. }
  164. if (boundColumnsInfo) {
  165. /**
  166. * presence of originalVisId indicates that this is a change viz request, therefore,
  167. * include the binding info for the original viz
  168. */
  169. var mapInfo = this._visToSmartsInfoMap[originalVisId] || this._visToSmartsInfoMap[visId];
  170. if (!mapInfo) {
  171. return Promise.reject(new Error('Invalid originalVisId or visId'));
  172. }
  173. columns = columns.concat(this._getColumnsInfoForBoundColumns(boundColumnsInfo, mapInfo));
  174. }
  175. var requestParameters = {
  176. columns: columns,
  177. chartType: this._visToSmartsInfoMap[visId].chartType,
  178. allowBindingsWithWarnings: true
  179. };
  180. if (originalVisId && this._visToSmartsInfoMap[originalVisId]) {
  181. requestParameters.originalChartType = this._visToSmartsInfoMap[originalVisId].chartType;
  182. }
  183. return this._addCommonRequestParameters(requestParameters, requestOptions, true /*bPartialTempModule*/).then(function () {
  184. return _this3._recommenderService.recommendAlternateVisualizationsOrBindings(requestParameters).then(_this3._transformBindingRecommendationResponse.bind(_this3, visId, requestOptions.module));
  185. });
  186. },
  187. /**
  188. * Get recommendation info from the chartInfo endpoint of smarts server
  189. * @param {object} options
  190. * @param {array} options.columns - list of columns and which slots they're mapped to
  191. * @param {string} options.visId - id of vis type
  192. * @param {object} options.requestOptions - other request-specific options
  193. * @return {object} payload from smarts server (including `label` attribute)
  194. */
  195. getRecommendInfo: function getRecommendInfo(_ref3) {
  196. var _this4 = this;
  197. var columns = _ref3.columns,
  198. visId = _ref3.visId,
  199. requestOptions = _ref3.requestOptions;
  200. var mapInfo = this._visToSmartsInfoMap[visId];
  201. if (!mapInfo) {
  202. return Promise.reject(new Error(this.getUnsupportedVizErrCode()));
  203. }
  204. columns = this._sanitizeColumnInfo(columns);
  205. var requestParameters = {
  206. columns: this._getColumnsInfoForBoundColumns(columns, mapInfo),
  207. chartType: this._visToSmartsInfoMap[visId].chartType
  208. };
  209. return this._addCommonRequestParameters(requestParameters, requestOptions, true /*bPartialTempModule*/).then(function () {
  210. return _this4._recommenderService.getChartInfo(requestParameters);
  211. });
  212. },
  213. getUnsupportedVizErrCode: function getUnsupportedVizErrCode() {
  214. return 'ERR_CODE_UNSUPPORTED_VIS';
  215. },
  216. _isSupportedViz: function _isSupportedViz(recommendation) {
  217. return this._supportedVisIds.indexOf(recommendation.chartType) !== -1;
  218. },
  219. _addCommonRequestParameters: function _addCommonRequestParameters(requestParameters, _ref4, bPartialTempModule) {
  220. var assetId = _ref4.assetId,
  221. numResults = _ref4.numResults,
  222. sourceType = _ref4.sourceType,
  223. oneChartPerType = _ref4.oneChartPerType,
  224. module = _ref4.module,
  225. filters = _ref4.filters;
  226. var columnIds = requestParameters.columns.map(function (column) {
  227. return column.id;
  228. }).reduce(function (acc, columnIds) {
  229. return acc.concat(columnIds);
  230. }, []);
  231. var tempModuleJSON = bPartialTempModule ? module.copyPartialMoserModuleJSON(columnIds) : module.getTemporarySessionModuleJSON();
  232. var commonRequestParameters = {
  233. sourceModuleId: assetId,
  234. oneChartPerType: oneChartPerType || true,
  235. numResults: numResults || 10, //upper limit of recommendatations returned. 10 is arbitrary.
  236. sourceType: sourceType,
  237. shapedModule: !!module.getShapingId() //module has shaping
  238. };
  239. if (tempModuleJSON) {
  240. commonRequestParameters.tempModule = JSON.stringify(tempModuleJSON);
  241. }
  242. if (filters) {
  243. commonRequestParameters.filters = filters;
  244. }
  245. return module.saveTemporaryModule().then(function (shapingModuleId) {
  246. if (assetId !== shapingModuleId) {
  247. commonRequestParameters.tempModuleId = shapingModuleId;
  248. }
  249. return _.extend(requestParameters, commonRequestParameters);
  250. });
  251. },
  252. _transformRecommendationResponse: function _transformRecommendationResponse(dataSourceId, recommendations) {
  253. return recommendations.filter(this._isSupportedViz.bind(this)).filter(this._isSupportedBinding.bind(this, dataSourceId)).filter(this._hasSupportedFilter.bind(this)).map(this._transformRecommendation.bind(this));
  254. },
  255. //remove the duplicate recommendations but keep the first occurrence and preserve the order
  256. _stripDuplicates: function _stripDuplicates(recommendations) {
  257. var result = [];
  258. var map = new Map();
  259. for (var _iterator = recommendations, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
  260. var _ref5;
  261. if (_isArray) {
  262. if (_i >= _iterator.length) break;
  263. _ref5 = _iterator[_i++];
  264. } else {
  265. _i = _iterator.next();
  266. if (_i.done) break;
  267. _ref5 = _i.value;
  268. }
  269. var recommendation = _ref5;
  270. if (!map.has(recommendation.visId)) {
  271. map.set(recommendation.visId, true);
  272. result.push(recommendation);
  273. }
  274. }
  275. return result;
  276. },
  277. /**
  278. * This method should be overriden as needed to provide the proper transformation
  279. * @ params {recommendation}
  280. * @ returns
  281. * {
  282. * visId: <visId>
  283. * slots : {slotId_1 : [columnIds],
  284. * slotId_2: [columnIds],
  285. * :
  286. * }
  287. * label: <label>
  288. * description: <description>
  289. * title: <title>
  290. * }
  291. */
  292. _transformRecommendation: function _transformRecommendation(recommendation) {
  293. var chartType = recommendation.chartType,
  294. label = recommendation.label,
  295. description = recommendation.description,
  296. title = recommendation.title,
  297. columns = recommendation.columns,
  298. filters = recommendation.filters,
  299. autoGroupingMap = recommendation.autoGroupingMap;
  300. var smartsToVisMap = this._smartsToVisInfoMap[chartType];
  301. return {
  302. label: label,
  303. description: description,
  304. title: title,
  305. visId: smartsToVisMap.visId,
  306. topBottom: this._transformTopBottomFromSmartsFilters(filters),
  307. filters: this._transformFiltersFromSmartsFilters(filters),
  308. autoGroup: this._transformAutoGroupFromSmartsAutoGroupingMap(autoGroupingMap),
  309. slots: this._transformFromSmartsColumnInfo(columns, smartsToVisMap)
  310. };
  311. },
  312. _getColumnsInfo: function _getColumnsInfo(columnIds) {
  313. return columnIds.map(function (columnId) {
  314. return { id: [columnId] };
  315. });
  316. },
  317. _sanitizeColumnInfo: function _sanitizeColumnInfo(boundColumnsInfo) {
  318. return boundColumnsInfo.map(function (info) {
  319. return {
  320. slotId: info.slotId,
  321. columnIds: _.without(info.columnIds, SlotAPIHelper.MULTI_MEASURES_SERIES)
  322. };
  323. }).filter(function (info) {
  324. return info.columnIds.length > 0;
  325. });
  326. },
  327. /** Massage the given boundColumnsInfo into one that Smarts understand
  328. * Transform
  329. * [{
  330. * slotId: <slotId>,
  331. * columnIds: [<column ids>]
  332. * }, {..}]
  333. * to
  334. * [{
  335. * slot: <slot name>
  336. * id: [<column ids>]
  337. * }, {..}]
  338. *
  339. */
  340. _getColumnsInfoForBoundColumns: function _getColumnsInfoForBoundColumns(boundColumnsInfo, mapInfo) {
  341. return boundColumnsInfo.map(function (columnInfo) {
  342. return {
  343. slot: mapInfo[columnInfo.slotId],
  344. id: columnInfo.columnIds
  345. };
  346. });
  347. },
  348. _transformAutoGroupFromSmartsAutoGroupingMap: function _transformAutoGroupFromSmartsAutoGroupingMap() {
  349. var autoGroupingMap = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  350. return Object.keys(autoGroupingMap).map(function (itemId) {
  351. var value = autoGroupingMap[itemId];
  352. return _extends({
  353. itemId: itemId
  354. }, value);
  355. });
  356. },
  357. _transformTopBottomFromSmartsFilters: function _transformTopBottomFromSmartsFilters() {
  358. var filters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  359. return filters.filter(function (filter) {
  360. return filter.type === 'TOP_BOTTOM';
  361. }).map(function (filter) {
  362. return {
  363. itemId: filter.columnId,
  364. spec: {
  365. type: IRUtils.IRToTopBottomType(filter.topBottomType, filter.method),
  366. value: filter.domainSize,
  367. context: filter.byColumn,
  368. aggregate: 'sum' // TODO
  369. }
  370. };
  371. });
  372. },
  373. _transformFiltersFromSmartsFilters: function _transformFiltersFromSmartsFilters() {
  374. var filters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  375. return filters.filter(function (filter) {
  376. return filter.type !== 'TOP_BOTTOM';
  377. }).map(function (filter) {
  378. return IRUtils.IRFilters2LocalFilter(filter);
  379. });
  380. },
  381. _transformFromSmartsColumnInfo: function _transformFromSmartsColumnInfo(columns, smartsToVisMap) {
  382. var slotToColumnIdsMap = {};
  383. columns.forEach(function (column) {
  384. var slotId = smartsToVisMap[column.slot];
  385. var columnIds = slotToColumnIdsMap[slotId] || [];
  386. slotToColumnIdsMap[slotId] = columnIds.concat(column.id);
  387. });
  388. return slotToColumnIdsMap;
  389. },
  390. /*
  391. * When a visualization is not supported by Smarts vis recommender, it is map to one that is supported
  392. * that has the same number and type of slots. This is only for recommend binding request.
  393. */
  394. _getSmartsToVisMappingForUnsupportedVisualization: function _getSmartsToVisMappingForUnsupportedVisualization(visId) {
  395. var smartsToVisMap = {};
  396. var oldMapInfo = this._visToSmartsInfoMap[visId];
  397. var chartProps = Object.keys(oldMapInfo);
  398. chartProps.forEach(function (prop) {
  399. if (prop === 'chartType') {
  400. smartsToVisMap.visId = visId;
  401. } else {
  402. smartsToVisMap[oldMapInfo[prop]] = prop;
  403. }
  404. });
  405. return smartsToVisMap;
  406. },
  407. _transformBindingRecommendationResponse: function _transformBindingRecommendationResponse(visId, module, bindingRecommendations) {
  408. var _this5 = this;
  409. var smartsToVisMap = void 0;
  410. if (UnsupportedSmartsVis.indexOf(visId) > -1) {
  411. smartsToVisMap = this._getSmartsToVisMappingForUnsupportedVisualization(visId);
  412. }
  413. return bindingRecommendations.map(function (bindingRec) {
  414. var chartType = bindingRec.chartType,
  415. columns = bindingRec.columns,
  416. unbound = bindingRec.unbound;
  417. smartsToVisMap = !smartsToVisMap ? _this5._smartsToVisInfoMap[chartType] : smartsToVisMap;
  418. return {
  419. visId: visId || smartsToVisMap.visId,
  420. slots: _this5._transformFromSmartsColumnInfo(columns, smartsToVisMap),
  421. unbound: unbound
  422. };
  423. });
  424. },
  425. /**
  426. * Returns true if the slot binding recommendation is supported for the
  427. * specified chart type
  428. */
  429. _isSupportedBinding: function _isSupportedBinding(dataSourceId, _ref6) {
  430. var chartType = _ref6.chartType,
  431. columns = _ref6.columns;
  432. switch (chartType.toUpperCase()) {
  433. case 'SPIRAL':
  434. return this._isSupportedSpiral(dataSourceId, columns);
  435. default:
  436. return true;
  437. }
  438. },
  439. _hasSupportedFilter: function _hasSupportedFilter(recommendation) {
  440. if (recommendation) {
  441. var filters = recommendation.filters;
  442. if (filters && filters.length > 0) {
  443. var isProjected = function isProjected(columnId) {
  444. var columns = recommendation.columns || [];
  445. var isOnASlot = columns.some(function (column) {
  446. if (column.id.indexOf(columnId) !== -1) {
  447. return true;
  448. }
  449. return false;
  450. });
  451. return isOnASlot;
  452. };
  453. var isIncompatible = filters.some(function (filter) {
  454. if (filter.type === 'TOP_BOTTOM') {
  455. if (!isProjected(filter.columnId)) {
  456. return true;
  457. } else {
  458. return false;
  459. }
  460. }
  461. return false;
  462. });
  463. return !isIncompatible;
  464. }
  465. }
  466. return true;
  467. },
  468. /**
  469. * Returns false if Driver analysis viz has attribute type bound to TARGET slot
  470. * @param module
  471. * @param columns
  472. * @returns {boolean}
  473. * @private
  474. */
  475. _isSupportedSpiral: function _isSupportedSpiral(dataSourceId, columns) {
  476. var dataSource = this.dataSources.getDataSource(dataSourceId);
  477. var containsOlapItem = columns.some(function (column) {
  478. var mdColumn = dataSource.getMetadataColumn(column.id);
  479. return mdColumn && mdColumn.isOlapColumn();
  480. });
  481. var module = this.moserDataSources.getModule(dataSourceId);
  482. // Spiral/Comet/Sunburst/Decision Tree charts are not supported for OLAP data
  483. return !(containsOlapItem || module.isOlapPackage());
  484. }
  485. });
  486. return BaseSmartsVisRecommenderWrapper;
  487. });
  488. //# sourceMappingURL=BaseSmartsVisRecommenderWrapper.js.map