@sis-cc/dotstatsuite-components 9.1.0 → 9.2.0

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 (80) hide show
  1. package/lib/rules/src/preparators/enhanceObservations.js +155 -0
  2. package/lib/rules/src/preparators/formatValue.js +77 -0
  3. package/lib/rules/src/preparators/getDimensions.js +55 -0
  4. package/lib/rules/src/preparators/getDisplay.js +32 -0
  5. package/lib/rules/src/preparators/getObservations.js +27 -0
  6. package/lib/rules/src/table/factories/getCells.js +1 -3
  7. package/lib/rules/src/table/factories/getLayoutData.js +102 -161
  8. package/lib/rules/src/table/factories/getSortedLayoutIndexes.js +118 -0
  9. package/lib/rules/src/table/factories/getTableData.js +12 -10
  10. package/lib/rules/src/table/factories/refineLayoutSize.js +210 -0
  11. package/lib/rules/src/table/preparators/prepareData.js +23 -23
  12. package/lib/rules/src/table/units/appendUnitsInLayoutDataEntry.js +10 -10
  13. package/lib/rules/src/table/units/getAttachmentSeriesIndexes.js +1 -1
  14. package/lib/rules/src/table/units/getUnitsSeries.js +31 -0
  15. package/lib/rules/src/v8-transformer.js +3 -2
  16. package/package.json +2 -2
  17. package/src/rules/src/preparators/enhanceObservations.js +163 -0
  18. package/src/rules/src/preparators/formatValue.js +73 -0
  19. package/src/rules/src/preparators/getDimensions.js +49 -0
  20. package/src/rules/src/preparators/getDisplay.js +20 -0
  21. package/src/rules/src/preparators/getObservations.js +19 -0
  22. package/src/rules/src/table/factories/getCells.js +3 -7
  23. package/src/rules/src/table/factories/getLayoutData.js +135 -184
  24. package/src/rules/src/table/factories/getSortedLayoutIndexes.js +108 -0
  25. package/src/rules/src/table/factories/getTableData.js +10 -10
  26. package/src/rules/src/table/factories/refineLayoutSize.js +224 -0
  27. package/src/rules/src/table/preparators/prepareData.js +23 -32
  28. package/src/rules/src/table/units/appendUnitsInLayoutDataEntry.js +15 -18
  29. package/src/rules/src/table/units/getAttachmentSeriesIndexes.js +2 -1
  30. package/src/rules/src/table/units/getUnitsSeries.js +20 -0
  31. package/src/rules/src/v8-transformer.js +4 -2
  32. package/test/appendUnitsInLayoutDataEntry.test.js +3 -3
  33. package/test/enhanceObservations.test.js +270 -0
  34. package/test/getAttachmentSeriesIndexes.test.js +1 -1
  35. package/test/getCells.test.js +4 -39
  36. package/test/getLayoutData2.test.js +194 -0
  37. package/test/getOneValueDimensions.test.js +1 -1
  38. package/test/getSortedLayoutIndexes.test.js +80 -0
  39. package/test/getUnitsArtefacts.test.js +1 -1
  40. package/test/getUnitsSeries.test.js +64 -0
  41. package/test/mocks/table-layout-multi-hierarchies--layout.json +621 -0
  42. package/test/mocks/table-layout-multi-hierarchies--layoutData.json +32410 -0
  43. package/test/mocks/table-layout-multi-hierarchies--layoutIndexes.json +2760 -0
  44. package/test/mocks/table-layout-multi-hierarchies--observations.json +30688 -0
  45. package/test/mocks/table-layout-multi-hierarchies--sizedIndexes.json +2761 -0
  46. package/test/mocks/table-layout-truncation1--layout.json +27469 -0
  47. package/test/mocks/table-layout-truncation1--layoutData.json +19413 -0
  48. package/test/mocks/table-layout-truncation1--layoutIndexes.json +7512 -0
  49. package/test/mocks/table-layout-truncation1--observations.json +70002 -0
  50. package/test/mocks/table-layout-truncation1--sizedIndexes.json +3010 -0
  51. package/test/mocks/table-prep-multi-hierarchies--attributes.json +44 -0
  52. package/test/mocks/table-prep-multi-hierarchies--dimensions.json +688 -0
  53. package/test/mocks/table-prep-multi-hierarchies--enhancedObservations.json +19696 -0
  54. package/test/mocks/table-prep-multi-hierarchies--observations.json +8246 -0
  55. package/test/mocks/table-prep-multi-hierarchies--sdmxJson.json +2985 -0
  56. package/test/mocks/table-prep-truncation1--dimensions.json +35057 -0
  57. package/test/mocks/table-prep-truncation1--enhancedObservations.json +70002 -0
  58. package/test/mocks/table-prep-truncation1--observations.json +27502 -0
  59. package/test/mocks/table-prep-truncation1--sdmxJson.json +55103 -0
  60. package/test/mocks/table-prep-units--observations.json +284286 -0
  61. package/test/mocks/table-prep-units--unitsSeries.json +41042 -0
  62. package/test/parseAttributes.test.js +1 -1
  63. package/test/refineLayoutSize.test.js +415 -0
  64. package/test/table-layout-perf.test.js +74 -0
  65. package/test/table-prep-perf.test.js +65 -0
  66. package/lib/rules/src/table/factories/getSortedLayoutData.js +0 -145
  67. package/lib/rules/src/table/factories/refineLayoutHierarchy.js +0 -61
  68. package/lib/rules/src/table/preparators/parseObservations.js +0 -76
  69. package/lib/rules/src/table/units/getUnitsSeriesOccurences.js +0 -39
  70. package/src/rules/src/table/factories/getSortedLayoutData.js +0 -133
  71. package/src/rules/src/table/factories/refineLayoutHierarchy.js +0 -64
  72. package/src/rules/src/table/preparators/parseObservations.js +0 -81
  73. package/src/rules/src/table/units/getUnitsSeriesOccurences.js +0 -51
  74. package/test/appendUnitsInLayoutData.test.js +0 -248
  75. package/test/getLayoutData.test.js +0 -311
  76. package/test/getSortedLayoutData.test.js +0 -382
  77. package/test/getUnitsSeriesOccurences.test.js +0 -133
  78. package/test/parseObservations.test.js +0 -116
  79. package/test/refineDimSeriesUnits.test.js +0 -24
  80. package/test/refineLayoutHierarchy.test.js +0 -79
