123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- 'use strict';
- /*
- *+------------------------------------------------------------------------+
- *| Licensed Materials - Property of IBM
- *| IBM Cognos Products: Dashboard
- *| (C) Copyright IBM Corp. 2018
- *|
- *| US Government Users Restricted Rights - Use, duplication or disclosure
- *| restrictedashboardSpecSvcd by GSA ADP Schedule Contract with IBM Corp.
- *+------------------------------------------------------------------------+
- */
- define(['underscore', './BaseSmartsVisRecommenderWrapper', './IRUtils', '../../apiHelpers/SlotAPIHelper', '../../widgets/livewidget/nls/StringResources', './LegacyMapMappingManager', './SmartsRecommenderError', './VisRecommenderBindingFallback', '../../extensions/smarts-recommender/impl/Utils'], function (_, BaseSmartsVisRecommenderWrapper, IRUtils, SlotAPIHelper, StringResources, LegacyMapMappingManager, SmartsError, VisRecommenderBindingFallback, Utils) {
- 'use strict';
- /*
- * This class is a wrapper for the Smarts Visualization Recommender for dashboard.
- */
- var DashboardSmartsVisRecommenderWrapper = BaseSmartsVisRecommenderWrapper.extend({
- _UnsupportedVisIds: ['list', 'dataPlayer', 'com.ibm.vis.rave2polygonmap'], // not supported by SmartsVisRecommender
- _LegacyMapMappingManager: LegacyMapMappingManager, //for unit tests
- _VisRecommenderBindingFallback: VisRecommenderBindingFallback,
- // constructor
- init: function init(_ref) {
- var moserDataSources = _ref.moserDataSources,
- visDefinitions = _ref.visDefinitions,
- logger = _ref.logger,
- dashboardApi = _ref.dashboardApi,
- dataSources = _ref.dataSources;
- this._visDefinitions = visDefinitions;
- this._logger = logger;
- this._dashboardApi = dashboardApi;
- var ajaxSvc = dashboardApi.getGlassCoreSvc('.Ajax');
- var glassOptions = {};
- dashboardApi.prepareGlassOptions(glassOptions);
- DashboardSmartsVisRecommenderWrapper.inherited('init', this, [{ moserDataSources: moserDataSources, ajaxSvc: ajaxSvc, logger: logger, glassOptions: glassOptions, dashboardApi: dashboardApi, dataSources: dataSources }]);
- },
- destroy: function destroy() {
- DashboardSmartsVisRecommenderWrapper.inherited('destroy', this, arguments);
- this.moserDataSources = null;
- this._visDefinitions = null;
- this._logger = null;
- this._dashboardApi = null;
- },
- /**
- * Fetch the recommender information for the current chart
- * @return {object} payload from smarts server (including `label` attribute)
- */
- getRecommendInfo: function getRecommendInfo(visualization) {
- var _this = this;
- var columns = this._getBoundColumnsInfo(visualization);
- this._replaceHiddenCustomGroupCols(columns, visualization);
- var options = this._getRequestOptions(visualization);
- var definition = visualization.getDefinition();
- return DashboardSmartsVisRecommenderWrapper.inherited('getRecommendInfo', this, [{
- columns: columns,
- visId: definition ? definition.getId() : null,
- requestOptions: options
- }]).then(function (chartInfo) {
- if (chartInfo.mandatorySlotsMissing) {
- return _this._buildChartInfoFromColumns(columns, options.module);
- }
- return chartInfo;
- }).catch(function (error) {
- if (error.message === _this.getUnsupportedVizErrCode()) {
- return _this._buildChartInfoFromColumns(columns, options.module);
- } else {
- return Promise.reject(error);
- }
- });
- },
- _replaceHiddenCustomGroupCols: function _replaceHiddenCustomGroupCols(columns, visualization) {
- var sourceId = visualization.getDataSource().getId();
- var shaping = this._dashboardApi.getFeature('DataSources.moser');
- if (shaping && shaping.isConsumerGroupColumn) {
- columns.forEach(function (columnEntry) {
- var columnIds = columnEntry.columnIds;
- columnIds.forEach(function (colId, i) {
- if (shaping.isConsumerGroupColumn(sourceId, colId)) {
- var customGroup = shaping.getCustomGroupData(sourceId, colId);
- if (customGroup && customGroup.basedOnMoserObjectId) {
- columnIds[i] = customGroup.basedOnMoserObjectId;
- }
- }
- });
- });
- }
- },
- /*
- * This works as a local fallback to get smart title for smarts-unsupported visualization types (such as list, legacy map etc)
- * By design, we will use the concatenated column labels as the 'smarts title'
- */
- _buildChartInfoFromColumns: function _buildChartInfoFromColumns(columns, module) {
- var aTitle = [];
- if (columns && columns.length > 0 && module) {
- columns.forEach(function (column) {
- column.columnIds.forEach(function (id) {
- module.getMetadataColumn(id) && aTitle.push(module.getMetadataColumn(id).getLabel());
- });
- });
- }
- var joinedTitles = aTitle.join(StringResources.get('listSeparator') + ' ');
- //This could be augmented to create descriptions
- return Promise.resolve({
- label: joinedTitles,
- title: joinedTitles
- });
- },
- /*
- * Given a visualization type, recommends the binding for the newly added columns (user added new columns to locked viz type)
- * @param {string} visId
- * @param {object} array of metadataColumn ids for the newly added columns
- * @returns {Object}
- */
- recommendBindingsForNewColumns: function recommendBindingsForNewColumns(visualization, columnsToBind) {
- var _this2 = this;
- var visId = visualization.getDefinition().getId();
- if (this._isUnsupportedVisId(visId)) {
- return this._recommendBindingsForUnsupportedVisId(visualization, visId, columnsToBind);
- }
- return new Promise(function (resolve) {
- var boundColumnsInfo = _this2._getBoundColumnsInfo(visualization);
- var requestOptions = _this2._getRequestOptions(visualization);
- resolve({
- columnsToBind: columnsToBind,
- boundColumnsInfo: boundColumnsInfo,
- requestOptions: requestOptions,
- visId: visId
- });
- }).then(this.recommendBindings.bind(this)).then(this._handleBindingRecommendationResponse.bind(this, visualization)).catch(this._handleSmartsError.bind(this));
- },
- /*
- * Recommend bindings (aka mappings) for the newly selected visualization (user chose to change viz type) based on columns in the current model.
- * @param {string} visId
- * @returns {Object}
- */
- recommendBindingsForSelectedViz: function recommendBindingsForSelectedViz(visualization, visId) {
- if (this._isUnsupportedVisId(visId)) {
- return this._recommendBindingsForUnsupportedVisId(visualization, visId);
- }
- var requestParams = {
- visId: visId,
- requestOptions: this._getRequestOptions(visualization)
- };
- var definition = visualization.getDefinition();
- var originalVisId = definition ? definition.getId() : null;
- if (this._isUnsupportedVisId(originalVisId)) {
- requestParams.columnsToBind = Utils.getUnboundColumnsIds(visualization);
- } else {
- requestParams.originalVisId = originalVisId;
- requestParams.boundColumnsInfo = this._getBoundColumnsInfo(visualization);
- }
- return this.recommendBindings(requestParams).then(this._handleBindingRecommendationResponse.bind(this, visualization)).catch(this._handleSmartsError.bind(this));
- },
- /*
- * Returns the best alternate visualization recommendation for the columns in the current model plus
- * new columns added (if any).
- * @param {newColumns} - optional
- * @returns {Object}
- */
- recommendBestAlternateVisualization: function recommendBestAlternateVisualization(visualization) {
- var _this3 = this;
- var newColumnsIds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
- return new Promise(function (resolve) {
- var columnIds = newColumnsIds.concat(Utils.getUnboundColumnsIds(visualization));
- resolve(columnIds);
- }).then(function (columnIds) {
- return _this3.recommendAlternateVisualizations(columnIds, _this3._getRequestOptions(visualization));
- }).then(function (recommendations) {
- return recommendations[0];
- }).catch(this._handleSmartsError.bind(this));
- },
- /*
- * Assign the slots for visualizations that are not supported by Smarts visRecommender
- */
- _recommendBindingsForUnsupportedVisId: function _recommendBindingsForUnsupportedVisId(visualization, visId, newColumnsIds) {
- switch (visId) {
- case 'com.ibm.vis.rave2polygonmap':
- {
- return this._recommendBindingsForLegacyMap(visualization, visId, newColumnsIds);
- }
- case 'dataPlayer':
- case 'list':
- {
- return this._recommendBindingsForList_DataPlayer(visualization, visId, newColumnsIds);
- }
- }
- },
- _handleSmartsError: function _handleSmartsError(e) {
- this._logger.warn('An error occurred in the smart recommender', e);
- throw e;
- },
- _handleBindingRecommendationResponse: function _handleBindingRecommendationResponse(visualization, recommendations) {
- var _this4 = this;
- /*
- * Recommender may return returns > 1 binding recommendations and the recommendation may contain
- * unsupported slots or recommendations that do not render in Dashboard/Explore due to required slots not being mapped
- * This function filters out the recommendations that contains unsupported slots and pick the best recommendation based on max
- * required slots mapped - this will reduce the chance that the visualization is not rendered.
- */
- //This is a binding request so the visId for all the recommendations are the same, so just use the first one.
- return this._getVisDefinition(recommendations[0].visId).then(function (visDefinition) {
- //filter out the hidden slots so we dont recommend them
- var slotDefinitions = visDefinition.getSlotList().filter(function (slot) {
- return !slot.getProperty('hidden');
- });
- var allSlotIds = slotDefinitions.map(function (dataSlot) {
- return dataSlot.getId();
- });
- var allSlotsAreSupported = function allSlotsAreSupported(recommendation) {
- return Object.keys(recommendation.slots).filter(function (slotId) {
- return allSlotIds.indexOf(slotId) === -1;
- }).length === 0;
- };
- var requiredSlotIds = visDefinition.getSlotList().filter(function (dataSlot) {
- return !dataSlot.getProperty('optional');
- }).map(function (dataSlot) {
- return dataSlot.getId();
- });
- // returns the recommendation that has the max required slots for the viz
- var maxRequiredSlotsMatched = function maxRequiredSlotsMatched(accumulator, currentRec) {
- var maxRequiredSlots = function maxRequiredSlots(recommendation) {
- return Object.keys(recommendation.slots).filter(function (slotId) {
- return requiredSlotIds.indexOf(slotId) !== -1;
- }).length;
- };
- if (maxRequiredSlots(currentRec) > maxRequiredSlots(accumulator)) {
- return currentRec;
- }
- return accumulator;
- };
- var recommendation = recommendations.filter(allSlotsAreSupported).reduce(maxRequiredSlotsMatched);
- //attempt to manually assign bindings for columns that smarts was unable to bind and show a toast for any that cannot be bound
- return _this4._handleUnboundColumns(visualization, recommendation, slotDefinitions);
- });
- },
- _getRequestOptions: function _getRequestOptions(visualization) {
- var module = this._getModule(visualization);
- return {
- assetId: module.getAssetId(),
- sourceType: module.getSourceType(),
- module: module
- };
- },
- _getVisDefinition: function _getVisDefinition(visId) {
- return Promise.resolve(this._visDefinitions.getById(visId));
- },
- //Return an array of column info with column and (SmartsVisRecommender) slot ids
- _getBoundColumnsInfo: function _getBoundColumnsInfo(visualization) {
- var mappedSlots = visualization.getSlots().getMappedSlotList();
- if (!mappedSlots || !mappedSlots.length) {
- return;
- }
- // ignore hidden slots which are not considered as part of mappings
- // e.g. 'rank' slot of grid, 'size' slot of scatter
- var columnInfos = mappedSlots.filter(function (slot) {
- return slot && !slot.getDefinition().isHidden();
- }).map(function (mappedSlot) {
- var columnIds = Utils.filterInvalidDataItems(mappedSlot.getDataItemList()).map(function (dataItem) {
- return dataItem.getColumnId();
- });
- var columnInfo = void 0;
- if (columnIds.length) {
- columnInfo = {
- slotId: mappedSlot.getId(),
- columnIds: columnIds
- };
- }
- return columnInfo;
- });
- return _.filter(columnInfos, function (columnInfo) {
- return columnInfo;
- });
- },
- /*
- * Shows the toast for dropped columns. Takes a list of column metadata as argument.
- */
- _showToastForDroppedColumns: function _showToastForDroppedColumns(messageId, droppedColumns) {
- if (!droppedColumns || !droppedColumns.length) {
- return;
- }
- var columnLabels = droppedColumns.map(function (column) {
- return column.getLabel();
- }).join(', ').toLocaleString();
- var toastInfoText = StringResources.get(messageId, { columns: columnLabels });
- this._dashboardApi.showToast(toastInfoText, { type: 'info', preventDuplicates: true });
- },
- _getUsedMetadataColumns: function _getUsedMetadataColumns(visualization) {
- var columns = [];
- visualization.getSlots().getMappingInfoList().forEach(function (mapping) {
- if (mapping.slot) {
- columns.push(mapping.dataItem.getColumnId());
- }
- });
- return columns;
- },
- _recommendBindingsForLegacyMap: function _recommendBindingsForLegacyMap(visualization, visId, newColumnsIds) {
- var bKeepExistingMappings = true;
- if (!newColumnsIds) {
- //drop existing mapping if user is changing visualization
- bKeepExistingMappings = false;
- newColumnsIds = this._getUsedMetadataColumns(visualization);
- }
- var columns = newColumnsIds.map(function (columnId) {
- return visualization.getDataSource().getMetadataColumn(columnId);
- });
- return this._getVisDefinition(visId).then(function (visDefinition) {
- var legacyMapMappingManager = new this._LegacyMapMappingManager(visualization);
- var mappedColumns = legacyMapMappingManager.mapColumnsToSlots(visDefinition, columns, bKeepExistingMappings);
- if (legacyMapMappingManager.getUnmappedColumns()) {
- this._showToastForDroppedColumns('maxColumnsExceeded', legacyMapMappingManager.getUnmappedColumns());
- }
- return {
- slots: mappedColumns,
- visId: visDefinition.getId()
- };
- }.bind(this));
- },
- _recommendBindingsForList_DataPlayer: function _recommendBindingsForList_DataPlayer(visualization, visId, newColumnsIds) {
- //The mapping of slots for both vis types is identical as they both support only one slot and any data type
- return this._getVisDefinition(visId).then(function (visDefinition) {
- var metadataColumns = this._getUsedMetadataColumns(visualization);
- if (newColumnsIds) {
- metadataColumns = metadataColumns.concat(newColumnsIds);
- }
- // If the number of columns exceeded the max allowed for slot, either
- // a. in the case of change vis type - drop enough columns to meet the max no. of columns limit or
- // b. in the case of add columns - add enough columns to equal the max no. of columns limit and drop the rest
- var droppedColumns;
- var maxItem = visDefinition.getSlotList()[0].getProperty('maxItems');
- if (metadataColumns.length > maxItem) {
- droppedColumns = metadataColumns.splice(maxItem);
- }
- if (droppedColumns && droppedColumns.length > 0) {
- this._showToastForDroppedColumns('maxColumnsExceeded', droppedColumns.map(function (id) {
- return visualization.getDataSource().getMetadataColumn(id);
- }));
- }
- var recommendation = {
- slots: {},
- visId: visDefinition.getId()
- };
- recommendation.slots[visDefinition.getSlotList()[0].getId()] = metadataColumns;
- return recommendation;
- }.bind(this));
- },
- _isUnsupportedVisId: function _isUnsupportedVisId(visId) {
- return visId == undefined ? true : this._UnsupportedVisIds.indexOf(visId) !== -1;
- },
- _handleUnboundColumns: function _handleUnboundColumns(visualization, recommendation, slotDefinitions) {
- var unboundColumnIds = recommendation.unbound;
- if (!unboundColumnIds || !unboundColumnIds.length) {
- return recommendation;
- }
- var VisRecommenderBindingFallback = new this._VisRecommenderBindingFallback(visualization);
- recommendation = VisRecommenderBindingFallback.assignUnboundColumns(recommendation, slotDefinitions);
- //show toast for columns still unable to bind
- var label = recommendation.unbound.length > 1 ? 'unboundColumns' : 'unboundColumn';
- var dataSource = visualization.getDataSource();
- this._showToastForDroppedColumns(label, recommendation.unbound.map(function (columnId) {
- return dataSource.getMetadataColumn(columnId);
- }));
- return recommendation;
- },
- _getModule: function _getModule(visualization) {
- //should not cache module because it might be undefined when the viz has no mappings
- var module = void 0;
- var dataSource = visualization.getDataSource();
- if (dataSource) {
- module = this.moserDataSources.getModule(dataSource.getId());
- }
- return module;
- }
- });
- return DashboardSmartsVisRecommenderWrapper;
- });
- //# sourceMappingURL=DashboardSmartsVisRecommenderWrapper.js.map
|