SmartNamingSvc.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2016, 2020
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['../../lib/@waca/core-client/js/core-client/ui/core/Class', 'underscore', '../nls/StringResources', '../../lib/@waca/dashboard-common/dist/core/MultilingualAttribute'], function (Class, _, resources, MultilingualAttribute) {
  8. // Static map of the slotIds used for labels in the smart names
  9. // Note that Data player (list) doesn't have an archetype.
  10. var chartInfo = {
  11. // Crosstab
  12. 'crosstab': {
  13. 'requiredMappings': ['values', 'row_level1'],
  14. 'optionalMapping': 'column_level1'
  15. },
  16. // Grid
  17. 'JQGrid': {
  18. 'requiredMappings': ['grid_cols']
  19. },
  20. // Hierarchy
  21. 'hierarchy': {
  22. 'requiredMappings': ['level1']
  23. },
  24. // Legacy map
  25. 'map': {
  26. 'requiredMappings': ['categories']
  27. },
  28. // Infographic summary
  29. 'infographicSummary': {
  30. 'requiredMappings': ['actual']
  31. },
  32. // Summary
  33. 'summary': {
  34. 'requiredMappings': ['actual']
  35. },
  36. // Kpi
  37. 'kpi': {
  38. 'requiredMappings': ['actual'],
  39. 'optionalMapping': 'sparkline.time'
  40. },
  41. // New live widget mappings
  42. 'LIVE_bubble_chart': {
  43. 'requiredMappings': ['x', 'y', 'size']
  44. },
  45. 'LIVE_line_column': {
  46. 'requiredMappings': ['columnValue', 'lineValue'],
  47. 'optionalMapping': 'categories'
  48. },
  49. 'LIVE_decision_tree': {
  50. 'requiredMappings': ['values']
  51. },
  52. 'LIVE_heatmap': {
  53. 'requiredMappings': ['color']
  54. },
  55. 'LIVE_pie': {
  56. 'requiredMappings': ['categories', 'values']
  57. },
  58. 'LIVE_pie_as_donut': {
  59. 'requiredMappings': ['categories', 'values']
  60. },
  61. 'LIVE_point': {
  62. 'requiredMappings': ['categories', 'values']
  63. },
  64. 'LIVE_line': {
  65. 'requiredMappings': ['categories', 'values']
  66. },
  67. 'LIVE_map': {
  68. 'requiredMappings': ['locations']
  69. },
  70. 'LIVE_network': {
  71. 'requiredMappings': ['categories', 'series'],
  72. 'optionalMapping': 'values'
  73. },
  74. 'LIVE_bar': {
  75. 'requiredMappings': ['values'],
  76. 'optionalMapping': 'categories'
  77. },
  78. 'LIVE_bullet': {
  79. 'requiredMappings': ['actual']
  80. },
  81. 'LIVE_stacked_bar': {
  82. 'requiredMappings': ['values'],
  83. 'optionalMapping': 'categories'
  84. },
  85. 'LIVE_column': {
  86. 'requiredMappings': ['values'],
  87. 'optionalMapping': 'categories'
  88. },
  89. 'LIVE_stacked_column': {
  90. 'requiredMappings': ['values'],
  91. 'optionalMapping': 'categories'
  92. },
  93. 'LIVE_packed_bubble': {
  94. 'requiredMappings': ['categories']
  95. },
  96. 'LIVE_hierarchy_packed_bubble': {
  97. 'requiredMappings': ['categories']
  98. },
  99. 'LIVE_marimekko': {
  100. 'requiredMappings': ['categories', 'values']
  101. },
  102. 'LIVE_sunburst': {
  103. 'requiredMappings': ['values']
  104. },
  105. 'LIVE_decision_analysis': {
  106. 'requiredMappings': ['values']
  107. },
  108. 'LIVE_data_player': {
  109. 'requiredMappings': ['category']
  110. },
  111. 'LIVE_radial_bar': {
  112. 'requiredMappings': ['values'],
  113. 'optionalMapping': 'categories'
  114. },
  115. 'LIVE_spiral': {
  116. 'requiredMappings': ['values']
  117. },
  118. 'LIVE_treemap': {
  119. 'requiredMappings': ['categories', 'size']
  120. },
  121. 'LIVE_wordcloud': {
  122. 'requiredMappings': ['categories']
  123. },
  124. 'LIVE_area': {
  125. 'requiredMappings': ['categories', 'values']
  126. },
  127. 'LIVE_area_as_stacked_percent': {
  128. 'requiredMappings': ['categories', 'values']
  129. },
  130. 'LIVE_scatter': {
  131. 'requiredMappings': ['x', 'y'],
  132. 'optionalMapping': 'categories'
  133. },
  134. 'LIVE_waterfall': {
  135. 'requiredMappings': ['values']
  136. }
  137. };
  138. // Map of archetype to visId. This only includes the ones where
  139. // the visId and archetype are different.
  140. // Many of them (like all the RAVE2 ones) are the same.
  141. var archetypeToVisIdMap = {
  142. // Grid
  143. 'grid': 'JQGrid',
  144. // Data player
  145. 'player': 'dataPlayer',
  146. // Summary(live)
  147. 'summaryChart': 'summary'
  148. };
  149. /** Sometimes the visId of a widget changes depending upon how many slots are used,
  150. * because of course they do. */
  151. var visIdSubstitution = {
  152. // Hierarchy(live)
  153. 'list': 'hierarchy',
  154. 'hierarchy2': 'hierarchy',
  155. 'hierarchy3': 'hierarchy',
  156. // Legacy map
  157. 'map1measure': 'map',
  158. 'map2measure': 'map',
  159. 'map3measure': 'map',
  160. //New Live vis widgets
  161. 'com.ibm.vis.rave2polygonmap': 'map',
  162. 'com.ibm.vis.rave2bundlearea': 'LIVE_area',
  163. 'com.ibm.vis.rave2bundlebar': 'LIVE_bar',
  164. 'com.ibm.vis.rave2bundlebullet': 'LIVE_bullet',
  165. 'com.ibm.vis.rave2bundlecomposite': 'LIVE_line_column',
  166. 'com.ibm.vis.rave2bundlepackedbubble': 'LIVE_packed_bubble',
  167. 'com.ibm.vis.rave2bundlehierarchicalpackedbubble': 'LIVE_hierarchy_packed_bubble',
  168. 'com.ibm.vis.rave2marimekko': 'LIVE_marimekko',
  169. 'com.ibm.vis.sunburst': 'LIVE_sunburst',
  170. 'com.ibm.vis.rave2comet': 'LIVE_decision_analysis',
  171. 'com.ibm.vis.rave2bundlepie': 'LIVE_pie',
  172. 'com.ibm.vis.rave2bundleradialbar': 'LIVE_radial_bar',
  173. 'com.ibm.vis.rave2bundletreemap': 'LIVE_treemap',
  174. 'com.ibm.vis.rave2bundlewordcloud': 'LIVE_wordcloud',
  175. 'com.ibm.vis.ravebubble': 'LIVE_bubble_chart',
  176. 'com.ibm.vis.decisiontree': 'LIVE_decision_tree',
  177. 'com.ibm.vis.rave2heat': 'LIVE_heatmap',
  178. 'com.ibm.vis.rave2line': 'LIVE_line',
  179. 'com.ibm.vis.rave2network': 'LIVE_network',
  180. 'com.ibm.vis.spiral': 'LIVE_spiral',
  181. 'com.ibm.vis.rave2bundletiledmap': 'LIVE_map',
  182. 'com.ibm.vis.rave2bundlecolumn': 'LIVE_column',
  183. 'com.ibm.vis.rave2bundlestackedbar': 'LIVE_stacked_bar',
  184. 'com.ibm.vis.rave2bundlestackedcolumn': 'LIVE_stacked_column',
  185. 'com.ibm.vis.rave2point': 'LIVE_point',
  186. 'com.ibm.vis.ravescatter': 'LIVE_scatter',
  187. 'com.ibm.vis.rave2bundlewaterfall': 'LIVE_waterfall',
  188. 'dataPlayer': 'LIVE_data_player',
  189. 'pieExpressedAsDonut': 'LIVE_pie_as_donut',
  190. 'areaExpressedAsStackedPercent': 'LIVE_area_as_stacked_percent'
  191. };
  192. var Svc = Class.extend({
  193. init: function init(widgetRegistry, boardModel) {
  194. this._widgetRegistry = widgetRegistry;
  195. this._boardModel = boardModel;
  196. },
  197. /** Returns a display name for a pin spec, generated based on the content of a pin spec.
  198. * @param pinSpec - a pin specification
  199. * @returns a generated display name for the pin spec.
  200. */
  201. getPinName: function getPinName(pinSpec) {
  202. // Count and sort our widgets, to get the most prevalent one.
  203. var count = 1;
  204. var typeCount = 1;
  205. var mostType;
  206. var widgets = [];
  207. if (pinSpec.content && pinSpec.content.widgets) {
  208. widgets = pinSpec.content.widgets;
  209. count = pinSpec.content.widgets.length;
  210. var widgetTypes = pinSpec.content.widgets.reduce(function (accumulator, elem) {
  211. var type = this._getWidgetType(elem);
  212. if (type in accumulator) {
  213. accumulator[type]++;
  214. } else {
  215. accumulator[type] = 1;
  216. }
  217. return accumulator;
  218. }.bind(this), {});
  219. mostType = Object.keys(widgetTypes).reverse(function (a, b) {
  220. return widgetTypes[a] > widgetTypes[b];
  221. })[0];
  222. typeCount = Object.keys(widgetTypes).length;
  223. }
  224. return this._getName(count, typeCount, mostType, widgets);
  225. },
  226. /** Generates a smart name for a single widget based on the widget's specification.
  227. * @param widgetSpec - the specification of a single widget.
  228. * @param existingNames - an optional object whose properties are existing names. If a
  229. * generated name exists in it, then the generated name will be unique-ified until it doesn't
  230. * exist in existingNames.
  231. * @returns the generated smart name.
  232. */
  233. getWidgetName: function getWidgetName(widgetSpec, existingNames) {
  234. var type = this._getWidgetType(widgetSpec);
  235. var widgets = [widgetSpec];
  236. return this._getName(1, 1, type, widgets, existingNames);
  237. },
  238. getLocalizedWidgetType: function getLocalizedWidgetType(widgetSpec) {
  239. var type = this._getWidgetType(widgetSpec);
  240. return resources.get(type + '_label');
  241. },
  242. _getWidgetType: function _getWidgetType(widgetSpec) {
  243. var type = widgetSpec.type;
  244. if (type === 'media' && widgetSpec.mediaType) {
  245. type = widgetSpec.mediaType;
  246. } else if (widgetSpec.placeholder && !this._getTextContent(widgetSpec).length) {
  247. type = widgetSpec.type === 'text' || widgetSpec.placeholder.text ? 'placeholder_text' : 'placeholder_vis';
  248. }
  249. return type;
  250. },
  251. _getCountKey: function _getCountKey(count, unknown) {
  252. if (count === 1) {
  253. return unknown ? 'one_unknown' : 'one';
  254. } else if (count > 1 && count < 5) {
  255. return unknown ? 'few_unknown' : 'few';
  256. } else {
  257. return unknown ? 'many_unknown' : 'many';
  258. }
  259. },
  260. _getLabelsFromMappings: function _getLabelsFromMappings(prev, curr) {
  261. prev[curr.slotId] = curr.label;
  262. return prev;
  263. },
  264. _isInfographics: function _isInfographics(widget) {
  265. return widget && widget.hasGraphic;
  266. },
  267. /** Returns the visId of the specified widget, as far as the SmartNamingSvc is concerned. */
  268. getWidgetVisId: function getWidgetVisId(widget) {
  269. var visId = widget.visId;
  270. // While editing charts (notably crosstabs), sometimes the visId is undefined, so try to use the archetype instead
  271. if (!visId && widget.archetype) {
  272. visId = archetypeToVisIdMap[widget.archetype] || widget.archetype;
  273. }
  274. // Special case for infograpics
  275. if (visId === 'summary' && this._isInfographics(widget)) {
  276. visId = 'infographicSummary';
  277. }
  278. // Some chart types(Pie and area) can change display from the properties
  279. // Check the properties for displaying reasonable names
  280. if (['com.ibm.vis.rave2bundlepie', 'com.ibm.vis.rave2bundlearea'].indexOf(visId) !== -1 && widget.properties) {
  281. visId = this._getExpressedVisId(visId, widget.properties);
  282. }
  283. // visId substitutions.
  284. if (visIdSubstitution[visId]) {
  285. visId = visIdSubstitution[visId];
  286. }
  287. return visId;
  288. },
  289. _getExpressedVisId: function _getExpressedVisId(visId, properties) {
  290. switch (visId) {
  291. case 'com.ibm.vis.rave2bundlepie':
  292. _.each(properties, function (property) {
  293. if (property.id === 'pie.has.hole' && property.value === true) {
  294. visId = 'pieExpressedAsDonut';
  295. }
  296. }.bind(this));
  297. break;
  298. case 'com.ibm.vis.rave2bundlearea':
  299. _.each(properties, function (property) {
  300. if (property.id === 'stacked.percent' && property.value === true) {
  301. visId = 'areaExpressedAsStackedPercent';
  302. }
  303. }.bind(this));
  304. break;
  305. default:
  306. break;
  307. }
  308. return visId;
  309. },
  310. _getKeyFromDataLabels: function _getKeyFromDataLabels(visId, labels) {
  311. if (!chartInfo[visId]) {
  312. return 'one_unknown_live';
  313. }
  314. var hasRequiredLabels = _.every(chartInfo[visId].requiredMappings, function (mapping) {
  315. return labels[mapping];
  316. });
  317. // Handle the optional mapping if there is any.
  318. var hasOptionalLabel = chartInfo[visId].optionalMapping && labels[chartInfo[visId].optionalMapping];
  319. if (hasOptionalLabel && hasRequiredLabels) {
  320. return visId + '_opt_mapping';
  321. } else if (hasRequiredLabels) {
  322. return visId + '_has_mapping';
  323. } else {
  324. return visId + '_no_mapping';
  325. }
  326. },
  327. _getName: function _getName(count, typeCount, mostType, widgets, existingNames) {
  328. var counts = this._getCountKey(count, false);
  329. var nameInfo = {
  330. count: count,
  331. resource_key: counts + '_' + mostType
  332. };
  333. var firstWidget = widgets[0];
  334. if (count === 1 && firstWidget) {
  335. switch (mostType) {
  336. case 'text':
  337. this._getTextName(nameInfo, firstWidget);
  338. break;
  339. case 'live':
  340. this._getLiveName(nameInfo, firstWidget);
  341. break;
  342. case 'filter':
  343. this._getFilterName(nameInfo, firstWidget);
  344. break;
  345. case 'shape':
  346. this._getShapeName(nameInfo, firstWidget);
  347. break;
  348. case 'image':
  349. case 'webpage':
  350. case 'youtube':
  351. case 'embedMedia':
  352. case 'media':
  353. case 'multipurpose':
  354. this._getStaticName(nameInfo, firstWidget, mostType);
  355. break;
  356. case 'placeholder_text':
  357. case 'placeholder_vis':
  358. this._getPlaceholderName(nameInfo, mostType);
  359. break;
  360. default:
  361. // Look it up in the registry
  362. this._getRegistryName(nameInfo, firstWidget, mostType, counts);
  363. break;
  364. }
  365. } else if (typeCount > 1) {
  366. nameInfo.resource_key = count < 5 ? 'few_unknown' : 'many_unknown';
  367. }
  368. // Make sure we return something reasonable in case new widget types are added and no one updates this code.
  369. var name = resources.get(nameInfo.resource_key, nameInfo);
  370. if (name === nameInfo.resource_key) {
  371. nameInfo.resource_key = this._getCountKey(count, true);
  372. name = resources.get(nameInfo.resource_key, nameInfo);
  373. }
  374. // Make sure the name is unique.
  375. if (existingNames && existingNames[name]) {
  376. nameInfo.resource_key = nameInfo.resource_key + '_numbered';
  377. // Assuming no one will ever have more than 500 widgets with the same name on single frame,
  378. // break to avoid getting into an infinite loop.
  379. for (var number = 1; number < 501 && existingNames[name]; number++) {
  380. nameInfo.number = number;
  381. name = resources.get(nameInfo.resource_key, nameInfo);
  382. }
  383. }
  384. return name;
  385. },
  386. _getTextContent: function _getTextContent(widgetSpec) {
  387. return new MultilingualAttribute(widgetSpec.name, this._boardModel.getLanguageModelOptions()).getValue();
  388. },
  389. _getTextName: function _getTextName(nameInfo, firstWidget) {
  390. // If content exists, use that
  391. var textContent = this._getTextContent(firstWidget);
  392. if (textContent) {
  393. nameInfo.name = textContent;
  394. nameInfo.resource_key = 'one_name';
  395. } else {
  396. if (firstWidget.placeholder) {
  397. nameInfo.resource_key = 'one_placeholder_text';
  398. } else {
  399. nameInfo.resource_key = 'noExtraText_text';
  400. }
  401. }
  402. },
  403. _getLiveName: function _getLiveName(nameInfo, firstWidget) {
  404. var visId = this.getWidgetVisId(firstWidget);
  405. var widgetTitle = null;
  406. if (firstWidget.name) {
  407. var multilingualAttribute = new MultilingualAttribute(firstWidget.name, this._boardModel.getLanguageModelOptions());
  408. widgetTitle = multilingualAttribute.getValue().trim();
  409. }
  410. if (widgetTitle && widgetTitle.length > 0) {
  411. nameInfo.name = widgetTitle;
  412. nameInfo.resource_key = visId + '_named';
  413. var name = resources.get(nameInfo.resource_key, nameInfo);
  414. // We do this check here so that we at least know that it is a named chart of some sort.
  415. if (name === nameInfo.resource_key) {
  416. nameInfo.resource_key = 'one_named_live';
  417. }
  418. } else {
  419. var slotsInfo = [];
  420. var slots = firstWidget.slotmapping && firstWidget.slotmapping.slots;
  421. var items = this._getAllDataItemsFromData(firstWidget.data);
  422. _.each(items, function (item) {
  423. var iteminfo = {
  424. label: item.itemLabel
  425. };
  426. _.each(slots, function (slot) {
  427. if (slot.dataItems.indexOf(item.id) >= 0) {
  428. iteminfo.slotId = slot.name;
  429. }
  430. }.bind(this));
  431. slotsInfo.push(iteminfo);
  432. }.bind(this));
  433. _.extend(nameInfo, slotsInfo.reduce(this._getLabelsFromMappings, {}));
  434. nameInfo.resource_key = this._getKeyFromDataLabels(visId, nameInfo);
  435. }
  436. },
  437. _getAllDataItemsFromData: function _getAllDataItemsFromData(data) {
  438. var items = [];
  439. if (data && data.dataViews) {
  440. data.dataViews.forEach(function (dataView) {
  441. items.push.apply(items, dataView.dataItems);
  442. });
  443. }
  444. return items;
  445. },
  446. _getFilterName: function _getFilterName(nameInfo, firstWidget) {
  447. if (firstWidget.properties.itemName) {
  448. nameInfo.name = firstWidget.properties.itemName.trim();
  449. var name = resources.get(nameInfo.resource_key, nameInfo);
  450. if (name === nameInfo.resource_key) {
  451. nameInfo.resource_key = 'one_named_filter';
  452. }
  453. } else {
  454. nameInfo.resource_key = 'one_unknown_filter';
  455. }
  456. },
  457. _getShapeName: function _getShapeName(nameInfo, firstWidget) {
  458. if (firstWidget.name) {
  459. nameInfo.name = firstWidget.name.trim();
  460. nameInfo.resource_key = 'one_name';
  461. } else {
  462. nameInfo.resource_key = 'noExtraText_shape';
  463. }
  464. },
  465. _getStaticName: function _getStaticName(nameInfo, firstWidget, mostType) {
  466. var multilingualKey;
  467. if (mostType === 'image') {
  468. multilingualKey = firstWidget.altText;
  469. } else {
  470. multilingualKey = firstWidget.title;
  471. }
  472. var textContent = null;
  473. if (multilingualKey) {
  474. var multilingualAttribute = new MultilingualAttribute(multilingualKey, this._boardModel.getLanguageModelOptions());
  475. textContent = multilingualAttribute.getValue();
  476. }
  477. if (textContent) {
  478. nameInfo.text = textContent.trim();
  479. nameInfo.resource_key = 'one_' + mostType;
  480. } else {
  481. nameInfo.resource_key = 'noExtraText_' + mostType;
  482. }
  483. },
  484. _getRegistryName: function _getRegistryName(nameInfo, firstWidget, mostType, counts) {
  485. if (this._widgetRegistry && this._widgetRegistry[mostType] && !this._widgetRegistry[mostType].builtin) {
  486. if (firstWidget.name) {
  487. var multilingualAttribute = new MultilingualAttribute(firstWidget.name, this._boardModel.getLanguageModelOptions());
  488. nameInfo.name = multilingualAttribute.getValue().trim();
  489. nameInfo.resource_key = 'one_custom_named';
  490. } else {
  491. nameInfo.resource_key = 'one_custom';
  492. }
  493. } else {
  494. nameInfo.resource_key = counts + '_' + mostType;
  495. }
  496. },
  497. _getPlaceholderName: function _getPlaceholderName(nameInfo, mostType) {
  498. nameInfo.resource_key = 'one_' + mostType;
  499. }
  500. });
  501. return Svc;
  502. });
  503. //# sourceMappingURL=SmartNamingSvc.js.map