@provoly/dashboard 0.15.8 → 0.15.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/esm2022/admin/components/admin-classes/admin-classes-customize/tooltip/admin-classes-customize-tooltip.component.mjs +7 -6
  2. package/esm2022/admin/components/admin-classes/admin-classes-form/admin-classes-form.component.mjs +5 -4
  3. package/esm2022/admin/components/admin-classes/admin-classes-view/admin-attributes-form/admin-attributes-form.component.mjs +3 -3
  4. package/esm2022/admin/components/admin-dataset/shared/admin-form-dataset/admin-form-dataset.component.mjs +2 -2
  5. package/esm2022/admin/i18n/fr.translations.mjs +2 -2
  6. package/esm2022/dataset/i18n/fr.translations.mjs +2 -2
  7. package/esm2022/filters/date/date-filter.component.mjs +3 -3
  8. package/esm2022/lib/core/auth/geoAuth.service.mjs +8 -15
  9. package/esm2022/lib/core/components/modal-status/modal-status.component.mjs +2 -2
  10. package/esm2022/lib/core/model/dataset.interface.mjs +1 -1
  11. package/esm2022/lib/core/model/filter.interface.mjs +1 -1
  12. package/esm2022/lib/core/model/widget-map-manifest.interface.mjs +1 -1
  13. package/esm2022/lib/core/public-api.mjs +2 -2
  14. package/esm2022/lib/core/store/aggregation/frontend-aggregation/aggregation-utils.class.mjs +184 -0
  15. package/esm2022/lib/core/store/aggregation/frontend-aggregation/frontend-aggregation.service.mjs +93 -0
  16. package/esm2022/lib/dashboard/store/dashboard.selectors.mjs +2 -2
  17. package/esm2022/presentation/components/presentation.component.mjs +3 -3
  18. package/esm2022/presentation/i18n/en.translations.mjs +3 -2
  19. package/esm2022/presentation/i18n/fr.translations.mjs +6 -5
  20. package/esm2022/presentation/style/css.component.mjs +2 -2
  21. package/esm2022/restitution/i18n/fr.translations.mjs +2 -2
  22. package/esm2022/restitution/style/css.component.mjs +2 -2
  23. package/esm2022/tooltips/attribute/attribute-tooltip.component.mjs +19 -10
  24. package/esm2022/widgets/widget-map/component/widget-map.component.mjs +18 -11
  25. package/esm2022/widgets/widget-map/utils/widget-map.utils.mjs +36 -1
  26. package/esm2022/widgets/widget-table/component/widget-table.component.mjs +3 -3
  27. package/fesm2022/provoly-dashboard-admin.mjs +14 -12
  28. package/fesm2022/provoly-dashboard-admin.mjs.map +1 -1
  29. package/fesm2022/provoly-dashboard-dataset.mjs +1 -1
  30. package/fesm2022/provoly-dashboard-dataset.mjs.map +1 -1
  31. package/fesm2022/provoly-dashboard-filters-date.mjs +2 -2
  32. package/fesm2022/provoly-dashboard-filters-date.mjs.map +1 -1
  33. package/fesm2022/provoly-dashboard-presentation.mjs +11 -9
  34. package/fesm2022/provoly-dashboard-presentation.mjs.map +1 -1
  35. package/fesm2022/provoly-dashboard-restitution.mjs +3 -3
  36. package/fesm2022/provoly-dashboard-restitution.mjs.map +1 -1
  37. package/fesm2022/provoly-dashboard-tooltips-attribute.mjs +18 -9
  38. package/fesm2022/provoly-dashboard-tooltips-attribute.mjs.map +1 -1
  39. package/fesm2022/provoly-dashboard-widgets-widget-map.mjs +52 -10
  40. package/fesm2022/provoly-dashboard-widgets-widget-map.mjs.map +1 -1
  41. package/fesm2022/provoly-dashboard-widgets-widget-table.mjs +2 -2
  42. package/fesm2022/provoly-dashboard-widgets-widget-table.mjs.map +1 -1
  43. package/fesm2022/provoly-dashboard.mjs +237 -179
  44. package/fesm2022/provoly-dashboard.mjs.map +1 -1
  45. package/lib/core/auth/geoAuth.service.d.ts +3 -1
  46. package/lib/core/model/dataset.interface.d.ts +1 -1
  47. package/lib/core/model/filter.interface.d.ts +1 -1
  48. package/lib/core/model/widget-map-manifest.interface.d.ts +1 -0
  49. package/lib/core/public-api.d.ts +1 -1
  50. package/lib/core/store/aggregation/frontend-aggregation/aggregation-utils.class.d.ts +33 -0
  51. package/lib/core/store/aggregation/frontend-aggregation/frontend-aggregation.service.d.ts +22 -0
  52. package/lib/dashboard/store/dashboard.selectors.d.ts +1 -1
  53. package/package.json +30 -30
  54. package/presentation/i18n/en.translations.d.ts +1 -0
  55. package/presentation/i18n/fr.translations.d.ts +1 -0
  56. package/presentation/style/_o-pry-presentation.scss +7 -2
  57. package/tooltips/attribute/attribute-tooltip.component.d.ts +5 -1
  58. package/widgets/widget-map/component/widget-map.component.d.ts +3 -4
  59. package/widgets/widget-map/utils/widget-map.utils.d.ts +1 -0
  60. package/esm2022/lib/core/store/aggregation/frontend-aggregation.service.mjs +0 -210
  61. package/lib/core/store/aggregation/frontend-aggregation.service.d.ts +0 -33