@@ -0,0 +1,49 @@
1
+ import * as R from 'ramda';
2
+ import {
3
+ getCodeOrder,
4
+ isTimePeriodDimension
5
+ } from '@sis-cc/dotstatsuite-sdmxjs';
6
+
7
+ export const isDisplayedFromOwnAnnotations = artefact => {
8
+ const artefactAnnotations = R.propOr([], 'annotations', artefact);
9
+ return R.pipe(
10
+ R.propOr([], 'annotations'),
11
+ R.find(R.propEq('type', 'NOT_DISPLAYED')),
12
+ R.isNil
13
+ )(artefact);
14
+ };
15
+
16
+ // sdmxJson
17
+
18
+ export const getDimensions = sdmxJson => {
19
+ const annotations = R.pathOr([], ['data', 'structure', 'annotations'], sdmxJson);
20
+ const locale = R.path([], ['meta', 'contentLanguage'], sdmxJson);
21
+
22
+ const enhanceValues = R.addIndex(R.map)((val, __index) => {
23
+ const annotationsIndexes = R.propOr([], 'annotations', val);
24
+ const valueAnnotations = R.props(annotationsIndexes, annotations);
25
+
26
+ return ({
27
+ ...value,
28
+ __index,
29
+ annotations: valueAnnotations,
30
+ name: R.pathOr(`[${val.id}]`, ['names', locale], val),
31
+ });
32
+ });
33
+
34
+ return R.pipe(
35
+ R.pathOr([], ['data', 'structure', 'dimensions']),
36
+ R.addIndex(R.map)((dimension, __index) => {
37
+ const annotationsIndexes = R.propOr([], 'annotations', dimension);
38
+ const dimensionAnnotations = R.props(annotationsIndexes, annotations);
39
+
40
+ return ({
41
+ ...dimension,
42
+ __index,
43
+ name: R.pathOr(`[${val.id}]`, ['names', locale], val),
44
+ annotations: dimensionAnnotations,
45
+ values: enhanceValues(R.propOr([], 'values', dimension))
46
+ });
47
+ })
48
+ )(sdmxJson);
49
+ };
@@ -0,0 +1,20 @@
1
+ import * as R from 'ramda';
2
+
3
+ export const getDisplay = ({ noDisplayIndexes = [], notDisplayedCodes = {} }) => {
4
+ const annotationsValidator = R.pipe(
5
+ R.propOr([], 'annotations'),
6
+ R.intersection(noDisplayIndexes),
7
+ R.isEmpty
8
+ );
9
+
10
+ const blacklistValidator = R.ifElse(
11
+ R.has('values'),
12
+ ({ id }) => R.anyPass([
13
+ R.complement(R.has)(id),
14
+ R.hasPath([id, 'values'])
15
+ ])(notDisplayedCodes),
16
+ ({ id, parent }) => R.complement(R.hasPath)([parent, 'values', id], notDisplayedCodes)
17
+ );
18
+
19
+ return R.allPass([annotationsValidator, blacklistValidator]);
20
+ };
@@ -0,0 +1,19 @@
1
+ import * as R from 'ramda';
2
+
3
+ export const getObservations = (sdmxJson) => R.pipe(
4
+ R.pathOr({}, ['data', 'dataSets', 0, 'observations']),
5
+ R.mapObjIndexed(
6
+ (observation, observationKey) => {
7
+ const value = R.head(observation);
8
+ const attrValuesIndexes = R.tail(observation);
9
+ const dimValuesIndexes = R.split(':', observationKey);
10
+
11
+ return ({
12
+ key: observationKey,
13
+ value,
14
+ attrValuesIndexes,
15
+ dimValuesIndexes
16
+ });
17
+ },
18
+ )
19
+ )(sdmxJson);
@@ -1,5 +1,4 @@
1
1
  import * as R from 'ramda';
