VisPossibleKeyDriversManager.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Watson Analytics (C) Copyright IBM Corp. 2018, 2019
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. * @module dashboard-analytics/widgets/livewidget/visapi/support/VisPossibleKeyDriversManager
  7. */
  8. define(['dashboard-analytics/lib/@waca/core-client/js/core-client/ui/core/Class', 'underscore', '../../query/CommonQueryBuilder', '../../../../widgets/livewidget/nls/StringResources', '../../../../widgets/livewidget/query/QueryFilterSpec'], function (Class, _, CommonQueryBuilder, stringResources, QueryFilterSpec) {
  9. 'use strict';
  10. /*
  11. * We want to limit possible key drivers to 250 so we don't overwhelm predict
  12. * or dss. More changes to come for this.
  13. */
  14. var MAX_POSSIBLE_KEY_DRIVERS = 250;
  15. /**
  16. * Possible key drivers manager is an object which manages the interaction between
  17. * other components and the possible key driver service and model.
  18. * To make use of the response data from the service a calling
  19. * component must first ensure that the information has been queried. To do
  20. * this they must wait for the promise from whenPossibleKeyDriversAreReady to be
  21. * resolved.
  22. */
  23. var VisPossibleKeyDriversManager = Class.extend({
  24. // FPD Messages to add to the info indicator
  25. FPD_RESPONSE_WARNINGS: 'fpd_response_warnings',
  26. FPD_RESPONSE_ERRORS: 'fpd_response_errors',
  27. init: function init(options) {
  28. VisPossibleKeyDriversManager.inherited('init', this, arguments);
  29. this.visAPI = options.visAPI;
  30. this.logger = options.logger;
  31. this.ownerWidget = options.ownerWidget;
  32. this.content = this.ownerWidget.content;
  33. this.smartsEnabled = options.smartsEnabled;
  34. this.possibleDriversResponse = {};
  35. },
  36. isNewEditDriversEnabled: function isNewEditDriversEnabled() {
  37. if (this.ownerWidget.dashboardApi) {
  38. // Access featureChecker to check feature flag for new edit drivers dialog
  39. var featureChecker = this.ownerWidget.dashboardApi && this.ownerWidget.dashboardApi.getGlassCoreSvc('.FeatureChecker');
  40. return !(featureChecker && featureChecker.checkValue('explore', 'newEditDriversDialog', 'false'));
  41. }
  42. return false;
  43. },
  44. /***************************************************************************
  45. * Public methods *
  46. ***************************************************************************/
  47. /***************************************************************************
  48. * Render Sequence methods *
  49. ***************************************************************************/
  50. _clearWarningsAndErrorsFromIndicator: function _clearWarningsAndErrorsFromIndicator() {
  51. var visView = this.ownerWidget.getCurrentVis();
  52. visView && visView.infoIndicator && visView.infoIndicator.clearMessagesWithIds([this.FPD_RESPONSE_WARNINGS, this.FPD_RESPONSE_ERRORS]);
  53. },
  54. /**
  55. * Called during the KeyDriverTask of the render sequence.
  56. * @returns {Promise} which is resolved when we have a valid possible key drivers response
  57. * The response will be of the format:
  58. * {
  59. * recommendedDrivers: {Array[fieldIDForExpression, label, selected]}
  60. * nonRecommendedDrivers: {Array[fieldIDForExpression, label, selected]}
  61. */
  62. whenPossibleKeyDriversAreReady: function whenPossibleKeyDriversAreReady(renderContext, targetId) {
  63. var _this = this;
  64. var useModelData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
  65. // Only get the possible drivers if mappings is complete.
  66. // TODO: Remove smartsEnabled once smarts is up and running stably.
  67. this._clearWarningsAndErrorsFromIndicator();
  68. var visualization = this.content.getFeature('Visualization');
  69. if (this.smartsEnabled && visualization.getSlots().isMappingComplete()) {
  70. /*
  71. * Possible drivers are cached per target ID so if users change the
  72. * target data item we do not always have to refetch them. We also want to
  73. * retry if we are currently in an error state.
  74. */
  75. var inErrorState = this.errorState && this.errorState.inErrorState;
  76. if (!inErrorState && this._useCache(targetId)) {
  77. this._addWarningsAndErrorsToInfoIndicator(this.possibleDriversResponse[targetId].messages);
  78. return Promise.resolve(this.possibleDriversResponse[targetId]);
  79. } else {
  80. // So its not cached, lets check the model.
  81. var modelPossibleKeyDrivers = this.visAPI.getPossibleKeyDrivers();
  82. if (!inErrorState && useModelData && modelPossibleKeyDrivers && modelPossibleKeyDrivers[targetId]) {
  83. // Its in the model so lets use it.
  84. var desc = modelPossibleKeyDrivers[targetId];
  85. var messages = desc.messages;
  86. this._cacheKeyDrivers(targetId, desc);
  87. this._addWarningsAndErrorsToInfoIndicator(messages);
  88. return Promise.resolve(this.possibleDriversResponse[targetId]);
  89. } else {
  90. // Reset the error state to start fresh
  91. this.errorState = {};
  92. // Alright, not in the cache and not in the model. Lets query for it.
  93. return this._queryForPossibleKeyDriversForTargetId(targetId).then(function (driverDesc) {
  94. var recommendedDrivers = driverDesc.possibleKeyDrivers;
  95. var messages = driverDesc.messages;
  96. var driversToUse = recommendedDrivers.length > MAX_POSSIBLE_KEY_DRIVERS ? recommendedDrivers.splice(0, MAX_POSSIBLE_KEY_DRIVERS) : recommendedDrivers;
  97. var optionsForSave = {
  98. messages: messages,
  99. targetId: targetId,
  100. recommended: driversToUse,
  101. isSilent: true, // Don't want to triger events on initial save.
  102. saveInModel: useModelData // save result in model only if we need to use model data
  103. };
  104. _this._addWarningsAndErrorsToInfoIndicator(messages);
  105. var formattedDriverDesc = _this._getFormattedDriversDescription(optionsForSave);
  106. _this._saveKeyDriversInModelAndCache(formattedDriverDesc, optionsForSave);
  107. return Promise.resolve(_this.possibleDriversResponse[targetId]);
  108. });
  109. }
  110. }
  111. } else {
  112. return Promise.resolve({});
  113. }
  114. },
  115. _useCache: function _useCache(targetId) {
  116. var response = this.possibleDriversResponse && this.possibleDriversResponse[targetId];
  117. var keyDrivers = response && !response.cacheInvalidated && response.recommendedDrivers;
  118. return keyDrivers && keyDrivers.length > 0;
  119. },
  120. _cacheKeyDrivers: function _cacheKeyDrivers(targetId, possibleDrivers) {
  121. this.possibleDriversResponse[targetId] = JSON.parse(JSON.stringify(possibleDrivers));
  122. },
  123. /*
  124. * Invalidate the key drivers cache
  125. * Calls to whenPossibleKeyDriversAreReady() will skip the cache when invalidated and rely on either model or query
  126. *
  127. * @param {string} targetId - targetId of the mapped column
  128. */
  129. invalidateCache: function invalidateCache(targetId) {
  130. if (this.possibleDriversResponse[targetId]) {
  131. this.possibleDriversResponse[targetId].cacheInvalidated = true;
  132. }
  133. },
  134. /***************************************************************************
  135. * Data Request methods *
  136. ***************************************************************************/
  137. /**
  138. * Execute a predict fast pattern detection request
  139. *
  140. * @param {string} targetId - targetId of the mapped column
  141. * @param {string[]} possibleKeyDrivers - list of possible key drivers
  142. * @param {string[]} methods - list of methods inquiring for the FPD request
  143. * @param {boolean} isPreview - indicator of whether the owner widget is a preview widget and do not need prompting for FPD request
  144. */
  145. executeFastPatternDetectionRequest: function executeFastPatternDetectionRequest(targetId, possibleKeyDrivers, methods, promptControlFunctions) {
  146. var isPreview = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
  147. var module = this.visAPI.getModule();
  148. var options = {
  149. sourceIdOrModule: module,
  150. querySpec: {
  151. version: '1.1',
  152. targetId: targetId,
  153. inputs: possibleKeyDrivers,
  154. filters: this._getPredictFilters(),
  155. dataFilters: this._getDataFilters(),
  156. methods: methods || ['drivers']
  157. },
  158. promptControlFunctions: promptControlFunctions,
  159. isPreview: isPreview
  160. };
  161. return this.visAPI.getQueryManager().queryService.runPredictQuery(options);
  162. },
  163. /**
  164. * @param {String} id of the mapped column.
  165. * @returns {Object} possibleKeyDrivers - {Array[ids]}, {Object} errorState
  166. */
  167. getEnabledPossibleKeyDrivers: function getEnabledPossibleKeyDrivers(targetId) {
  168. /**
  169. * We need to find all selected possible key drivers. Basically all recommended
  170. * or non recommended drivers that are selected.
  171. */
  172. var results = [];
  173. // TODO: Remove case where smarts isn't enabled when smarts is up and running stably.
  174. if (this.smartsEnabled) {
  175. if (this.possibleDriversResponse[targetId]) {
  176. var selectedRecommendedDrivers = _.filter(this.possibleDriversResponse[targetId].recommendedDrivers, function (item) {
  177. return item.selected;
  178. });
  179. var selectedNonRecommendedDrivers = _.filter(this.possibleDriversResponse[targetId].nonRecommendedDrivers, function (item) {
  180. return item.selected;
  181. });
  182. var totalSelected = selectedRecommendedDrivers.concat(selectedNonRecommendedDrivers);
  183. // We only need the ids of the selected possible key drivers.
  184. results = _.pluck(totalSelected, 'fieldIDForExpression');
  185. }
  186. return {
  187. possibleKeyDrivers: results,
  188. errorState: this.errorState
  189. };
  190. } else {
  191. var module = this.visAPI.getModule();
  192. // Get the selected inputs.
  193. module.getMetadataColumns(function (col) {
  194. var columnId = col.getId();
  195. var columnType = col.getType();
  196. if (columnType === 'fact' || columnType === 'attribute') {
  197. // Ensure the selected column ids are still valid.
  198. if (columnId !== targetId) {
  199. results.push(columnId);
  200. }
  201. }
  202. });
  203. return {
  204. possibleKeyDrivers: results,
  205. errorState: this.errorState
  206. };
  207. }
  208. },
  209. /***************************************************************************
  210. * Dialog methods *
  211. ***************************************************************************/
  212. /**
  213. * Return the possible drivers in a state that is easily displayable.
  214. * This is called when the dialog is created/refreshed.
  215. * @param {String} id of the mapped column.
  216. * @returns {Array} of objects with a form similar to:
  217. * {
  218. * id:{string}
  219. * label:{string}
  220. * selected: {boolean}
  221. * }
  222. */
  223. getPossibleKeyDriversDisplayState: function getPossibleKeyDriversDisplayState(itemId) {
  224. var desc = this.possibleDriversResponse[itemId];
  225. return this._getFormattedDisplayStateFromDriverDescription(itemId, desc);
  226. },
  227. /**
  228. * Update the model with the new possible key driver state. Called when a user
  229. * has modified the possible key driver states through the dialog.
  230. * @param {String} id of the mapped column.
  231. * @param {Array} of key drivers indicating their selection state.
  232. * {
  233. * selected: {Boolean} true iff the key driver is currently selected (i.e. on)
  234. * label: {String} localized label used to display the key driver in a dialog
  235. * id: {String} ID used to store the key driver when selected in the spec.
  236. * }
  237. */
  238. possibleKeyDriverSelectionStateChanged: function possibleKeyDriverSelectionStateChanged(targetId, keyDriversDisplayStates) {
  239. // Remove the label property as its existence will affect the comparisons we will make
  240. this._removeColumnLabelsFromDisplayStates(keyDriversDisplayStates);
  241. var modelKeyDrivers = this.visAPI.getPossibleKeyDrivers();
  242. var possibleDriverState = modelKeyDrivers[targetId] || this.possibleDriversResponse[targetId];
  243. // Only update the model (cause a re-render) if the selection state has changed.
  244. if (this._haveSelectedKeyDriversChanged(keyDriversDisplayStates.recommendedDrivers, possibleDriverState.recommendedDrivers) || this._haveSelectedKeyDriversChanged(keyDriversDisplayStates.nonRecommendedDrivers, possibleDriverState.nonRecommendedDrivers)) {
  245. possibleDriverState.recommendedDrivers = keyDriversDisplayStates.recommendedDrivers;
  246. possibleDriverState.nonRecommendedDrivers = keyDriversDisplayStates.nonRecommendedDrivers;
  247. modelKeyDrivers = {};
  248. modelKeyDrivers[targetId] = possibleDriverState;
  249. this.visAPI.setPossibleKeyDrivers(modelKeyDrivers);
  250. this.possibleDriversResponse[targetId].recommendedDrivers = keyDriversDisplayStates.recommendedDrivers;
  251. this.possibleDriversResponse[targetId].nonRecommendedDrivers = keyDriversDisplayStates.nonRecommendedDrivers;
  252. }
  253. },
  254. /**
  255. * Query the Smarts service for new possible key drivers. This is called from
  256. * the dialog when a user decides to 'Reset to default'.
  257. * Note, this does not change the current key drivers in the model.
  258. * @param {String} id of the mapped column.
  259. * @returns {Promise} resolved with possible drivers formatted in a way easily displayed.
  260. */
  261. getNewPossibleKeyDriversForTargetId: function getNewPossibleKeyDriversForTargetId(targetId) {
  262. var _this2 = this;
  263. return this._queryForPossibleKeyDriversForTargetId(targetId).then(function (driverDesc) {
  264. var recommendedDrivers = driverDesc.possibleKeyDrivers;
  265. var messages = driverDesc.messages;
  266. var options = {
  267. messages: messages,
  268. targetId: targetId,
  269. recommended: recommendedDrivers,
  270. isSilent: true
  271. };
  272. // We have the drivers, now format the response and return it in a displayable state.
  273. var formattedDriverDesc = _this2._getFormattedDriversDescription(options);
  274. return _this2._getFormattedDisplayStateFromDriverDescription(targetId, formattedDriverDesc);
  275. });
  276. },
  277. /***************************************************************************
  278. * Private methods *
  279. ***************************************************************************/
  280. /**
  281. * @param {Object} formattedDriverDesc - recommended and nonRecommended drivers.
  282. * @param {Object} options - targetId, recommendedDrivers array, isSilent for model save, saveInModel for the case of comet viz.
  283. */
  284. _saveKeyDriversInModelAndCache: function _saveKeyDriversInModelAndCache(formattedDriverDesc, options) {
  285. this._cacheKeyDrivers(options.targetId, formattedDriverDesc);
  286. if (options.saveInModel) {
  287. var currentModelDrivers = {};
  288. currentModelDrivers[options.targetId] = formattedDriverDesc;
  289. this.visAPI.setPossibleKeyDrivers(currentModelDrivers, options.isSilent ? { silent: true } : {});
  290. }
  291. },
  292. /**
  293. * @returns {boolean} true iff the new selected key driver ids does not match the current state
  294. */
  295. _haveSelectedKeyDriversChanged: function _haveSelectedKeyDriversChanged(newKeyDriverSelections) {
  296. var _this3 = this;
  297. var currentSelections = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  298. var hasChanged = null;
  299. // If lengths don't match then things have changed.
  300. if (newKeyDriverSelections.length !== currentSelections.length) {
  301. hasChanged = true;
  302. }
  303. // If both lengths are 0, nothing has changed.
  304. if (newKeyDriverSelections.length === 0 && currentSelections.length === 0) {
  305. hasChanged = false;
  306. }
  307. // Lengths are the same but maybe the contents do not match?
  308. if (hasChanged === null) {
  309. hasChanged = _.find(currentSelections, function (currentItem) {
  310. var newSelect = null;
  311. newSelect = _.find(newKeyDriverSelections, function (newSelect) {
  312. if (_this3.isNewEditDriversEnabled()) {
  313. return newSelect.fieldIDForExpression === currentItem.fieldIDForExpression || newSelect.selected === currentItem.selected;
  314. }
  315. return newSelect.fieldIDForExpression === currentItem.fieldIDForExpression;
  316. });
  317. var isEqual = newSelect && _.isEqual(newSelect, currentItem);
  318. return !isEqual;
  319. });
  320. }
  321. return hasChanged ? true : false;
  322. },
  323. _getMessagesForInfoIndicator: function _getMessagesForInfoIndicator(msgTypeId, msgTypeLabel, arrayOfMessages) {
  324. var items = [];
  325. arrayOfMessages.forEach(function (fpdMsg) {
  326. items.push({
  327. id: fpdMsg.id,
  328. label: fpdMsg.caption
  329. });
  330. });
  331. return {
  332. id: msgTypeId,
  333. label: msgTypeLabel,
  334. items: items
  335. };
  336. },
  337. _addWarningsAndErrorsToInfoIndicator: function _addWarningsAndErrorsToInfoIndicator(messages) {
  338. if (!messages || !messages.fpdWarnings && !messages.fpdErrors) {
  339. return;
  340. }
  341. var warnings = messages.fpdWarnings;
  342. var errors = messages.fpdErrors;
  343. var visView = this.ownerWidget.getCurrentVis();
  344. var usages = this.visAPI.getDefinition().usages || [];
  345. var isAdvancedVisualization = usages.indexOf('Advanced') !== -1;
  346. if (visView && visView.infoIndicator && isAdvancedVisualization) {
  347. var infosForIndicator = [];
  348. infosForIndicator.push(this._getMessagesForInfoIndicator(this.FPD_RESPONSE_ERRORS, stringResources.get('fpd_response_errors'), errors));
  349. infosForIndicator.push(this._getMessagesForInfoIndicator(this.FPD_RESPONSE_WARNINGS, stringResources.get('fpd_response_warnings'), warnings));
  350. visView.infoIndicator.addInfo(infosForIndicator);
  351. }
  352. },
  353. /**
  354. * Query the smarts service for the recommended drivers.
  355. * @param {String} targetId - id on the mapped column
  356. * @param {Array} of drivers, all in selected state.
  357. */
  358. _queryForPossibleKeyDriversForTargetId: function _queryForPossibleKeyDriversForTargetId(targetId) {
  359. var _this4 = this;
  360. var module = this.visAPI.getModule();
  361. return this.visAPI.whenPossibleKeyDriversQueryIsReady(targetId, module.getAssetId()).then(function (queryResponse) {
  362. var possibleKeyDrivers = [];
  363. if (queryResponse.result === 'SUCCESS') {
  364. // Clear the error state and set the possible key drivers
  365. _this4.errorState = {};
  366. possibleKeyDrivers = queryResponse.possibleDrivers;
  367. } else {
  368. // Error, lets set the error state, log it
  369. _this4.errorState = {
  370. inErrorState: true,
  371. error: queryResponse.error
  372. };
  373. _this4.logger.error(queryResponse.error);
  374. }
  375. if (possibleKeyDrivers) {
  376. // Currently smarts only tells us the selected ones
  377. possibleKeyDrivers.forEach(function (driver) {
  378. driver.selected = true;
  379. });
  380. }
  381. return {
  382. possibleKeyDrivers: possibleKeyDrivers,
  383. messages: {
  384. fpdWarnings: queryResponse.fpdWarnings,
  385. fpdErrors: queryResponse.fpdErrors
  386. }
  387. };
  388. });
  389. },
  390. /**
  391. * @param {Object} options possibly containing recommended and nonrecommended drivers.
  392. * @returns {Object} containing recommended and nonrecommended drivers.
  393. */
  394. _getFormattedDriversDescription: function _getFormattedDriversDescription(options) {
  395. // Default to empty if they are not specified.
  396. return {
  397. recommendedDrivers: options.recommended || [],
  398. nonRecommendedDrivers: options.nonRecommended || [],
  399. messages: options.messages || {}
  400. };
  401. },
  402. /**
  403. * @returns if the specified column is found in the selected Ids array.
  404. */
  405. _isColSelected: function _isColSelected(colId, selectedIds) {
  406. return selectedIds.indexOf(colId) >= 0;
  407. },
  408. /**
  409. * Get the predict topbottom filter spec
  410. * @return predict topbottom filter spec
  411. */
  412. _getPredictFilters: function _getPredictFilters() {
  413. var filters = [];
  414. // spiral has only one slot with one data item
  415. var slots = this.content.getFeature('Visualization').getSlots().getSlotList();
  416. var dataItemList = slots[0].getDataItemList();
  417. // obtain the topcount from the data item
  418. var top = dataItemList && dataItemList.length ? dataItemList[0].getTopBottom() : null;
  419. if (top && top.type === 'topcount') {
  420. filters.push({
  421. topcount: top.value
  422. });
  423. }
  424. return filters;
  425. },
  426. /**
  427. * Get the predict sampling data filter spec
  428. * @return filter spec for sampling data
  429. */
  430. _getDataFilters: function _getDataFilters() {
  431. // apply the page context filters
  432. var pageContext = this.visAPI.getPageContextAPI();
  433. var contextItems = pageContext.getNetPageContextItems({ scope: this.visAPI.getScope(),
  434. sourceId: this.visAPI.getModule().getSourceId(),
  435. eventGroupId: this.visAPI.getEventGroupId() });
  436. var filters = CommonQueryBuilder.buildFilterFromPageContext(_.map(contextItems, function (item) {
  437. return item.getPageContextSpecItem();
  438. }));
  439. // apply local filters
  440. var localFiltersSpecs = this._getLocalFiltersSpecs();
  441. _.each(localFiltersSpecs, function (localFilterSpec) {
  442. filters.push(localFilterSpec);
  443. });
  444. return filters;
  445. },
  446. _getLocalFiltersSpecs: function _getLocalFiltersSpecs() {
  447. var localFiltersDef = this.visAPI.getLocalFilters();
  448. if (localFiltersDef) {
  449. var queryFilterSpec = new QueryFilterSpec();
  450. queryFilterSpec.addFiltersToSpec(localFiltersDef);
  451. if (queryFilterSpec.hasFilterSpec()) {
  452. return queryFilterSpec.getFilterSpec();
  453. }
  454. }
  455. return null;
  456. },
  457. /**
  458. * Find the non recommended possible key drivers (basically, all those in the
  459. * data set that are not the target and are not recommended).
  460. * @param {String} target id of interest.
  461. * @returns {Array} of key drivers in the form of:
  462. * @returns {Object}
  463. * @returns {fieldIDForExpression} id of the column
  464. * @returns {label} of the column
  465. */
  466. _getUnrecommendedColumns: function _getUnrecommendedColumns(itemId) {
  467. var _this5 = this;
  468. var module = this.visAPI.getModule();
  469. var inputs = [];
  470. // Go through all the fact or attribute columns for this module.
  471. module.getMetadataColumns(function (col) {
  472. if (_this5._isMetadataColumnNonRecommended(itemId, col)) {
  473. inputs.push({
  474. fieldIDForExpression: col.getId(),
  475. label: col.getLabel()
  476. });
  477. }
  478. });
  479. return inputs;
  480. },
  481. /**
  482. * @param {String} targetId of the mapped column
  483. * @param {Object} col metadata column
  484. * @returns {Boolean} true iff the column is to be considered in the non recommended list.
  485. */
  486. _isMetadataColumnNonRecommended: function _isMetadataColumnNonRecommended(targetId, col) {
  487. var isNonRecommended = false;
  488. var columnType = col.getType();
  489. if ((columnType === 'fact' || columnType === 'attribute') && !col.isHidden()) {
  490. var columnId = col.getId();
  491. // Check if its part of the recommended list.
  492. var partOfRecommended = _.find(this.possibleDriversResponse[targetId].recommendedDrivers, function (recommendedKeyDriver) {
  493. return recommendedKeyDriver.fieldIDForExpression === columnId;
  494. });
  495. if (!partOfRecommended && columnId !== targetId) {
  496. isNonRecommended = true;
  497. }
  498. }
  499. return isNonRecommended;
  500. },
  501. /**
  502. * The recommended drivers returned by the smarts service will not have the
  503. * label needed to display the drivers. We need to look it up.
  504. */
  505. _addColumnLabelsToTheDriverDesc: function _addColumnLabelsToTheDriverDesc(formattedDriverDesc) {
  506. var _this6 = this;
  507. var formattedDriverDescClone = formattedDriverDesc ? {} : null;
  508. if (formattedDriverDesc && formattedDriverDesc.recommendedDrivers) {
  509. formattedDriverDescClone = JSON.parse(JSON.stringify(formattedDriverDesc));
  510. formattedDriverDescClone.recommendedDrivers.forEach(function (driver) {
  511. var dataSource = _this6.content.getFeature('Visualization').getDataSource();
  512. var column = dataSource.getMetadataColumn(driver.fieldIDForExpression);
  513. if (column) {
  514. driver.label = column.getLabel();
  515. }
  516. });
  517. }
  518. return formattedDriverDescClone;
  519. },
  520. /**
  521. * The recommended drivers coming from the ui dialog include a label property which
  522. * was added via _addColumnLabelsToTheDriverDesc before. Remove this label property.
  523. */
  524. _removeColumnLabelsFromDisplayStates: function _removeColumnLabelsFromDisplayStates(keyDriversDisplayStates) {
  525. if (keyDriversDisplayStates.recommendedDrivers) {
  526. // This data comes from the state, remove label property before passing it along
  527. keyDriversDisplayStates.recommendedDrivers.forEach(function (driver) {
  528. driver.label && delete driver.label;
  529. });
  530. }
  531. },
  532. /**
  533. * @returns {Object} inErrorState - true if we are in error state, {String} caption - error message.
  534. */
  535. getPossibleKeyDriversErrorState: function getPossibleKeyDriversErrorState() {
  536. var inErrorState = this.errorState && this.errorState.inErrorState !== undefined ? this.errorState.inErrorState : false;
  537. var caption = this.errorState && this.errorState.error ? this.errorState.error.caption : '';
  538. var state = {
  539. inErrorState: inErrorState,
  540. caption: caption
  541. };
  542. return state;
  543. },
  544. _getMaxPossibleDrivers: function _getMaxPossibleDrivers(recommendedDrivers, searchAbleColumns) {
  545. var totalPossibleDrivers = recommendedDrivers.length + searchAbleColumns.length;
  546. return totalPossibleDrivers > MAX_POSSIBLE_KEY_DRIVERS ? MAX_POSSIBLE_KEY_DRIVERS : totalPossibleDrivers;
  547. },
  548. /**
  549. * @param {String} targetId of the column of interest.
  550. * @param {Object} formattedDriverDesc description of the current drivers. This
  551. * includes:
  552. * recommendedDrivers
  553. * nonRecommendedDrivers
  554. * @returns the information needed to display the possible drivers.
  555. */
  556. _getFormattedDisplayStateFromDriverDescription: function _getFormattedDisplayStateFromDriverDescription(targetId, formattedDriverDesc) {
  557. var formattedDriverDescWithLabel = this._addColumnLabelsToTheDriverDesc(formattedDriverDesc);
  558. /*
  559. Get the format needed for display, this includes the recommended drivers
  560. list, the list of searchable columns to add as non-recommended, and the
  561. current user selected non-recommended drivers.
  562. */
  563. var recommendedDrivers = formattedDriverDescWithLabel.recommendedDrivers || [];
  564. var nonRecommendedDrivers = formattedDriverDescWithLabel.nonRecommendedDrivers || [];
  565. var selectedDrivers = [].concat(recommendedDrivers, nonRecommendedDrivers).filter(function (item) {
  566. return item.selected === true;
  567. });
  568. var searchAbleColumns = this._getUnrecommendedColumns(targetId);
  569. var result = {
  570. recommendedDrivers: recommendedDrivers,
  571. searchableColumns: searchAbleColumns,
  572. nonRecommendedDrivers: nonRecommendedDrivers,
  573. maxPossibleDrivers: this._getMaxPossibleDrivers(recommendedDrivers, searchAbleColumns),
  574. selectedDrivers: selectedDrivers,
  575. formattedDriverDesc: formattedDriverDesc
  576. };
  577. return result;
  578. },
  579. /**
  580. * @param {Object} dataItem of the column of interest.
  581. * @returns {Object} with possible drivers.
  582. */
  583. getKeyDrivers: function getKeyDrivers(dataItem) {
  584. var module = this.visAPI.getModule();
  585. // TODO: Remove dataItem.getItemId() when new slots panel feature flag is turned on
  586. var itemId = dataItem.getItemId && dataItem.getItemId() || dataItem.getColumnId();
  587. var displayState = this.getPossibleKeyDriversDisplayState(itemId);
  588. var recommendedDrivers = displayState.recommendedDrivers;
  589. var nonRecommendedDrivers = displayState.nonRecommendedDrivers;
  590. var selectedDrivers = [].concat(recommendedDrivers, nonRecommendedDrivers).filter(function (item) {
  591. return item.selected === true;
  592. });
  593. var recommendedFieldIds = recommendedDrivers.map(function (item) {
  594. return item.fieldIDForExpression;
  595. });
  596. var nonRecommendedFieldIds = nonRecommendedDrivers.map(function (item) {
  597. return item.fieldIDForExpression;
  598. });
  599. var selectedFieldIds = selectedDrivers.map(function (item) {
  600. return item.fieldIDForExpression;
  601. });
  602. var recommendedItems = module.getMetadataColumns(function (col) {
  603. return recommendedFieldIds.indexOf(col.getId()) !== -1;
  604. }).map(function (col) {
  605. return col.moserObject;
  606. });
  607. var nonRecommendedItems = module.getMetadataColumns(function (col) {
  608. return nonRecommendedFieldIds.indexOf(col.getId()) !== -1;
  609. }).map(function (col) {
  610. return col.moserObject;
  611. });
  612. var selectedItems = module.getMetadataColumns(function (col) {
  613. return selectedFieldIds.indexOf(col.getId()) !== -1;
  614. }).map(function (col) {
  615. return col.moserObject;
  616. });
  617. var initialItems = !nonRecommendedItems.length ? recommendedItems : selectedItems;
  618. var initialFieldIds = !selectedFieldIds.length ? recommendedFieldIds : selectedFieldIds;
  619. return {
  620. initialFieldIds: initialFieldIds, initialItems: initialItems, recommendedDrivers: recommendedDrivers, nonRecommendedDrivers: nonRecommendedDrivers, recommendedFieldIds: recommendedFieldIds, nonRecommendedFieldIds: nonRecommendedFieldIds, recommendedItems: recommendedItems, nonRecommendedItems: nonRecommendedItems
  621. };
  622. }
  623. });
  624. return VisPossibleKeyDriversManager;
  625. });
  626. //# sourceMappingURL=VisPossibleKeyDriversManager.js.map