@@ -2264,7 +2264,7 @@ const frTranslations = {
2264
2264
  WARNING: 'Warning'
2265
2265
  },
2266
2266
  code: {
2267
- UNRECOGNIZED: 'Attribut{{plural}} {{name}} non reconnu{{plural}}',
2267
+ UNRECOGNIZED: 'Attribut {{plural}} {{name}} non reconnu {{plural}}',
2268
2268
  FORMAT: "Format d'attribut {{name}} incorrect",
2269
2269
  STORAGE: "Erreur d'insertion pour l'élément {{recordId}}",
2270
2270
  NO_VALUES: 'Pas de valeur pour la ligne {{recordId}}',
@@ -2573,7 +2573,7 @@ class PryModalStatusComponent {
2573
2573
  .flat()
2574
2574
  .map((preview) => ({
2575
2575
  ...preview,
2576
- name: preview.name.split(',').join(', ')
2576
+ name: (preview.name ?? '').split(',').join(', ')
2577
2577
  }))));
2578
2578
  this.messageCount$ = this.store.select(DataSourceSelectors.datasetPreviews).pipe(map((previews) => {
2579
2579
  return previews.map((preview) => preview.count).reduce((p, c) => p + c, 0);
@@ -3856,7 +3856,7 @@ const namedQueriesUses = createSelector(globalManifest, (manifest) => manifest.w
3856
3856
  const presentation = createSelector(feature$4, (state) => state?.presentation);
3857
3857
  const isCurrentPresentationModified = createSelector(presentation, globalManifest, (state, global) => !equal(global, state.initial));
3858
3858
  const isCurrentPresentationOwner = createSelector(presentation, (state) => !!state.current && state.current.owner);
3859
- const filters = createSelector(feature$4, (state) => state?.manifests.manifest.filters ?? {});
3859
+ const filters = createSelector(feature$4, (state) => state?.manifests.manifest.filters ?? []);
3860
3860
  const datasourceFilters = createSelector(feature$4, (state) => state?.manifests.manifest.filters
3861
3861
  ? state?.manifests.manifest.filters.reduce((obj, filter) => (filter.attributes.forEach((attribute) => (obj[attribute.datasource] || (obj[attribute.datasource] = [])).push({
3862
3862
  attribute: attribute.id,
@@ -4884,29 +4884,21 @@ class PryGeoAuthService {
4884
4884
  }
4885
4885
  const PRY_GEOAUTH_TOKEN = new InjectionToken('PRY_GEOAUTH_TOKEN');
4886
4886
  class PryDefaultGeoAuthService extends PryGeoAuthService {
4887
- constructor() {
4887
+ constructor(httpClient) {
4888
4888
  super();
4889
+ this.httpClient = httpClient;
4889
4890
  }
4890
4891
  customLoader(tile, src) {
4891
- const xhr = new XMLHttpRequest();
4892
- xhr.responseType = 'blob';
4893
- xhr.addEventListener('loadend', function (evt) {
4894
- const data = this.response;
4895
- if (data !== undefined) {
4896
- tile.getImage().src = URL.createObjectURL(data);
4897
- }
4892
+ this.httpClient.get(src, { observe: 'response', params: {}, responseType: 'blob' }).subscribe((blob) => {
4893
+ tile.getImage().src = URL.createObjectURL(blob.body);
4898
4894
  });
4899
- xhr.open('GET', src);
4900
- // Exemple of how to pass authentification header
4901
- // client.setRequestHeader('Authorization', 'Basic ' + window.btoa('user:pass'));
4902
- xhr.send();
4903
4895
  }
4904
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: PryDefaultGeoAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4896
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: PryDefaultGeoAuthService, deps: [{ token: i1$2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
4905
4897
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: PryDefaultGeoAuthService }); }
4906
4898
  }
4907
4899
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: PryDefaultGeoAuthService, decorators: [{
4908
4900
  type: Injectable
4909
- }], ctorParameters: function () { return []; } });
4901
+ }], ctorParameters: function () { return [{ type: i1$2.HttpClient }]; } });
4910
4902
 
4911
4903
  const SELECTORS = [
4912
4904
  TranslateIdPipe,
@@ -5293,195 +5285,261 @@ class ResultsetUtils {
5293
5285
  }
5294
5286
  }
5295
5287
 
5296
- class PryFrontendAggregationService extends PryAggregationService {
5297
- constructor(store) {
5298
- super();
5299
- this.store = store;
5288
+ class AggregationUtils {
5289
+ static getAxisAttribute(classes, options, axis) {
5290
+ return classes
5291
+ .map((clazz) => clazz.attributes.find((attr) => attr.id === options[axis].attribute))
5292
+ .find((attr) => !!attr)?.name;
5300
5293
  }
5301
- // @ts-ignore
5302
- aggregate(datasource, options) {
5303
- return combineLatest([
5304
- this.store.select(DashboardSelectors.resultSets),
5305
- this.store.select(ClassSelectors.classes)
5306
- ]).pipe(map(([resultSets, classes]) => {
5307
- const abscissa = classes
5308
- .map((clazz) => clazz.attributes.find((attr) => attr.id === options.abscissa.attribute))
5309
- .find((attr) => !!attr)?.name;
5310
- const ordinate = classes
5311
- .map((clazz) => clazz.attributes.find((attr) => attr.id === options.ordinate.attribute))
5312
- .find((attr) => !!attr)?.name;
5313
- if (!abscissa || !ordinate) {
5314
- throw new Error('abscissaorodinateinvalid');
5315
- }
5316
- let data = datasource.map((rsName) => resultSets[rsName ?? '']).filter((rsName) => !!rsName);
5317
- const resultSet = data.reduce((rs1, rs2) => ResultsetUtils.mergeResultSets(rs1, rs2), {
5318
- items: {},
5319
- relations: [],
5320
- merged: data.length
5321
- });
5322
- let items = Object.keys(resultSet.items)
5323
- .map((classId) => resultSet.items[classId])
5324
- .reduce((prev, curr) => [...prev, ...curr], []);
5325
- if (!!options.groupBy) {
5326
- const groupBy = classes
5327
- .map((clazz) => clazz.attributes.find((attr) => attr.id === options.groupBy?.attribute))
5328
- .find((attr) => !!attr)?.name;
5329
- if (!groupBy) {
5330
- throw new Error('groupByAttributeisinvalid');
5331
- }
5332
- const itemsGrouped = this.groupBy(items, groupBy);
5333
- const values = [];
5334
- Object.keys(itemsGrouped).forEach((key) => {
5335
- values.push({
5336
- key,
5337
- groupBy: this.getValueFromOperation(itemsGrouped[key], options.ordinate.operation, abscissa, ordinate, options.abscissa.limit)
5338
- });
5339
- });
5340
- return { operation: options.ordinate.operation, values: values };
5341
- }
5342
- const values = this.getValueFromOperation(items, options.ordinate.operation, abscissa, ordinate, options.abscissa.limit);
5343
- return { operation: options.ordinate.operation, values: values };
5344
- }));
5294
+ static getChartDatasourceResultSet(datasources, resultSets) {
5295
+ return datasources
5296
+ .filter((rsName) => rsName in resultSets)
5297
+ .map((rsName) => resultSets[rsName])
5298
+ .reduce((rs1, rs2) => ResultsetUtils.mergeResultSets(rs1, rs2), {
5299
+ items: {},
5300
+ relations: [],
5301
+ merged: datasources.filter((rsName) => rsName in resultSets).length
5302
+ });
5345
5303
  }
5346
- getValueFromOperation(items, operation, abscissa, ordinate, limit) {
5347
- let result = [];
5348
- const groupByAbscissa = this.groupBy(items, abscissa);
5349
- groupByAbscissa.map((test) => console.log(groupByAbscissa[test]));
5304
+ static getAggregatedValue(operation, groupedItems, abscissaAttribute, ordinateAttribute) {
5305
+ return this.getAggregatedValueForOperation(operation, groupedItems[abscissaAttribute].map((item) => this.getValueFromAttribute(item, ordinateAttribute).value));
5306
+ }
5307
+ static getValueFromAttribute(item, attribute) {
5308
+ let value = ItemUtils.getAttributeValue(item, attribute);
5309
+ return Array.isArray(value) ? value[0] : value;
5310
+ }
5311
+ static getAggregatedValueForOperation(operation, values) {
5350
5312
  switch (operation) {
5351
5313
  case Operation.COUNT:
5352
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5353
- key: key,
5354
- value: groupByAbscissa[key].length
5355
- })));
5356
- break;
5314
+ return values.length;
5357
5315
  case Operation.SUM:
5358
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5359
- key: key,
5360
- value: this.getSum(groupByAbscissa[key], ordinate)
5361
- })));
5362
- break;
5316
+ return values.reduce((acc, val) => acc + val, 0);
5363
5317
  case Operation.DISTINCT:
5364
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5365
- key: key,
5366
- value: this.getDistinct(groupByAbscissa[key], ordinate)
5367
- })));
5368
- break;
5318
+ return [...new Set(values)].length;
5369
5319
  case Operation.AVERAGE:
5370
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5371
- key: key,
5372
- value: this.getAverage(groupByAbscissa[key], ordinate)
5373
- })));
5374
- break;
5320
+ return values.reduce((acc, val) => acc + val, 0) / values.length;
5375
5321
  case Operation.MEDIAN:
