data_validator.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /**
  2. * DevExtreme (viz/components/data_validator.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var typeUtils = require("../../core/utils/type");
  11. var STRING = "string";
  12. var NUMERIC = "numeric";
  13. var DATETIME = "datetime";
  14. var DISCRETE = "discrete";
  15. var SEMIDISCRETE = "semidiscrete";
  16. var CONTINUOUS = "continuous";
  17. var LOGARITHMIC = "logarithmic";
  18. var VALUE_TYPE = "valueType";
  19. var ARGUMENT_TYPE = "argumentType";
  20. var extend = require("../../core/utils/extend").extend;
  21. var axisTypeParser = require("../core/utils").enumParser([STRING, NUMERIC, DATETIME]);
  22. var _getParser = require("./parse_utils").getParser;
  23. var _isDefined = typeUtils.isDefined;
  24. var _isFunction = typeUtils.isFunction;
  25. var _isArray = Array.isArray;
  26. var _isString = typeUtils.isString;
  27. var _isDate = typeUtils.isDate;
  28. var _isNumber = typeUtils.isNumeric;
  29. var _isObject = typeUtils.isObject;
  30. function groupingValues(data, others, valueField, index) {
  31. if (index >= 0) {
  32. data.slice(index).forEach(function(cell) {
  33. if (_isDefined(cell[valueField])) {
  34. others[valueField] += cell[valueField];
  35. cell[valueField] = void 0
  36. }
  37. })
  38. }
  39. }
  40. function processGroups(groups) {
  41. groups.forEach(function(group) {
  42. group.valueType = group.valueAxisType = null;
  43. group.series.forEach(function(series) {
  44. series.updateDataType({})
  45. });
  46. group.valueAxis && group.valueAxis.resetTypes(VALUE_TYPE)
  47. })
  48. }
  49. function sortValues(data, asc, selector) {
  50. var func = asc ? function(a, b) {
  51. return a - b
  52. } : function(a, b) {
  53. return b - a
  54. };
  55. data.sort(function(a, b) {
  56. var valA = selector(a);
  57. var valB = selector(b);
  58. var aa = _isDefined(valA) ? 1 : 0;
  59. var bb = _isDefined(valB) ? 1 : 0;
  60. return aa && bb ? func(valA, valB) : func(aa, bb)
  61. });
  62. return data
  63. }
  64. function resetArgumentAxes(axes) {
  65. axes && axes.forEach(function(axis) {
  66. axis.resetTypes(ARGUMENT_TYPE)
  67. })
  68. }
  69. function parseCategories(categories, parser) {
  70. var newArray = [];
  71. categories.forEach(function(category) {
  72. var parsedCategory = parser(category);
  73. void 0 !== parsedCategory && newArray.push(parsedCategory)
  74. });
  75. return newArray
  76. }
  77. function parseAxisCategories(groupsData, parsers) {
  78. var argumentCategories = groupsData.argumentOptions && groupsData.argumentOptions.categories;
  79. groupsData.groups.forEach(function(valueGroup, i) {
  80. var categories = valueGroup.valueOptions && valueGroup.valueOptions.categories;
  81. if (categories) {
  82. valueGroup.valueOptions.categories = parseCategories(categories, parsers[i + 1])
  83. }
  84. });
  85. if (argumentCategories) {
  86. groupsData.argumentOptions.categories = parseCategories(argumentCategories, parsers[0])
  87. }
  88. }
  89. function filterForLogAxis(val, field, incidentOccurred) {
  90. if (val <= 0 && null !== val) {
  91. incidentOccurred("E2004", [field]);
  92. val = null
  93. }
  94. return val
  95. }
  96. function eigen(x) {
  97. return x
  98. }
  99. function getType(unit, type) {
  100. var result = type;
  101. if (type === STRING || _isString(unit)) {
  102. result = STRING
  103. } else {
  104. if (type === DATETIME || _isDate(unit)) {
  105. result = DATETIME
  106. } else {
  107. if (_isNumber(unit)) {
  108. result = NUMERIC
  109. }
  110. }
  111. }
  112. return result
  113. }
  114. function correctAxisType(type, axisType, hasCategories, incidentOccurred) {
  115. if (type === STRING && (axisType === CONTINUOUS || axisType === LOGARITHMIC || axisType === SEMIDISCRETE)) {
  116. incidentOccurred("E2002")
  117. }
  118. return axisType === LOGARITHMIC ? LOGARITHMIC : hasCategories || axisType === DISCRETE || type === STRING ? DISCRETE : axisType === SEMIDISCRETE ? SEMIDISCRETE : CONTINUOUS
  119. }
  120. function validUnit(unit, field, incidentOccurred) {
  121. if (unit) {
  122. incidentOccurred(!_isNumber(unit) && !_isDate(unit) && !_isString(unit) ? "E2003" : "E2004", [field])
  123. }
  124. }
  125. function createParserUnit(type, axisType, incidentOccurred) {
  126. var parser = type ? _getParser(type) : eigen;
  127. var filter = axisType === LOGARITHMIC ? filterForLogAxis : eigen;
  128. var filterInfinity = axisType !== DISCRETE ? function(x) {
  129. return isFinite(x) || void 0 === x ? x : null
  130. } : eigen;
  131. return function(unit, field) {
  132. var filterLogValues = function(x) {
  133. return filter(x, field, incidentOccurred)
  134. };
  135. var parseUnit = filterLogValues(filterInfinity(parser(unit)));
  136. if (void 0 === parseUnit) {
  137. validUnit(unit, field, incidentOccurred)
  138. }
  139. return parseUnit
  140. }
  141. }
  142. function prepareParsers(groupsData, incidentOccurred) {
  143. var argumentParser = createParserUnit(groupsData.argumentType, groupsData.argumentAxisType, incidentOccurred);
  144. var sizeParser;
  145. var valueParser;
  146. var categoryParsers = [argumentParser];
  147. var cache = {};
  148. var list = [];
  149. groupsData.groups.forEach(function(group, groupIndex) {
  150. group.series.forEach(function(series) {
  151. valueParser = createParserUnit(group.valueType, group.valueAxisType, incidentOccurred);
  152. sizeParser = createParserUnit(NUMERIC, CONTINUOUS, incidentOccurred);
  153. cache[series.getArgumentField()] = argumentParser;
  154. series.getValueFields().forEach(function(field) {
  155. categoryParsers[groupIndex + 1] = valueParser;
  156. cache[field] = valueParser
  157. });
  158. if (series.getSizeField()) {
  159. cache[series.getSizeField()] = sizeParser
  160. }
  161. })
  162. });
  163. for (var field in cache) {
  164. list.push([field, cache[field]])
  165. }
  166. list.length && parseAxisCategories(groupsData, categoryParsers);
  167. return list
  168. }
  169. function getParsedCell(cell, parsers) {
  170. var i;
  171. var ii = parsers.length;
  172. var obj = extend({}, cell);
  173. var field;
  174. var value;
  175. for (i = 0; i < ii; ++i) {
  176. field = parsers[i][0];
  177. value = cell[field];
  178. obj[field] = parsers[i][1](value, field)
  179. }
  180. return obj
  181. }
  182. function parse(data, parsers) {
  183. var parsedData = [];
  184. var i;
  185. var ii = data.length;
  186. parsedData.length = ii;
  187. for (i = 0; i < ii; ++i) {
  188. parsedData[i] = getParsedCell(data[i], parsers)
  189. }
  190. return parsedData
  191. }
  192. function findIndexByThreshold(data, valueField, threshold) {
  193. var i;
  194. var ii = data.length;
  195. var value;
  196. for (i = 0; i < ii; ++i) {
  197. value = data[i][valueField];
  198. if (_isDefined(value) && threshold > value) {
  199. break
  200. }
  201. }
  202. return i
  203. }
  204. function groupMinSlices(originalData, argumentField, valueField, smallValuesGrouping) {
  205. smallValuesGrouping = smallValuesGrouping || {};
  206. var mode = smallValuesGrouping.mode;
  207. var others = {};
  208. if (!mode || "none" === mode) {
  209. return
  210. }
  211. others[argumentField] = String(smallValuesGrouping.groupName || "others");
  212. others[valueField] = 0;
  213. var data = sortValues(originalData.slice(), false, function(a) {
  214. return a[valueField]
  215. });
  216. groupingValues(data, others, valueField, "smallValueThreshold" === mode ? findIndexByThreshold(data, valueField, smallValuesGrouping.threshold) : smallValuesGrouping.topCount);
  217. others[valueField] && originalData.push(others)
  218. }
  219. function groupPieData(data, groupsData) {
  220. var firstSeries = groupsData.groups[0] && groupsData.groups[0].series[0];
  221. var isPie = firstSeries && ("pie" === firstSeries.type || "doughnut" === firstSeries.type || "donut" === firstSeries.type);
  222. if (!isPie) {
  223. return
  224. }
  225. groupsData.groups.forEach(function(group) {
  226. group.series.forEach(function(series) {
  227. groupMinSlices(data, series.getArgumentField(), series.getValueFields()[0], series.getOptions().smallValuesGrouping)
  228. })
  229. })
  230. }
  231. function addUniqueItemToCollection(item, collection, itemsHash) {
  232. if (!itemsHash[item]) {
  233. collection.push(item);
  234. itemsHash[item] = true
  235. }
  236. }
  237. function getUniqueArgumentFields(groupsData) {
  238. var uniqueArgumentFields = [];
  239. var hash = {};
  240. groupsData.groups.forEach(function(group) {
  241. group.series.forEach(function(series) {
  242. addUniqueItemToCollection(series.getArgumentField(), uniqueArgumentFields, hash)
  243. })
  244. });
  245. return uniqueArgumentFields
  246. }
  247. function sort(a, b) {
  248. var result = a - b;
  249. if (isNaN(result)) {
  250. if (!_isDefined(a)) {
  251. return 1
  252. }
  253. if (!_isDefined(b)) {
  254. return -1
  255. }
  256. return 0
  257. }
  258. return result
  259. }
  260. function sortByArgument(data, argumentField) {
  261. return data.slice().sort(function(a, b) {
  262. return sort(a[argumentField], b[argumentField])
  263. })
  264. }
  265. function sortByCallback(data, callback) {
  266. return data.slice().sort(callback)
  267. }
  268. function checkValueTypeOfGroup(group, cell) {
  269. group.series.forEach(function(series) {
  270. series.getValueFields().forEach(function(field) {
  271. group.valueType = getType(cell[field], group.valueType)
  272. })
  273. });
  274. return group.valueType
  275. }
  276. function getSortByCategories(categories) {
  277. var hash = {};
  278. categories.forEach(function(value, i) {
  279. hash[value] = i
  280. });
  281. return function(data, argumentField) {
  282. return sortValues(data.slice(), true, function(a) {
  283. return hash[a[argumentField]]
  284. })
  285. }
  286. }
  287. function sortData(data, groupsData, options, uniqueArgumentFields) {
  288. var dataByArguments = {};
  289. var isDiscrete = groupsData.argumentAxisType === DISCRETE;
  290. var userCategories = isDiscrete && groupsData.argumentOptions && groupsData.argumentOptions.categories;
  291. var sortFunction = function(data) {
  292. return data
  293. };
  294. var sortingMethodOption = options.sortingMethod;
  295. var reSortCategories;
  296. if (!userCategories && _isFunction(sortingMethodOption)) {
  297. data = sortByCallback(data, sortingMethodOption)
  298. }
  299. if (isDiscrete) {
  300. groupsData.categories = getCategories(data, uniqueArgumentFields, userCategories)
  301. }
  302. if (userCategories || !_isFunction(sortingMethodOption) && groupsData.argumentType === STRING && !options._skipArgumentSorting) {
  303. sortFunction = getSortByCategories(groupsData.categories)
  304. } else {
  305. if (true === sortingMethodOption && groupsData.argumentType !== STRING) {
  306. sortFunction = sortByArgument;
  307. reSortCategories = isDiscrete
  308. }
  309. }
  310. uniqueArgumentFields.forEach(function(field) {
  311. dataByArguments[field] = sortFunction(data, field)
  312. });
  313. if (reSortCategories) {
  314. groupsData.categories = groupsData.categories.sort(sort)
  315. }
  316. return dataByArguments
  317. }
  318. function checkItemExistence(collection, item) {
  319. return collection.map(function(collectionItem) {
  320. return collectionItem.valueOf()
  321. }).indexOf(item.valueOf()) === -1
  322. }
  323. function getCategories(data, uniqueArgumentFields, userCategories) {
  324. var categories = userCategories ? userCategories.slice() : [];
  325. uniqueArgumentFields.forEach(function(field) {
  326. data.forEach(function(item) {
  327. var dataItem = item[field];
  328. _isDefined(dataItem) && checkItemExistence(categories, dataItem) && categories.push(dataItem)
  329. })
  330. });
  331. return categories
  332. }
  333. function checkArgumentTypeOfGroup(series, cell, groupsData) {
  334. series.forEach(function(currentSeries) {
  335. groupsData.argumentType = getType(cell[currentSeries.getArgumentField()], groupsData.argumentType)
  336. });
  337. return groupsData.argumentType
  338. }
  339. function checkType(data, groupsData, checkTypeForAllData) {
  340. var groupsWithUndefinedValueType = [];
  341. var groupsWithUndefinedArgumentType = [];
  342. var argumentTypeGroup = groupsData.argumentOptions && axisTypeParser(groupsData.argumentOptions.argumentType);
  343. var groupsIndexes;
  344. groupsData.groups.forEach(function(group) {
  345. if (!group.series.length) {
  346. return
  347. }
  348. var valueTypeGroup = group.valueOptions && axisTypeParser(group.valueOptions.valueType);
  349. group.valueType = valueTypeGroup;
  350. groupsData.argumentType = argumentTypeGroup;
  351. !valueTypeGroup && groupsWithUndefinedValueType.push(group);
  352. !argumentTypeGroup && groupsWithUndefinedArgumentType.push(group)
  353. });
  354. if (groupsWithUndefinedValueType.length || groupsWithUndefinedArgumentType.length) {
  355. groupsIndexes = groupsWithUndefinedValueType.map(function(_, index) {
  356. return index
  357. });
  358. data.some(function(cell) {
  359. var defineArg;
  360. groupsWithUndefinedValueType.forEach(function(group, groupIndex) {
  361. if (checkValueTypeOfGroup(group, cell) && groupsIndexes.indexOf(groupIndex) >= 0) {
  362. groupsIndexes.splice(groupIndex, 1)
  363. }
  364. });
  365. if (!defineArg) {
  366. groupsWithUndefinedArgumentType.forEach(function(group) {
  367. defineArg = checkArgumentTypeOfGroup(group.series, cell, groupsData)
  368. })
  369. }
  370. if (!checkTypeForAllData && defineArg && 0 === groupsIndexes.length) {
  371. return true
  372. }
  373. })
  374. }
  375. }
  376. function checkAxisType(groupsData, incidentOccurred) {
  377. var argumentOptions = groupsData.argumentOptions || {};
  378. var userArgumentCategories = argumentOptions && argumentOptions.categories || [];
  379. var argumentAxisType = correctAxisType(groupsData.argumentType, argumentOptions.type, !!userArgumentCategories.length, incidentOccurred);
  380. groupsData.groups.forEach(function(group) {
  381. var valueOptions = group.valueOptions || {};
  382. var valueCategories = valueOptions.categories || [];
  383. var valueAxisType = correctAxisType(group.valueType, valueOptions.type, !!valueCategories.length, incidentOccurred);
  384. group.series.forEach(function(series) {
  385. var optionsSeries = {};
  386. optionsSeries.argumentAxisType = argumentAxisType;
  387. optionsSeries.valueAxisType = valueAxisType;
  388. groupsData.argumentAxisType = groupsData.argumentAxisType || optionsSeries.argumentAxisType;
  389. group.valueAxisType = group.valueAxisType || optionsSeries.valueAxisType;
  390. optionsSeries.argumentType = groupsData.argumentType;
  391. optionsSeries.valueType = group.valueType;
  392. optionsSeries.showZero = valueOptions.showZero;
  393. series.updateDataType(optionsSeries)
  394. });
  395. group.valueAxisType = group.valueAxisType || valueAxisType;
  396. if (group.valueAxis) {
  397. group.valueAxis.setTypes(group.valueAxisType, group.valueType, VALUE_TYPE);
  398. group.valueAxis.validate()
  399. }
  400. });
  401. groupsData.argumentAxisType = groupsData.argumentAxisType || argumentAxisType;
  402. if (groupsData.argumentAxes) {
  403. groupsData.argumentAxes.forEach(function(axis) {
  404. axis.setTypes(groupsData.argumentAxisType, groupsData.argumentType, ARGUMENT_TYPE);
  405. axis.validate()
  406. })
  407. }
  408. }
  409. function verifyData(source, incidentOccurred) {
  410. var data = [];
  411. var sourceIsDefined = _isDefined(source);
  412. var hasError = sourceIsDefined && !_isArray(source);
  413. var i;
  414. var ii;
  415. var k;
  416. var item;
  417. if (sourceIsDefined && !hasError) {
  418. for (i = 0, ii = source.length, k = 0; i < ii; ++i) {
  419. item = source[i];
  420. if (_isObject(item)) {
  421. data[k++] = item
  422. } else {
  423. if (item) {
  424. hasError = true
  425. }
  426. }
  427. }
  428. }
  429. if (hasError) {
  430. incidentOccurred("E2001")
  431. }
  432. return data
  433. }
  434. function validateData(data, groupsData, incidentOccurred, options) {
  435. data = verifyData(data, incidentOccurred);
  436. groupsData.argumentType = groupsData.argumentAxisType = null;
  437. processGroups(groupsData.groups);
  438. resetArgumentAxes(groupsData.argumentAxes);
  439. checkType(data, groupsData, options.checkTypeForAllData);
  440. checkAxisType(groupsData, incidentOccurred);
  441. if (options.convertToAxisDataType) {
  442. data = parse(data, prepareParsers(groupsData, incidentOccurred))
  443. }
  444. groupPieData(data, groupsData);
  445. var dataByArgumentFields = sortData(data, groupsData, options, getUniqueArgumentFields(groupsData));
  446. return dataByArgumentFields
  447. }
  448. exports.validateData = validateData;