2
- import { getCellValue } from './getCellValue';
3
2
  import { dimensionValueDisplay } from '../../dimension-utils';
4
3
 
5
4
  const getAttrLabel = (display) => (attribute) => `${display(attribute)}: ${display(R.propOr({}, 'value', attribute))}`;
@@ -82,13 +81,10 @@ export const getCells = (data, display, customAttributes, unitsProps) => R.mapOb
82
81
  R.lensProp('flags'),
83
82
  getFlags(
84
83
  customAttributes, dimensionValueDisplay(display), unitsProps
85
- )(R.pick(['attributes', 'units'], observation))
86
- ),
87
- R.set(R.lensProp('intValue'), R.prop('value')(observation)),
88
- R.set(
89
- R.lensProp('value'),
90
- getCellValue(observation)
84
+ )(observation)
91
85
  ),
86
+ R.set(R.lensProp('intValue'), R.prop('value', observation)),
87
+ R.set(R.lensProp('value'), R.prop('formattedValue', observation)),
92
88
  R.assoc('indexedDimValIds', R.prop('indexedDimValIds', observation))
93
89
  )({}),
94
90
  R.propOr({}, 'observations', data)
@@ -1,203 +1,154 @@
1
1
  import * as R from 'ramda';
2
- import { getSortedLayoutData } from './getSortedLayoutData';
3
- import { getCuratedCells } from './getCuratedCells';
4
2
 