5376
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5377
- key: key,
5378
- value: this.getQuartile(groupByAbscissa[key], ordinate, 0.5)
5379
- })));
5380
- break;
5322
+ return this.getQuartile(values, 0.5);
5381
5323
  case Operation.Q1:
5382
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5383
- key: key,
5384
- value: this.getQuartile(groupByAbscissa[key], ordinate, 0.25)
5385
- })));
5386
- break;
5324
+ return this.getQuartile(values, 0.25);
5387
5325
  case Operation.Q3:
5388
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5389
- key: key,
5390
- value: this.getQuartile(groupByAbscissa[key], ordinate, 0.75)
5391
- })));
5392
- break;
5326
+ return this.getQuartile(values, 0.75);
5393
5327
  case Operation.MIN:
5394
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5395
- key: key,
5396
- value: this.getMin(groupByAbscissa[key], ordinate)
5397
- })));
5398
- break;
5328
+ return Math.min(...values);
5329
+ case Operation.MAX:
5330
+ return Math.max(...values);
5399
5331
  default:
5400
- result.push(...Object.keys(groupByAbscissa).map((key) => ({
5401
- key: key,
5402
- value: this.getMax(groupByAbscissa[key], ordinate)
5403
- })));
5404
- break;
5332
+ return -1;
5405
5333
  }
