'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2018, 2020 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['./Utils', './VisRecommenderDelegator', '../../../api/impl/Visualization', '../../../widgets/livewidget/models/LiveWidgetModel'], function (Utils, VisRecommenderDelegator, Visualization, LiveWidgetModel) { 'use strict'; var VisualizationSmartsRecommender = function () { function VisualizationSmartsRecommender(_ref) { var features = _ref.features; _classCallCheck(this, VisualizationSmartsRecommender); this.features = features; this.transaction = features['Transaction']; this.state = features['state.internal']; this.logger = features['Logger']; this.visDefinitions = this.features['VisDefinitions']; this.moserDataSources = this.features['DataSources.moser']; this._visRecommenderDelegator = new VisRecommenderDelegator({ dataSources: this.features['DataSources'], moserDataSources: this.moserDataSources, visDefinitions: this.visDefinitions, dashboardApi: this.features['Dashboard.API'], logger: this.logger }); } VisualizationSmartsRecommender.prototype.destroy = function destroy() { this.features = null; this.transaction = null; this.state = null; this.logger = null; this.visDefinitions = null; this.creationTransactionToken = null; this._visRecommenderDelegator = null; }; VisualizationSmartsRecommender.prototype.getAPI = function getAPI(type) { if (type === 'deprecated') { return this._deprecatedAPI; } return this._api; }; VisualizationSmartsRecommender.prototype.isValidDataSource = function isValidDataSource(visualization) { return visualization.getDataSource() && visualization.getDataSource().getState() !== 'error'; }; VisualizationSmartsRecommender.prototype.getRecommendedVisualizations = function getRecommendedVisualizations(visualization) { var _this = this; if (this.isValidDataSource(visualization) && visualization.getSlots().isMappingComplete()) { return this.recommendAlternateVisualizations(visualization).then(function (recommendations) { return recommendations.map(function (recommendation) { return _this.visDefinitions.getById(recommendation.visId); }); }); } else { return Promise.resolve([]); } }; VisualizationSmartsRecommender.prototype.getRecommendedTitle = function getRecommendedTitle(visualization) { if (this.isValidDataSource(visualization)) { return this._visRecommenderDelegator.getRecommendInfo(visualization).then(function (info) { return info.label; }).catch(function (error) { return Promise.reject(new Error(error)); }); } else { return Promise.resolve(); } }; VisualizationSmartsRecommender.prototype.setType = function setType(visualization, type, transactionToken) { var _this2 = this; var subTransaction = this.transaction.startTransaction(transactionToken); var newDefinition = this.visDefinitions.getByType(type); if (newDefinition) { if (!visualization.isTypeLocked()) { visualization.lockType(subTransaction); } if (this.isValidDataSource(visualization)) { return this._visRecommenderDelegator.recommendBindingsForSelectedViz(visualization, newDefinition.getId()).then(function (recommendation) { _this2._applyRecommendationToVisualization(visualization, recommendation, subTransaction); }).finally(function () { _this2.transaction.endTransaction(subTransaction); }); } else { visualization.setType(type, subTransaction); this.transaction.endTransaction(subTransaction); return Promise.resolve(); } } throw new Error('Invalid definition'); }; VisualizationSmartsRecommender.prototype.addDataItems = function addDataItems(visualization, dataItemSpecList, transactionToken) { var _this3 = this; var dataSource = visualization.getDataSource(); if (!dataSource) { throw new Error('You must set a datasource in the visualization feature first'); } var columnIds = dataItemSpecList.map(function (dataItemSpec) { return dataItemSpec.columnId; }); var subTransactionToken = this.transaction.startTransaction(transactionToken); if (visualization.isTypeLocked()) { return this._visRecommenderDelegator.recommendBindingsForNewColumns(visualization, columnIds).then(function (recommendation) { _this3._applyRecommendationToVisualization(visualization, recommendation, subTransactionToken); }).finally(function () { _this3.transaction.endTransaction(subTransactionToken); }); } else { return this._visRecommenderDelegator.recommendBestAlternateVisualization(visualization, columnIds).then(function (recommendation) { if (_this3.features.LiveWidgetSegment) { _this3.features.LiveWidgetSegment.track({ visualization: visualization, info: recommendation, category: 'fromRecommendation' }); } _this3._applyRecommendationToVisualization(visualization, recommendation, subTransactionToken); }).finally(function () { _this3.transaction.endTransaction(subTransactionToken); }); } }; VisualizationSmartsRecommender.prototype.recommendBestVisualization = function recommendBestVisualization(visualization, transactionToken) { var _this4 = this; if (this.isValidDataSource(visualization)) { var subTransaction = this.transaction.startTransaction(transactionToken); if (visualization.isTypeLocked()) { visualization.unlockType(subTransaction); } return this._visRecommenderDelegator.recommendBestAlternateVisualization(visualization).then(function (recommendation) { if (_this4.features.LiveWidgetSegment) { _this4.features.LiveWidgetSegment.track({ visualization: visualization, info: recommendation, category: 'fromRecommendation' }); } _this4._applyRecommendationToVisualization(visualization, recommendation, subTransaction); }).finally(function () { _this4.transaction.endTransaction(subTransaction); }); } return Promise.resolve(); }; VisualizationSmartsRecommender.prototype.recommendAlternateVisualizations = function recommendAlternateVisualizations(visualization) { var result = void 0; if (this.isValidDataSource(visualization)) { var dataSource = visualization.getDataSource(); var module = this.moserDataSources.getModule(dataSource.getId()); var columnIds = Utils.getUnboundColumnsIds(visualization); result = this._visRecommenderDelegator.recommendAlternateVisualizations(columnIds, { assetId: module.getAssetId(), sourceType: module.getSourceType(), module: module }); } else { result = Promise.resolve([]); } return result; }; VisualizationSmartsRecommender.prototype.recommendAlternateVisualizationsAsWidgetSpecs = function recommendAlternateVisualizationsAsWidgetSpecs(visualization, baseWidgetSpec) { var _this5 = this; return this.recommendAlternateVisualizations(visualization).then(function (recommendations) { return _this5._transformWidgetRecommendationsToLiveWidgetSpecs(recommendations, baseWidgetSpec); }); }; VisualizationSmartsRecommender.prototype.recommendRelatedVisualizations = function recommendRelatedVisualizations(visualization) { var result = void 0; if (this.isValidDataSource(visualization)) { var dataSource = visualization.getDataSource(); var columnIds = Utils.getUnboundColumnsIds(visualization); var topBottomItems = Utils.getTopBottomItems(visualization); var filterItems = Utils.getFilterItems(visualization); result = this.recommendRelatedVisualizationsFromColumnIds(dataSource.getId(), columnIds, [].concat(topBottomItems, filterItems)); } else { result = Promise.resolve([]); } return result; }; VisualizationSmartsRecommender.prototype.recommendAlternateVisualizationsFromColumnIds = function recommendAlternateVisualizationsFromColumnIds(dataSourceId, columnIds) { var module = this.moserDataSources.getModule(dataSourceId); return this._visRecommenderDelegator.recommendAlternateVisualizations(columnIds, { assetId: module.getAssetId(), sourceType: module.getSourceType(), module: module }); }; VisualizationSmartsRecommender.prototype.recommendRelatedVisualizationsFromColumnIds = function recommendRelatedVisualizationsFromColumnIds(dataSourceId, columnIds, filters, includeBestVisForTargetFields) { var module = this.moserDataSources.getModule(dataSourceId); return this._visRecommenderDelegator.recommendRelatedVisualizations(columnIds, { assetId: module.getAssetId(), sourceType: module.getSourceType(), module: module, filters: filters, includeBestVisForTargetFields: !!includeBestVisForTargetFields, moserDataSources: this.moserDataSources, visualization: this.visualization }); }; // deprecated interface -- called by explore VisualizationSmartsRecommender.prototype.recommendRelatedVisualizationsAsWidgetSpecs = function recommendRelatedVisualizationsAsWidgetSpecs(visualization, baseWidgetSpec) { var _this6 = this; return this.recommendRelatedVisualizations(visualization).then(function (recommendations) { return _this6._transformWidgetRecommendationsToLiveWidgetSpecs(recommendations, baseWidgetSpec); }); }; VisualizationSmartsRecommender.prototype.recommendCompareVisualizationsAsWidgetSpecs = function recommendCompareVisualizationsAsWidgetSpecs(visualization, widgetSpec) { var result = void 0; if (this.isValidDataSource(visualization)) { var dataSource = visualization.getDataSource(); var module = this.moserDataSources.getModule(dataSource.getId()); var options = {}; options.module = module; options.columnIds = Utils.getUnboundColumnsIds(visualization); options.widgetSpec = widgetSpec; options.visualization = visualization; options.assetId = dataSource.getAssetId(); options.sourceType = dataSource.getType(); options.moserDataSources = this.moserDataSources; result = this._visRecommenderDelegator.recommendCompareVisualizations(options.columnIds, options); } else { result = Promise.resolve([]); } return result; }; VisualizationSmartsRecommender.prototype._transformWidgetRecommendationsToLiveWidgetSpecs = function _transformWidgetRecommendationsToLiveWidgetSpecs(recommendations, basewidgetSpec) { var _this7 = this; var promises = []; var results = []; recommendations.forEach(function (recommendation) { basewidgetSpec = JSON.parse(JSON.stringify(basewidgetSpec)); // Shoud be done throught the API -- but we need to be able to easily create a content API basewidgetSpec.name = recommendation.label; // Should be done through the filter API once it is ready if (recommendation.filters && recommendation.filters.length > 0) { basewidgetSpec.localFilters = recommendation.filters; } var widgetModelCopy = new LiveWidgetModel(basewidgetSpec); var visualization = new Visualization({ doNotRegisterDataSourceUsage: true, content: _this7.content, features: { 'Models.internal': { getWidgetModel: function getWidgetModel() { return widgetModelCopy; } }, 'Dashboard.API': _this7.features['Dashboard.API'], 'Dashboard.Logger': _this7.features['Logger'], 'Dashboard.Colors': _this7.features['Colors'], 'Dashboard.Transaction': _this7.features['Transaction'], 'Dashboard.DataSources': _this7.features['DataSources'], 'Dashboard.internal': _this7.features['Dashboard.internal'], 'Dashboard.VisDefinitions': _this7.features['VisDefinitions'], 'Dashboard.VisDefinitions.internal': _this7.features['VisDefinitions.internal'] } }); promises.push(visualization.initialize().then(function () { visualization.unlockType(); _this7._applyRecommendationToVisualization(visualization, recommendation); // strip the thumbnailId from the base model var spec = widgetModelCopy.toJSON([], ['thumbnailId']); // return recommendation specs without ids will generate ids at the addition time delete spec.id; // TODO -- why this ? // the conversation expects a recommenderInfo for some reason.. spec.recommenderInfo = { rtitle: recommendation.title, description: recommendation.description, label: recommendation.label }; results.push({ name: widgetModelCopy.get('name'), spec: spec }); })); }); console.log('recommendations... ', results); return Promise.all(promises).then(function () { return results; }); }; VisualizationSmartsRecommender.prototype._applyRecommendationToVisualization = function _applyRecommendationToVisualization(visualization, recommendation, transactionToken) { var subTransaction = this.transaction.startTransaction(transactionToken); // Get the original list of dataItems mapped to slots before the visId change. var originalSlots = visualization.getSlots(); var originalMappedDataItemIdList = originalSlots.getMappingInfoList().map(function (diInfo) { return diInfo.dataItem && diInfo.dataItem.getId(); }); // Check if we are changing the viz if (recommendation.visId !== visualization.getDefinition().getId()) { var newDef = this.visDefinitions.getById(recommendation.visId); visualization.setType(newDef.getType(), subTransaction); } var slots = visualization.getSlots(); var unmappedDataItemList = []; var mappedDataItemListBySlot = {}; slots.getDataItemList().forEach(function (dataItem) { var mapping = slots.getMappingInfo(dataItem.getId()); if (!mapping) { unmappedDataItemList.push(dataItem); } else { var slotId = mapping.slot.getId(); if (!mappedDataItemListBySlot[slotId]) { mappedDataItemListBySlot[slotId] = []; } mappedDataItemListBySlot[slotId].push(dataItem); } }); var recommendedColumns = {}; for (var slotName in recommendation.slots) { recommendation.slots[slotName].forEach(function (value) { recommendedColumns[value] = true; }); } // Check if the recommender has changed some mapping to a different slot. If so, we move the dataItem to the unmapped datsaItems so that it can be // picked by another slot var slotList = slots.getSlotList(); slotList.forEach(function (slot) { var name = slot.getId(); var dataItems = slot.getDataItemList(true); dataItems.forEach(function (dataItem) { if (!recommendation.slots[name] || !recommendation.slots[name].find(function (columnId) { return dataItem.getColumnId() === columnId; })) { unmappedDataItemList.push(dataItem); } }); }); var newMappingBySlot = {}; slotList.forEach(function (slot) { var name = slot.getId(); var slotId = slot.getId(); var dataItemsToAdd = []; if (recommendation.slots[name]) { recommendation.slots[name].forEach(function (columnId) { var dataItem = void 0; var filterDataItems = function filterDataItems(di) { if (!dataItem && columnId === di.getColumnId()) { dataItem = di; return false; } return true; }; // 1 - Search the previous mapped dataitems to this slot (if they exist) if (mappedDataItemListBySlot[slotId]) { // We filter to exclude the matched dataitems as we find them mappedDataItemListBySlot[slotId] = mappedDataItemListBySlot[slotId].filter(filterDataItems); } // 2 - Search the unmapped data items if (!dataItem) { // Search the unmapped list unmappedDataItemList = unmappedDataItemList.filter(filterDataItems); } // 3 - New column eing added, create a dataImte if (!dataItem) { dataItem = slots.createDataItems([{ columnId: columnId }], subTransaction)[0]; } dataItemsToAdd.push(dataItem.getId()); }); } else { // slots which are not recommendable should maintain the original mappings // for example, heat slot for crosstab and table var unmappedSlot = slots.getSlot(slotId); var isHidden = unmappedSlot.getDefinition().isHidden(); if (isHidden) { unmappedSlot.getDataItemList().forEach(function (dataItem) { if (!recommendedColumns[dataItem.getColumnId()]) { dataItemsToAdd.push(dataItem.getId()); } }); } } var dataItemList = slot.getDataItemList(true); var dataItemsToRemove = dataItemList.filter(function (dataItem) { return dataItem.isColumnUnavailable(); }); if (dataItemsToRemove.length > 0) { slot.removeDataItems(dataItemsToRemove.map(function (dataItem) { return dataItem.getId(); }), subTransaction); } newMappingBySlot[slotId] = dataItemsToAdd; slot.removeDataItemsMapping(dataItemList.map(function (dataItem) { return dataItem.getId(); }), subTransaction); }); //Place slots which can generate measure groups at the end. //This means when a measure group is generated in addDataItemsMappings, //any other data items in the same slot will already exist, so it can be //placed in the correct position. var slotIds = Object.keys(newMappingBySlot).map(function (id) { return slots.getSlot(id); }).sort(function (slotA, slotB) { var a = slotA.getDefinition().getProperty('multiMeasure') ? 1 : -1, b = slotB.getDefinition().getProperty('multiMeasure') ? 1 : -1; return a - b; }).map(function (slot) { return slot.getId(); }); // Add all the new mappings slotIds.forEach(function (id) { if (newMappingBySlot[id].length > 0) { slots.getSlot(id).addDataItemsMapping(newMappingBySlot[id], -1, subTransaction); } }); var mappingInfoList = slots.getMappingInfoList(); // remove all leftover top bottom items if smarts returns top bottom items, such as for related-viz case if (recommendation.topBottom && recommendation.topBottom.length > 0) { recommendation.topBottom.forEach(function (_ref2) { var itemId = _ref2.itemId, spec = _ref2.spec; var mappingEntry = mappingInfoList.find(function (mappingInfoList) { return mappingInfoList.dataItem.getColumnId() === itemId; }); if (mappingEntry) { var topBottom = { type: spec.type, value: spec.value }; if (spec.context) { topBottom.context = { itemId: spec.context, aggregate: spec.aggregate }; } mappingEntry.dataItem.setTopBottom(topBottom, subTransaction); } }); } // Auto Group (recommendation.autoGroup || []).forEach(function (autoGroupItem) { var itemId = autoGroupItem.itemId, groupSize = autoGroupItem.groupSize; if (autoGroupItem && groupSize) { var mappingEntry = mappingInfoList.find(function (mappingInfoList) { return mappingInfoList.dataItem.getColumnId() === itemId; }); if (mappingEntry) { mappingEntry.dataItem.setBinning({ bins: groupSize }, subTransaction); } } }); //Compare the final mapped DataItem list to the original one. Clean any dataItems that are no longer mapped. var mappedDataItemIdList = slots.getMappingInfoList().map(function (diInfo) { return diInfo.dataItem && diInfo.dataItem.getId(); }); var unmappedDataItemIdList = originalMappedDataItemIdList.filter(function (originalId) { return mappedDataItemIdList.indexOf(originalId) === -1; }); if (unmappedDataItemIdList.length > 0) { slots.deleteDataItems(unmappedDataItemIdList, subTransaction); } this.transaction.endTransaction(subTransaction); }; return VisualizationSmartsRecommender; }(); return VisualizationSmartsRecommender; }); //# sourceMappingURL=SmartsRecommenderFeature.js.map