5
- const layoutDataToKey = R.pipe(
6
- R.addIndex(R.map)((entry, index) => ({ ...entry, f: R.path([index, 'value', '__index']) })),
7
- R.map(R.prop('f')),
8
- getters => R.converge((...results) => R.join(':', results), getters),
3
+ const indexesToLayoutData = (dimensions, indexes) => R.addIndex(R.map)(
4
+ (valueIndex, dimensionIndex) => {
5
+ const dimension = R.nth(dimensionIndex, dimensions);
6
+ return ({
7
+ dimension: R.pick(['id', 'name', '__index'], dimension),
8
+ value: R.nth(Number(valueIndex), R.propOr([], 'values', dimension))
9
+ });
10
+ },
11
+ indexes
9
12
  );
10
13
 
11
- export const refineSectionsData = (sectionsData, extractedKeys, curatedData, dataToKey) => R.pipe(
12
- R.map(
13
- (section) => {
14
- const sectionKey = dataToKey.sections(R.head(section));
15
- return R.over(
16
- R.lensIndex(1),
17
- R.filter(
18
- rowData => {
19
- const rowKey = dataToKey.rows(rowData);
20
- return R.pipe(
21
- R.path([sectionKey, rowKey]),
22
- R.omit(extractedKeys),
23
- R.isEmpty,
24
- R.not
25
- )(curatedData);
26
- }
27
- )
28
- )(section);
29
- }
30
- ),
31
- R.filter(
32
- R.pipe(R.nth(1), R.isEmpty, R.not)
33
- )
34
- )(sectionsData);
35
-
36
- export const refineHeaderData = (headerData, extractedKeys, curatedData, dataToKey) => R.filter( // extracted : { [sectionKey]: [...rowKeys] }
37
- (header) => {
38
- const headerKey = dataToKey.header(header);
39
- return R.pipe(
40
- R.prop(headerKey),
41
- (sections) => {
42
- return R.pickBy(
43
- (rows, section) => R.pipe(
44
- R.omit(R.propOr([], section, extractedKeys)),
45
- R.isEmpty,
46
- R.not
47
- )(rows),
48
- sections
14
+ const getRowData = (indexes, dimension) => {
15
+ return R.pipe(
16
+ R.propOr([], 'values'),
17
+ R.props(indexes),
18
+ R.reduce(
19
+ ({ data, missingParents }, _value) => {
20
+ const raw = {
21
+ dimension: R.pick(['id', 'name', '__index'], dimension),
22
+ value: _value,
23
+ };
24
+ if (R.isNil(_value.parents) || R.isEmpty(_value.parents)) {
25
+ return ({ data: R.append([raw], data), missingParents: {} });
26
+ }
27
+ const previous = R.head(R.last(data) || []);
28
+
29
+ if (R.last(_value.parents) === R.path(['value', 'id'], previous)) { //previous is parent
30
+ const value = R.set(
31
+ R.lensPath(['value', 'parents']),
32
+ R.append(
33
+ R.path(['value', 'id'], previous),
34
+ R.pathOr([], ['value', 'parents'], previous)
35
+ )
36
+ )(raw);
37
+ return ({ data: R.append([value], data), missingParents: {} });
38
+ }
39
+
40
+ const previousParent = R.pipe(R.pathOr([], ['value', 'parents']), R.last)(previous);
41
+ if (R.last(_value.parents) === previousParent) { //previous is bro
42
+ const value = R.set(
43
+ R.lensPath(['value']),
44
+ {
45
+ ..._value,
46
+ name: R.isEmpty(missingParents) ? _value.name : `${missingParents.name} > ${_value.name}`,
47
+ parents: R.pathOr([], ['value', 'parents'], previous)
48
+ }
49
+ )(raw);
50
+ return ({ data: R.append([value], data), missingParents });
51
+ }
52
+
53
+ const missingIds = R.takeLastWhile(id => id !== R.path(['value', 'id'], previous), _value.parents);
54
+ const _missingParents = R.reduce(
55
+ (acc, value) => {
56
+ if (value.id !== R.head(acc.missingIds)) {
57
+ return acc;
58
+ }
59
+ const _missingIds = R.tail(acc.missingIds);
60
+ if (R.isEmpty(acc.name)) {
61
+ return ({ name: value.name, missingIds: _missingIds });
62
+ }
63
+ return ({
64
+ name: `${acc.name} > ${value.name}`,
65
+ missingIds: _missingIds
66
+ });
67
+ },
68
+ { name: '', missingIds },
69
+ dimension.values
49
70
  );
50
- },
51
- R.isEmpty,
52
- R.not
53
- )(curatedData);
54
- }
55
- )(headerData);
56
-
57
- export const truncateSectionRows = (n, sectionsData, dataToKey) => {
58
- const [rest, extracted] = R.pipe(
59
- R.last, // last section
60
- R.nth(1), // rows
61
- R.splitAt(R.negate(n)) // truncate from the end
62
- )(sectionsData);
63
-
64
- const truncatedData = R.isEmpty(rest)
65
- ? R.dropLast(1, sectionsData) // remove section if no more rows
66
- : R.adjust(-1, R.adjust(1, R.always(rest)), sectionsData)
67
-
68
- const sectionKey = dataToKey.sections(R.pipe(R.last, R.head)(sectionsData));
69
- const rowKeys = R.map(dataToKey.rows, extracted);
70
-
71
- const extractedLength = R.length(extracted);
72
- if (extractedLength < n) {
73
- const { truncated, extractedKeys } = truncateSectionRows(
74
- n - extractedLength,
75
- truncatedData,
76
- dataToKey
77
- );
78
- return ({ truncated, extractedKeys: { ...extractedKeys, [sectionKey]: rowKeys } });
79
- }
80
71
 
81
- return ({ truncated: truncatedData, extractedKeys: { [sectionKey]: rowKeys } });
82
- };
72
+ const value = R.set(
73
+ R.lensProp('value'),
74
+ {
75
+ ..._value,
76
+ name: `${_missingParents.name} > ${_value.name}`,
77
+ parents: R.length(missingIds) === R.length(_value.parents)
78
+ ? []
79
+ : R.append(R.path(['value', 'id'], previous), R.pathOr([], ['value', 'parents'], previous))
80
+ }
81
+ )(raw);
83
82
 
84
- export const truncateHeader = (n, headerData, dataToKey) => {
85
- const [truncated, extracted] = R.splitAt(R.negate(n))(headerData);
86
- return ({ truncated, extractedKeys: R.map(dataToKey.header, extracted) });
83
+ return ({ data: R.append([value], data), missingParents: _missingParents });
84
+ },
85
+ { data: [], missingParents: {} }
86
+ ),
87
+ R.prop('data')
88
+ )(dimension);
87
89
  };
88
90
 
89
- const refineLength = length => length === 0 ? 1 : length;
90
-
91
- const truncateLayout = (isVertical) => R.ifElse(
92
- R.always(isVertical),
93
- truncateSectionRows,
94
- truncateHeader,
95
- );
96
-
97
- const refineLayout = (isVertical) => R.ifElse(
98
- R.always(isVertical),
99
- refineHeaderData,
100
- refineSectionsData
101
- )
102
-
103
- const getShape = isVertical => isVertical ? ['header', 'sections', 'rows'] : ['sections', 'rows', 'header'];
91
+ const getRowsData = (rowsIndexes, dimensions) => {
92
+ const dimension = R.head(dimensions);
104
93
 
105
- const getRefinedLayout = (isVertical, truncated, refined) => R.ifElse(
106
- R.always(isVertical),
107
- R.pipe(R.assoc('sectionsData', truncated), R.assoc('headerData', refined)),
108
- R.pipe(R.assoc('headerData', truncated), R.assoc('sectionsData', refined)),
109
- )({});
110
-
111
- const segregateLayout = (isVertical) => R.ifElse(
112
- R.always(isVertical),
113
- R.props(['sectionsData', 'headerData']),
114
- R.props(['headerData', 'sectionsData'])
115
- );
116
-
117
- export const getLayoutData = ({ layout, observations, limit }) => {
118
- const layoutData = getSortedLayoutData(layout, observations);
119
- const { headerData, sectionsData } = layoutData;
120
- if (R.isNil(limit) || limit === 0 || R.all(R.isEmpty, [headerData, sectionsData])) {
121
- return R.assoc('truncated', false, layoutData);
122
- }
123
-
124
- //number of dimensions in header
125
- const headerDimCount = R.pipe(
126
- R.head, // first column
127
- R.when(R.isNil, R.always([])),
128
- R.length // number of dims
129
- )(headerData);
130
-
131
- //number of columns for values
132
- const headerValuesCount = R.pipe(
133
- R.length,
134
- R.when(R.equals(0), R.always(1))
135
- )(headerData);
136
-
137
- //total of cells in header
138
- const headerCellsCount = headerDimCount * (headerValuesCount + 1);
139
-
140
- // number of dimensions in a row
141
- const rowDimCount = R.pipe(
142
- R.head, // firstSection
143
- R.last, // rows,
144
- R.head, // first row
145
- R.length
146
- )(sectionsData);
147
-
148
- // number of cells in a row
149
- const rowCellsCount = rowDimCount + headerValuesCount + 1;
150
-
151
- // number of rows
152
- const rowsCount = R.pipe(
153
- R.map(R.last),
154
- R.unnest,
155
- R.length,
156
- R.add(1)
157
- )(sectionsData);
158
-
159
- // total of cells in all rows
160
- const rowsCellsCount = rowCellsCount * rowsCount;
161
-
162
- // number of sections cells
163
- const sectionsCellsCount = R.ifElse(
164
- R.pipe(R.head, R.head, R.length, R.equals(0)),
165
- R.always(0),
166
- R.length
167
- )(sectionsData);
168
-
169
- const total = rowsCellsCount + sectionsCellsCount + headerCellsCount;
170
-
171
- const excess = total - limit;
172
- if (excess <= 0) {
173
- return R.assoc('truncated', false, layoutData);
94
+ if (R.length(dimensions) === 1) {
95
+ return getRowData(R.unnest(rowsIndexes), R.head(dimensions));
174
96
  }
175
97
 
176
- // total of cells in one column
177
- const columnCellsCount = headerDimCount + rowsCount;
178
-
179
- const isVertical = columnCellsCount > rowCellsCount
180
-
181
- const [toTruncate, toRefine] = segregateLayout(isVertical)(layoutData);
182
-
183
- const cutLength = R.pipe(
184
- R.ifElse(R.identity, R.always(rowCellsCount), R.always(columnCellsCount)),
185
- R.divide(excess),
186
- Math.ceil
187
- )(isVertical);
98
+ const groupedIndexes = R.reduce(
99
+ (acc, indexes) => {
100
+ const headIndex = R.head(indexes);
101
+ const last = R.last(acc.heads);
102
+ if (last === headIndex) {
103
+ return ({
104
+ ...acc,
105
+ tails: R.over(
106
+ R.lensIndex(-1),
107
+ R.append(R.tail(indexes))
108
+ )(acc.tails)
109
+ });
110
+ }
111
+ return ({
112
+ heads: R.append(headIndex, acc.heads),
113
+ tails: R.append([R.tail(indexes)], acc.tails)
114
+ });
115
+ },
116
+ { heads: [], tails: [] },
117
+ rowsIndexes
118
+ );
119
+
120
+ const headsDatas = getRowData(groupedIndexes.heads, R.head(dimensions));
121
+
122
+ const res = R.addIndex(R.map)(
123
+ (tail, i) => {
124
+ const tailData = getRowsData(tail, R.tail(dimensions));
125
+ return R.map(
126
+ R.prepend(R.head(headsDatas[i])),
127
+ tailData
128
+ );
129
+ },
130
+ groupedIndexes.tails
131
+ );
132
+
133
+ return R.unnest(res);
134
+ };
188
135
 
189
- const dataToKey = R.mapObjIndexed(layoutDataToKey, layout);
136
+ export const getLayoutData = (layoutIndexes, layout) => {
137
+ const { header, sections, truncated } = layoutIndexes;
138
+ const headerData = R.map(i => indexesToLayoutData(layout.header, i), header);
190
139
 
191
- const { truncated, extractedKeys } = truncateLayout(isVertical)(cutLength, toTruncate, dataToKey);
140
+ const sectionsData = R.map(
141
+ ([sectionIndexes, rows]) => {
142
+ const sectionData = indexesToLayoutData(layout.sections, sectionIndexes);
192
143
 
193
- const curatedData = R.pipe(
194
- getShape,
195
- (shape) => getCuratedCells({ layout, observations, shape })
196
- )(isVertical);
197
144
 
198
- const refined = refineLayout(isVertical)(toRefine, extractedKeys, curatedData, dataToKey);
145
+ const rowsData = getRowsData(rows, layout.rows);
199
146
 
200
- const result = getRefinedLayout(isVertical, truncated, refined);
147
+ return [sectionData, rowsData];
148
+ },
149
+ sections
150
+ );
201
151
 
202
- return R.assoc('truncated', true, result);
152
+ return ({ headerData, sectionsData, truncated });
203
153
  };
154
+
@@ -0,0 +1,108 @@
1
+ import * as R from 'ramda';
2
+
3
+ /*
4
+
5
+ const layout = {
6
+ header: [
7
+ { ...dim, values: [] } // values already sorted regarding order annotation
8
+ ],
9
+ rows: [],
10
+ sections: []
11
+ };
12
+
13
+ const observations = {
14
+ [obsKey]: {
15
+ ...obs,
16
+ orderedDimIndexes: [sortedValIndex]
17
+ }
18
+ }
19
+
20
+ */
21
+
22
+ const getLayoutPivots = layout => {
23
+ return R.mapObjIndexed(
24
+ dimensions => R.props(R.pluck('__index', dimensions)),
25
+ )(layout);
26
+ };
27
+
28
+ const comparator = size => (a, b) => {
29
+ let i = 0;
30
+ while (a[i] === b[i] && i < size) {
31
+ i++;
32
+ }
33
+ return a[i] - b[i];
34
+ };
35
+
36
+ // simple call to ramda uniq method is very slow regarding performance
37
+ const uniqIndexes = (indexes) => R.pipe(
38
+ R.reduce(
39
+ (acc, i) => {
40
+ const key = R.join(':', i);
41
+ if (R.has(key, acc.keys)) {
42
+ return acc;
43
+ }
44
+ return ({
45
+ indexes: R.append(i, acc.indexes),
46
+ keys: R.assoc(key, key, acc.keys)
47
+ });
48
+ },
49
+ { indexes: [], keys: {} },
50
+ ),
51
+ R.prop('indexes')
52
+ )(indexes);
53
+
54
+ export const getSortedLayoutIndexes = (layout, observations) => {
55
+ const dimIndexes = R.pipe(R.values, R.pluck('orderedDimIndexes'))(observations);
56
+
57
+ const headerLength = R.length(layout.header);
58
+
59
+ const sectionLength = R.length(layout.sections);
60
+ const rowLength = R.length(layout.rows);
61
+
62
+ const layoutPivots = getLayoutPivots(layout);
63
+
64
+ const { header, sections } = R.pipe(
65
+ R.reduce(
66
+ (acc, indexes) => {
67
+ const headerIndexes = layoutPivots.header(indexes);
68
+ const sectionsIndexes = layoutPivots.sections(indexes);
69
+ const rowsIndexes = layoutPivots.rows(indexes);
70
+
71
+ return ({
72
+ header: R.append(headerIndexes, acc.header),
73
+ sections: R.append(
74
+ R.concat(sectionsIndexes, rowsIndexes),
75
+ acc.sections
76
+ )
77
+ });
78
+ },
79
+ { header: [], sections: [] },
80
+ ),
81
+ R.mapObjIndexed(uniqIndexes),
82
+ R.evolve({
83
+ header: R.sort(comparator(headerLength)),
84
+ sections: R.sort(comparator(sectionLength + rowLength))
85
+ }),
86
+ indexes => ({
87
+ ...indexes,
88
+ sections: R.reduce(
89
+ (acc, i) => {
90
+ const [sectionIndexes, rowIndexes] = R.splitAt(sectionLength, i);
91
+ const previousSecIndexes = R.pipe(R.nth(-1), i => R.isNil(i) ? null : R.head(i))(acc);
92
+ if (R.equals(sectionIndexes, previousSecIndexes)) {
93
+ return R.over(
94
+ R.lensIndex(-1),
95
+ R.over(R.lensIndex(1), R.append(rowIndexes))
96
+ )(acc);
97
+ }
98
+
99
+ return R.append([sectionIndexes, [rowIndexes]], acc);
100
+ },
101
+ [],
102
+ indexes.sections
103
+ )
104
+ })
105
+ )(dimIndexes);
106
+
107
+ return ({ header, sections });
108
+ };
@@ -3,15 +3,17 @@ import { withFlatHierarchy, isTimePeriodDimension } from '@sis-cc/dotstatsuite-s
3
3
  import { getCells } from './getCells';
4
4
  import { getConfirmedSeriesAttributesIds } from './getConfirmedSeriesAttributesIds';
5
5
  import { getCuratedCells } from './getCuratedCells';
6
- import { getLayoutData } from './getLayoutData';
7
6
  import { getLayoutDataWithFlags } from './getLayoutWithFlags';
8
- import { refineLayoutHierarchy } from './refineLayoutHierarchy';
9
7
 
10
8
  import { getUnitsInLayout } from '../units/getUnitsinLayout';
11
9
  import { appendUnitsInLayoutData } from '../units/appendUnitsInLayoutData';
12
10
  import { cleanUnitsInLayoutData } from '../units/cleanUnitsInLayoutData';
13
11
 
14
12
 
13
+ import { getSortedLayoutIndexes } from './getSortedLayoutIndexes';
14
+ import { refineLayoutSize } from './refineLayoutSize';
15
+ import { getLayoutData } from './getLayoutData';
16
+
15
17
  export const getTableProps = ({ data, layoutIds, display, customAttributes, limit, isTimeInverted }) => {
16
18
  const seriesAttributes = R.propOr({}, 'seriesAttributes', data);
17
19
 
@@ -59,8 +61,8 @@ export const getTableProps = ({ data, layoutIds, display, customAttributes, limi
59
61
  R.lensProp('values'),
60
62
  R.sortWith([R.ascend(R.prop('__indexPosition')), R.ascend(R.prop('order'))]),
61
63
  )(dim),
62
- R.when( // refine hierarchy on last row
63
- R.pipe(R.prop('id'), R.equals(R.last(R.propOr([], 'rows', layoutIds)))),
64
+ R.when( // refine hierarchy on rows
65
+ R.pipe(R.prop('id'), R.flip(R.includes)(R.propOr([], 'rows', layoutIds))),
64
66
  withFlatHierarchy
65
67
  )
66
68
  ),
@@ -79,11 +81,9 @@ export const getTableProps = ({ data, layoutIds, display, customAttributes, limi
79
81
  )(data);
80
82
 
81
83
  const layoutData = R.pipe(
82
- getLayoutData,
83
- R.over(
84
- R.lensProp('sectionsData'),
85
- sectionsData => refineLayoutHierarchy(sectionsData, R.last(R.prop('rows', layout)))
86
- ),
84
+ getSortedLayoutIndexes,
85
+ refineLayoutSize({ layout, observations: data.observations, limit: R.when(R.isNil, R.always(0))(limit) }),
86
+ indexes => getLayoutData(indexes, layout),
87
87
  appendUnitsInLayoutData({
88
88
  display, unitDimension, unitsDisplay: unitsLevelDisplay, unitsSeries,
89
89
  unitsDefinitionCodes, unitsIndexes: unitsAttachmentIndexesInLayout,
@@ -91,7 +91,7 @@ export const getTableProps = ({ data, layoutIds, display, customAttributes, limi
91
91
  }),
92
92
  getLayoutDataWithFlags(seriesAttributesValues, display, customAttributes),
93
93
  cleanUnitsInLayoutData({ unitsDisplay: unitsLevelDisplay, unitsLayoutIndexes: unitsIndexesInLayout })
94
- )({ layout, observations: cells, limit: R.when(R.isNil, R.always(0))(limit) });
94
+ )(layout, R.propOr({}, 'observations', data));
95
95
  return ({
96
96
  cells: getCuratedCells({ layout, observations: cells, shape: ['header', 'sections', 'rows'] }),
97
97
  layout,