5406
- if (limit) {
5407
- result = this.formatByLimit(result, limit);
5334
+ }
5335
+ static getQuartile(values, q) {
5336
+ if (values.length === 0)
5337
+ return 0;
5338
+ values.sort((a, b) => a - b);
5339
+ let pos = (values.length - 1) * q;
5340
+ if (pos % 1 === 0) {
5341
+ return values[pos];
5408
5342
  }
5409
- return result;
5343
+ pos = Math.floor(pos);
5344
+ return values[pos + 1] !== undefined ? (values[pos] + values[pos + 1]) / 2 : values[pos];
5410
5345
  }
5411
- groupBy(items, attribute) {
5412
- let distincts = [];
5413
- items.forEach((item) => {
5414
- let value = this.getValueFromAttribute(item, attribute);
5415
- if (distincts[value.value]) {
5416
- distincts[value.value].push(item);
5346
+ static formatByLimit(data, limit, operation) {
5347
+ if (!limit.isTimeLimit) {
5348
+ data
5349
+ .sort((a, b) => (limit.order === 'asc' ? (a.value > b.value ? 1 : -1) : a.value < b.value ? 1 : -1))
5350
+ .splice(limit.at);
5351
+ return data;
5352
+ }
5353
+ return this.formatDataByTimeLimit(data, limit, operation);
5354
+ }
5355
+ static formatDataByTimeLimit(data, limit, operation) {
5356
+ let distinctValues = [];
5357
+ data
5358
+ .map((obj) => ({ ...obj, key: this.getKeyValueForTimeInterval(obj.key, limit.interval) }))
5359
+ .forEach((obj) => {
5360
+ const isNotDistinct = distinctValues.find((distinct) => obj.key === distinct.key);
5361
+ if (!!isNotDistinct) {
5362
+ isNotDistinct.values.push(obj.value);
5417
5363
  }
5418
5364
  else {
5419
- distincts[value.value] = [item];
5365
+ distinctValues.push({ key: obj.key, values: [] });
5420
5366
  }
5421
5367
  });
5422
- return distincts;
5423
- }
5424
- getValueFromAttribute(item, attribute) {
5425
- let value = ItemUtils.getAttributeValue(item, attribute);
5426
- return Array.isArray(value) ? value[0] : value;
5427
- }
5428
- getSum(items, attribute) {
5429
- let sum = 0;
5430
- items.forEach((item) => {
5431
- sum += this.getValueFromAttribute(item, attribute).value;
5432
- });
5433
- return sum;
5368
+ return distinctValues.map((obj) => ({
5369
+ ...obj,
5370
+ value: AggregationUtils.getAggregatedValueForOperation(operation, obj.values)
5371
+ }));
5434
5372
  }
5435
- getMin(items, attribute) {
5436
- const result = items.reduce((prev, curr) => {
5437
- const prevValue = this.getValueFromAttribute(prev, attribute).value;
5438
- const currValue = this.getValueFromAttribute(curr, attribute).value;
5439
- return prevValue < currValue ? prev : curr;
5440
- });
5441
- return this.getValueFromAttribute(result, attribute).value;
5373
+ static getKeyValueForTimeInterval(key, interval) {
5374
+ const date = new Date(key);
5375
+ switch (interval) {
5376
+ case 'second':
5377
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()).toISOString();
5378
+ case 'minute':
5379
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes()).toISOString();
5380
+ case 'hour':
5381
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours()).toISOString();
5382
+ case 'day':
5383
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString();
5384
+ case 'week':
5385
+ const daysToSubtract = (7 + date.getUTCDay()) % 7;
5386
+ date.setUTCDate(date.getUTCDate() - daysToSubtract);
5387
+ return date.toISOString();
5388
+ case 'quarter':
5389
+ const quarterStartMonths = [0, 3, 6, 9]; // January, April, July, October
5390
+ const quarter = Math.floor(date.getUTCMonth() / 3);
5391
+ const year = date.getUTCFullYear();
5392
+ const startOfQuarter = new Date(Date.UTC(year, quarterStartMonths[quarter], 1, 0, 0, 0, 0));
5393
+ return startOfQuarter.toISOString();
5394
+ case 'year':
5395
+ return new Date(new Date(key).getFullYear(), 0, 1).toISOString();
5396
+ default:
5397
+ return date.toISOString();
5398
+ }
5442
5399
  }
5443
- getMax(items, attribute) {
5444
- const result = items.reduce((prev, curr) => {
5445
- const prevValue = this.getValueFromAttribute(prev, attribute).value;
5446
- const currValue = this.getValueFromAttribute(curr, attribute).value;
5447
- return prevValue > currValue ? prev : curr;
5448
- });
5449
- return this.getValueFromAttribute(result, attribute).value;
5400
+ static filterItems(items, filters) {
5401
+ let filtersByAttribute = filters
5402
+ .map((filter) => filter.attributes.map((attribute) => ({
5403
+ ...filter,
5404
+ attribute: attribute.id,
5405
+ attributes: undefined
5406
+ })))
5407
+ .flat();
5408
+ return filters.length > 0
5409
+ ? [
5410
+ ...new Set(filtersByAttribute
5411
+ .map((filter) => items.filter((item) => this.doesItemValuePassFilter(item, filter.attribute, filter.operator, filter.value)))
5412
+ .flat())
5413
+ ]
5414
+ : items;
5415
+ }
5416
+ static doesItemValuePassFilter(item, attribute, operator, filterValue) {
5417
+ let value = AggregationUtils.getValueFromAttribute(item, attribute)?.value;
5418
+ let date1, date2;
5419
+ console.log(operator);
5420
+ switch (operator) {
5421
+ case 'EQUALS':
5422
+ return value === filterValue;
5423
+ case 'NOT_EQUALS':
5424
+ return value !== filterValue;
5425
+ case 'CONTAINS':
5426
+ return value.includes(filterValue);
5427
+ case 'START_WITH':
5428
+ return value.startsWith(filterValue);
5429
+ case 'END_WITH':
5430
+ return value.endsWith(filterValue);
5431
+ case 'GREATER_THAN':
5432
+ if (typeof filterValue === 'number') {
5433
+ return value > filterValue;
5434
+ }
5435
+ else if (typeof filterValue === 'string') {
5436
+ return new Date(value) > new Date(filterValue);
5437
+ }
5438
+ else {
5439
+ return false;
5440
+ }
5441
+ case 'LOWER_THAN':
5442
+ if (typeof filterValue === 'number') {
5443
+ return value < filterValue;
5444
+ }
5445
+ else if (typeof filterValue === 'string') {
5446
+ return new Date(value) < new Date(filterValue);
5447
+ }
5448
+ else {
5449
+ return false;
5450
+ }
5451
+ case 'INSIDE':
5452
+ [date1, date2] = [
5453
+ new Date(filterValue.split(',')[0]),
5454
+ new Date(filterValue.split(',')[1])
5455
+ ];
5456
+ return new Date(value) > new Date(date1) && new Date(value) < new Date(date2);
5457
+ case 'OUTSIDE':
5458
+ [date1, date2] = [
5459
+ new Date(filterValue.split(',')[0]),
5460
+ new Date(filterValue.split(',')[1])
5461
+ ];
5462
+ return new Date(value) < new Date(date1) && new Date(value) > new Date(date2);
5463
+ default:
5464
+ return true;
5465
+ }
5450
5466
  }
5451
- getAverage(items, attribute) {
5452
- const allValues = items.map((item) => this.getValueFromAttribute(item, attribute).value);
5453
- return allValues.reduce((acc, val) => acc + val, 0) / allValues.length;
5467
+ }
5468
+
5469
+ class PryFrontendAggregationService extends PryAggregationService {
5470
+ constructor(store) {
5471
+ super();
5472
+ this.store = store;
5454
5473
  }
5455
- getDistinct(items, attribute) {
5456
- return [...new Set(items.map((item) => this.getValueFromAttribute(item, attribute).value))].length;
5474
+ aggregate(datasources, options) {
5475
+ return combineLatest([
5476
+ this.store.select(DashboardSelectors.resultSets),
5477
+ this.store.select(ClassSelectors.classes),
5478
+ this.store.select(DashboardSelectors.filters)
5479
+ ]).pipe(map(([resultSets, classes, filters]) => {
5480
+ const resultSet = AggregationUtils.getChartDatasourceResultSet(datasources, resultSets);
5481
+ let items = Object.keys(resultSet.items)
5482
+ .map((classId) => resultSet.items[classId])
5483
+ .reduce((prev, curr) => [...prev, ...curr], []);
5484
+ items = AggregationUtils.filterItems(items, filters);
5485
+ return this.getAggregationResult(options, classes, items);
5486
+ }));
5457
5487
  }
5458
- getQuartile(items, attribute, q) {
5459
- const allValues = items.map((item) => this.getValueFromAttribute(item, attribute).value);
5460
- const allValuesSorted = allValues.sort((a, b) => a - b);
5461
- let pos = (allValuesSorted.length - 1) * q;
5462
- if (pos % 1 === 0) {
5463
- return allValuesSorted[pos];
5488
+ getAggregationResult(options, classes, items) {
5489
+ const [abscissa, ordinate] = [
5490
+ AggregationUtils.getAxisAttribute(classes, options, 'abscissa') ?? '',
5491
+ AggregationUtils.getAxisAttribute(classes, options, 'ordinate') ?? ''
5492
+ ];
5493
+ const aggregationResult = {
5494
+ operation: options.ordinate.operation,
5495
+ values: []
5496
+ };
5497
+ if (!options.groupBy) {
5498
+ if (!!options.ordinate.operation)
5499
+ console.warn('no ordinate operation selected');
5500
+ aggregationResult.values = this.getChartDataBasedOnOperation(items, options.ordinate.operation, abscissa, ordinate, options.abscissa.limit);
5501
+ console.log(aggregationResult.values);
5464
5502
  }
5465
- pos = Math.floor(pos);
5466
- if (allValuesSorted[pos + 1] !== undefined) {
5467
- return (allValuesSorted[pos] + allValuesSorted[pos + 1]) / 2;
5503
+ else {
5504
+ const groupByAttribute = classes
5505
+ .map((clazz) => clazz.attributes.find((attr) => attr.id === options.groupBy?.attribute))
5506
+ .find((attr) => !!attr)?.name ?? '';
5507
+ const itemsGrouped = this.getItemsGroupedByAttributeValue(items, groupByAttribute);
5508
+ aggregationResult.values = Object.keys(itemsGrouped).map((key) => ({
5509
+ key,
5510
+ groupBy: this.getChartDataBasedOnOperation(itemsGrouped[key], options.ordinate.operation, abscissa, ordinate, options.abscissa.limit)
5511
+ }));
5468
5512
  }
5469
- return allValuesSorted[pos];
5513
+ return aggregationResult;
5470
5514
  }
5471
- getPercentile(items, attribute, q) {
5472
- const allValues = items.map((item) => this.getValueFromAttribute(item, attribute).value);
5473
- const allValuesSorted = allValues.sort((a, b) => a - b);
5474
- return ((100 * allValuesSorted.reduce((acc, v) => acc + (v < q ? 1 : 0) + (v === q ? 0.5 : 0), 0)) /
5475
- allValuesSorted.length);
5476
- }
5477
- formatByLimit(data, limit) {
5478
- if (!limit.isTimeLimit) {
5479
- data
5480
- .sort((a, b) => (limit.order === 'asc' ? (a.value > b.value ? 1 : -1) : a.value < b.value ? 1 : -1))
5481
- .splice(limit.at);
5482
- return data;
5483
- }
5484
- return data;
5515
+ getItemsGroupedByAttributeValue(items, attribute) {
5516
+ if (attribute === '')
5517
+ console.warn('groupBy attribute not found');
5518
+ let distinctValues = {};
5519
+ items.forEach((item) => {
5520
+ let value = AggregationUtils.getValueFromAttribute(item, attribute)?.value;
5521
+ if (!!value)
5522
+ distinctValues[value] = [...(distinctValues[value] ?? []), item];
5523
+ });
5524
+ return distinctValues;
5525
+ }
5526
+ // get { key, value } object list that will be our data to show in the chart
5527
+ getChartDataBasedOnOperation(items, operation, abscissa, ordinate, limit) {
5528
+ if (abscissa === '' || ordinate === '')
5529
+ console.warn('abscissa or ordinate attribute not found');
5530
+ const groupByAbscissa = this.getItemsGroupedByAttributeValue(items, abscissa);
5531
+ let result = [
5532
+ ...Object.keys(groupByAbscissa).map((abscissaAttribute) => {
5533
+ console.log(AggregationUtils.getAggregatedValue(operation, groupByAbscissa, abscissaAttribute, ordinate ?? ''));
5534
+ return {
5535
+ key: +abscissaAttribute || abscissaAttribute,
5536
+ value: AggregationUtils.getAggregatedValue(operation, groupByAbscissa, abscissaAttribute, ordinate ?? '')
5537
+ };
5538
+ })
5539
+ ];
5540
+ if (limit)
5541
+ result = AggregationUtils.formatByLimit(result, limit, operation);
5542
+ return result;
5485
5543
  }
5486
5544
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: PryFrontendAggregationService, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Injectable }); }
5487
5545
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: PryFrontendAggregationService, providedIn: 'root' }); }