@gisce/ooui 0.6.2 → 0.6.3

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 (48) hide show
  1. package/dist/Float.js +17 -1
  2. package/dist/Float.js.map +1 -1
  3. package/dist/Graph/GraphAxis.d.ts +1 -8
  4. package/dist/Graph/GraphAxis.js +1 -17
  5. package/dist/Graph/GraphAxis.js.map +1 -1
  6. package/dist/Graph/GraphChart.d.ts +5 -5
  7. package/dist/Graph/GraphXAxis.d.ts +6 -0
  8. package/dist/Graph/GraphXAxis.js +24 -0
  9. package/dist/Graph/GraphXAxis.js.map +1 -0
  10. package/dist/Graph/GraphYAxis.d.ts +13 -0
  11. package/dist/Graph/GraphYAxis.js +41 -0
  12. package/dist/Graph/GraphYAxis.js.map +1 -0
  13. package/dist/Graph/graphHelper.d.ts +3 -3
  14. package/dist/Graph/graphHelper.js +10 -10
  15. package/dist/Graph/graphHelper.js.map +1 -1
  16. package/dist/Graph/index.d.ts +2 -0
  17. package/dist/Graph/index.js +2 -0
  18. package/dist/Graph/index.js.map +1 -1
  19. package/dist/Graph/processor/fieldUtils.d.ts +16 -0
  20. package/dist/Graph/processor/fieldUtils.js +43 -0
  21. package/dist/Graph/processor/fieldUtils.js.map +1 -0
  22. package/dist/Graph/processor/graphProcessor.d.ts +49 -0
  23. package/dist/Graph/processor/graphProcessor.js +201 -0
  24. package/dist/Graph/processor/graphProcessor.js.map +1 -0
  25. package/dist/helpers/attributeParser.js +45 -8
  26. package/dist/helpers/attributeParser.js.map +1 -1
  27. package/dist/helpers/domainParser.js +17 -1
  28. package/dist/helpers/domainParser.js.map +1 -1
  29. package/dist/helpers/stateParser.js +71 -16
  30. package/dist/helpers/stateParser.js.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +2 -2
  33. package/dist/index.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/Graph/GraphAxis.ts +1 -24
  36. package/src/Graph/GraphChart.ts +5 -5
  37. package/src/Graph/GraphXAxis.ts +7 -0
  38. package/src/Graph/GraphYAxis.ts +29 -0
  39. package/src/Graph/graphHelper.ts +15 -14
  40. package/src/Graph/index.ts +2 -0
  41. package/src/Graph/processor/fieldUtils.ts +60 -0
  42. package/src/Graph/processor/graphProcessor.ts +221 -0
  43. package/src/index.ts +4 -0
  44. package/src/spec/Graph.spec.ts +2 -2
  45. package/src/spec/graphProcessor.spec.ts +250 -0
  46. package/src/spec/mockData/graphs/Lectura.ts +620 -0
  47. package/src/spec/mockData/graphs/Polissa.ts +4996 -0
  48. package/src/spec/mockData/graphs/index.ts +4 -0
@@ -0,0 +1,221 @@
1
+ import { GraphChart, GraphYAxis, Operator } from "..";
2
+ import { getValueAndLabelForField } from "./fieldUtils";
3
+
4
+ export type GroupedValues = {
5
+ [key: string]: { label: string; entries: { [key: string]: any }[] };
6
+ };
7
+
8
+ export const labelsForOperator = {
9
+ count: "count",
10
+ "+": "sum",
11
+ "-": "subtract",
12
+ "*": "multiply",
13
+ avg: "average",
14
+ min: "min",
15
+ max: "max",
16
+ };
17
+
18
+ export const processGraphData = ({
19
+ ooui,
20
+ values,
21
+ fields,
22
+ }: {
23
+ ooui: GraphChart;
24
+ values: { [key: string]: any }[];
25
+ fields: { [key: string]: any };
26
+ }) => {
27
+ const valuesGroupedByX = getValuesGroupedByField({
28
+ fieldName: ooui.x.name,
29
+ values,
30
+ fields,
31
+ });
32
+
33
+ const fieldsData = {
34
+ xField: ooui.x.name,
35
+ yFields: [...new Set(ooui.y.map((item) => getYAxisFieldname(item)))],
36
+ seriesFields:
37
+ ooui.y.filter((yField) => yField.label).length !== 0
38
+ ? [...new Set(ooui.y.map((item) => item.label))]
39
+ : undefined,
40
+ };
41
+
42
+ const data: { [key: string]: any }[] = [];
43
+
44
+ ooui.y.forEach((yField) => {
45
+ Object.keys(valuesGroupedByX).forEach((xValue) => {
46
+ const xLabel = valuesGroupedByX[xValue].label;
47
+ const objectsForXValue = valuesGroupedByX[xValue].entries;
48
+
49
+ if (yField.label) {
50
+ const valuesGroupedByYLabel = getValuesGroupedByField({
51
+ fieldName: yField.label,
52
+ values: objectsForXValue,
53
+ fields,
54
+ });
55
+
56
+ Object.keys(valuesGroupedByYLabel).forEach((yUniqueValue) => {
57
+ const entries = valuesGroupedByYLabel[yUniqueValue].entries;
58
+ const label = valuesGroupedByYLabel[yUniqueValue].label;
59
+ const valuesForYField = entries
60
+ .map((obj) => {
61
+ return getValueAndLabelForField({
62
+ fieldName: yField.name,
63
+ values: obj,
64
+ fields: fields,
65
+ });
66
+ })
67
+ .map(({ value, label }) => {
68
+ return label;
69
+ });
70
+ const finalValue = getValueForOperator({
71
+ values: valuesForYField,
72
+ operator: yField.operator,
73
+ });
74
+ data.push({
75
+ [ooui.x.name]: xLabel || false,
76
+ [`${yField.name}_${
77
+ labelsForOperator[yField.operator!]
78
+ }`]: finalValue,
79
+ [yField.label!]: label,
80
+ });
81
+ });
82
+ } else {
83
+ const valuesForYField = objectsForXValue
84
+ .map((obj) => {
85
+ return getValueAndLabelForField({
86
+ fieldName: yField.name,
87
+ values: obj,
88
+ fields: fields,
89
+ });
90
+ })
91
+ .map(({ value, label }) => {
92
+ return label;
93
+ });
94
+
95
+ const finalValue = getValueForOperator({
96
+ values: valuesForYField,
97
+ operator: yField.operator,
98
+ });
99
+
100
+ data.push({
101
+ [ooui.x.name]: xLabel || false,
102
+ [getYAxisFieldname(yField)]: finalValue,
103
+ });
104
+ }
105
+ });
106
+ });
107
+
108
+ // If we don't have y axis fields with label specified, we need to,
109
+ // merge the results with the same name key
110
+ if (ooui.y.filter((yField) => yField.label).length === 0) {
111
+ const uniqueXkeys = [...new Set(data.map((item) => item[ooui.x.name]))];
112
+ const processedData = uniqueXkeys.map((key) => {
113
+ const mergedRecord = {};
114
+ const valuesForKey = data.filter((item) => item[ooui.x.name] === key);
115
+ valuesForKey.forEach((item) => {
116
+ Object.assign(mergedRecord, item);
117
+ });
118
+ return mergedRecord as { [key: string]: any };
119
+ });
120
+ return { data: processedData, ...fieldsData };
121
+ }
122
+
123
+ return {
124
+ data,
125
+ ...fieldsData,
126
+ };
127
+ };
128
+
129
+ export function getValueForOperator({
130
+ operator,
131
+ values,
132
+ }: {
133
+ operator: Operator;
134
+ values: any[];
135
+ }) {
136
+ switch (operator) {
137
+ case "count": {
138
+ return values.length;
139
+ }
140
+ case "+": {
141
+ return roundNumber(
142
+ values.reduce(function (previousValue: any, currentValue: any) {
143
+ return previousValue + currentValue;
144
+ })
145
+ );
146
+ }
147
+ case "-": {
148
+ return roundNumber(
149
+ values.reduce(function (previousValue: any, currentValue: any) {
150
+ return previousValue - currentValue;
151
+ })
152
+ );
153
+ }
154
+ case "*": {
155
+ return roundNumber(
156
+ values.reduce(function (previousValue: any, currentValue: any) {
157
+ return previousValue * currentValue;
158
+ })
159
+ );
160
+ }
161
+ case "avg": {
162
+ const sum = values.reduce((a: any, b: any) => a + b, 0);
163
+ const avg = sum / values.length || 0;
164
+ return roundNumber(avg);
165
+ }
166
+ case "min": {
167
+ return Math.min(...values);
168
+ }
169
+ case "max": {
170
+ return Math.max(...values);
171
+ }
172
+ }
173
+ }
174
+
175
+ function roundNumber(num: number) {
176
+ return Math.round((num + Number.EPSILON) * 100) / 100;
177
+ }
178
+
179
+ export function getValuesGroupedByField({
180
+ fieldName,
181
+ fields,
182
+ values,
183
+ }: {
184
+ fieldName: string;
185
+ values: { [key: string]: any }[];
186
+ fields: { [key: string]: any };
187
+ }) {
188
+ const groupedValues: GroupedValues = {};
189
+
190
+ values.forEach((entry) => {
191
+ const { value, label } = getValueAndLabelForField({
192
+ fields,
193
+ values: entry,
194
+ fieldName,
195
+ });
196
+
197
+ if (!groupedValues[value]) {
198
+ groupedValues[value] = { label, entries: [] };
199
+ }
200
+
201
+ groupedValues[value].entries.push(entry);
202
+ });
203
+
204
+ return groupedValues;
205
+ }
206
+
207
+ export function getAllObjectsInGroupedValues(grouped: GroupedValues) {
208
+ let totalObjects: any[] = [];
209
+ Object.keys(grouped).forEach((key) => {
210
+ const group = grouped[key];
211
+ totalObjects = totalObjects.concat(group.entries);
212
+ });
213
+ return totalObjects;
214
+ }
215
+
216
+ export function getYAxisFieldname(y: GraphYAxis) {
217
+ if (y.operator) {
218
+ return y.name + "_" + labelsForOperator[y.operator];
219
+ }
220
+ return y.name!;
221
+ }
package/src/index.ts CHANGED
@@ -42,6 +42,8 @@ import {
42
42
  GraphType,
43
43
  parseGraph,
44
44
  Operator,
45
+ GraphYAxis,
46
+ GraphXAxis,
45
47
  } from "./Graph";
46
48
 
47
49
  export {
@@ -82,6 +84,8 @@ export {
82
84
  DashboardItem,
83
85
  Graph,
84
86
  GraphAxis,
87
+ GraphYAxis,
88
+ GraphXAxis,
85
89
  GraphIndicator,
86
90
  GraphChart,
87
91
  GraphType,
@@ -4,6 +4,7 @@ import {
4
4
  parseGraph,
5
5
  GraphAxis,
6
6
  GraphIndicatorField,
7
+ GraphYAxis,
7
8
  } from "..";
8
9
 
9
10
  describe("A Graph", () => {
@@ -28,7 +29,7 @@ describe("A Graph", () => {
28
29
  `;
29
30
 
30
31
  const graph = parseGraph(xml) as GraphChart;
31
- const y: GraphAxis = graph.y[0];
32
+ const y: GraphYAxis = graph.y[0];
32
33
 
33
34
  expect(graph.type).toBe("line");
34
35
  expect(graph.x).toBeDefined();
@@ -37,7 +38,6 @@ describe("A Graph", () => {
37
38
  expect(y.name).toBe("data_alta");
38
39
  expect(graph.x.axis).toBe("x");
39
40
  expect(y.axis).toBe("y");
40
- expect(graph.x?.operator).toBeUndefined();
41
41
  expect(y.operator).toBe("+");
42
42
  });
43
43
  it("should parse a basic XML title and type indicator field", () => {
@@ -0,0 +1,250 @@
1
+ import models from "./mockData/graphs";
2
+ import {
3
+ getAllObjectsInGroupedValues,
4
+ getValuesGroupedByField,
5
+ processGraphData,
6
+ } from "../Graph/processor/graphProcessor";
7
+ import { GraphChart, parseGraph } from "../Graph";
8
+
9
+ describe("in getValuesGroupedByField method", () => {
10
+ it("should properly group a lectura values for field 'name' - char", () => {
11
+ const lecturaModel = models.find((m) => m.key === "lectura");
12
+ expect(lecturaModel).toBeDefined();
13
+ if (!lecturaModel) {
14
+ throw new Error("Model not found");
15
+ }
16
+ const lecturaValues = lecturaModel.data;
17
+ const lecturaFields = lecturaModel.fields;
18
+
19
+ const grouped = getValuesGroupedByField({
20
+ fieldName: "name",
21
+ values: lecturaValues as any,
22
+ fields: lecturaFields as any,
23
+ });
24
+
25
+ expect(getAllObjectsInGroupedValues(grouped).length).toBe(23);
26
+ expect(Object.keys(grouped).length).toBe(12);
27
+ expect(grouped["2020-09-30"].label).toBe("2020-09-30");
28
+ expect(grouped["2020-09-30"].entries.length).toBe(1);
29
+ expect(grouped["2020-09-30"].entries[0].name).toBe("2020-09-30");
30
+ expect(grouped["2020-07-31"].entries.length).toBe(6);
31
+ expect(grouped["2020-07-08"].entries.length).toBe(4);
32
+ expect(grouped["2020-05-31"].entries.length).toBe(2);
33
+ expect(grouped["2015-10-31"].entries.length).toBe(1);
34
+ });
35
+ it("should properly group a lectura values for field 'comptador' - many2one", () => {
36
+ const lecturaModel = models.find((m) => m.key === "lectura");
37
+ expect(lecturaModel).toBeDefined();
38
+ if (!lecturaModel) {
39
+ throw new Error("Model not found");
40
+ }
41
+ const lecturaValues = lecturaModel.data;
42
+ const lecturaFields = lecturaModel.fields;
43
+
44
+ const grouped = getValuesGroupedByField({
45
+ fieldName: "comptador",
46
+ values: lecturaValues as any,
47
+ fields: lecturaFields as any,
48
+ });
49
+
50
+ expect(getAllObjectsInGroupedValues(grouped).length).toBe(23);
51
+ expect(Object.keys(grouped).length).toBe(5);
52
+ expect(Object.keys(grouped).toString()).toBe("1,3,13,14,15");
53
+ expect(grouped["1"].label).toBe("B63011077");
54
+ expect(grouped["3"].label).toBe("42553686");
55
+ expect(grouped["13"].label).toBe("1234567898");
56
+ expect(grouped["1"].entries.length).toBe(5);
57
+ expect(grouped["3"].entries.length).toBe(1);
58
+ expect(grouped["13"].entries.length).toBe(6);
59
+ });
60
+ it("should properly group a lectura values for field 'motiu_ajust' - selection", () => {
61
+ const lecturaModel = models.find((m) => m.key === "lectura");
62
+ expect(lecturaModel).toBeDefined();
63
+ if (!lecturaModel) {
64
+ throw new Error("Model not found");
65
+ }
66
+ const lecturaValues = lecturaModel.data;
67
+ const lecturaFields = lecturaModel.fields;
68
+
69
+ const grouped = getValuesGroupedByField({
70
+ fieldName: "motiu_ajust",
71
+ values: lecturaValues as any,
72
+ fields: lecturaFields as any,
73
+ });
74
+
75
+ expect(getAllObjectsInGroupedValues(grouped).length).toBe(23);
76
+ expect(Object.keys(grouped).length).toBe(2);
77
+ expect(Object.keys(grouped).toString()).toBe("false,01");
78
+ expect(grouped["false"].label).toBeUndefined();
79
+ expect(grouped["false"].entries.length).toBe(21);
80
+ expect(grouped["01"].label).toBe("Verificación equipo de medida");
81
+ expect(grouped["01"].entries.length).toBe(2);
82
+ });
83
+ it("should properly throw an error if the field is not found", () => {
84
+ const lecturaModel = models.find((m) => m.key === "lectura");
85
+ expect(lecturaModel).toBeDefined();
86
+ if (!lecturaModel) {
87
+ throw new Error("Model not found");
88
+ }
89
+ const lecturaValues = lecturaModel.data;
90
+ const lecturaFields = lecturaModel.fields;
91
+
92
+ const test = () => {
93
+ getValuesGroupedByField({
94
+ fieldName: "data_alta",
95
+ values: lecturaValues as any,
96
+ fields: lecturaFields as any,
97
+ });
98
+
99
+ expect(test).toThrowError();
100
+ };
101
+ });
102
+ });
103
+
104
+ describe("in processGraphData method", () => {
105
+ it("should do basic test with one y axis", () => {
106
+ const parsedGraph = parseGraph(`<?xml version="1.0"?>
107
+ <graph type="pie">
108
+ <field name="llista_preu" axis="x"/>
109
+ <field name="llista_preu" operator="count" axis="y"/>
110
+ </graph>
111
+ `) as GraphChart;
112
+ const model = models.find((m) => m.key === "polissa");
113
+ expect(model).toBeDefined();
114
+ if (!model) {
115
+ throw new Error("Model not found");
116
+ }
117
+ const values = model.data;
118
+ const fields = model.fields;
119
+
120
+ const { data } = processGraphData({
121
+ ooui: parsedGraph,
122
+ values: values as any,
123
+ fields: fields as any,
124
+ });
125
+
126
+ expect(data.length).toBe(6);
127
+ expect(
128
+ data.find(
129
+ (d) =>
130
+ d.llista_preu === "TARIFAS ELECTRICIDAD (EUR)" &&
131
+ d.llista_preu_count === 8
132
+ )
133
+ ).toBeTruthy();
134
+ expect(
135
+ data.find(
136
+ (d) => d.llista_preu === "Adeu (CHF)" && d.llista_preu_count === 4
137
+ )
138
+ ).toBeTruthy();
139
+ expect(
140
+ data.find(
141
+ (d) =>
142
+ d.llista_preu === "Hola bipartit (EUR)" && d.llista_preu_count === 5
143
+ )
144
+ ).toBeTruthy();
145
+ expect(
146
+ data.find(
147
+ (d) =>
148
+ d.llista_preu === "Mucha potencia (EUR)" && d.llista_preu_count === 1
149
+ )
150
+ ).toBeTruthy();
151
+
152
+ expect(
153
+ data.find(
154
+ (d) => d.llista_preu === "Hola (EUR)" && d.llista_preu_count === 13
155
+ )
156
+ ).toBeTruthy();
157
+
158
+ expect(
159
+ data.find((d) => d.llista_preu === false && d.llista_preu_count === 2)
160
+ ).toBeTruthy();
161
+
162
+ expect(
163
+ data.find((d) => d.llista_preu === "random" && d.llista_preu_count === 15)
164
+ ).toBeUndefined();
165
+
166
+ expect(data).toBeTruthy();
167
+ });
168
+ it("should do basic test with two y axis", () => {
169
+ const parsedGraph = parseGraph(`<?xml version="1.0"?>
170
+ <graph type="bar">
171
+ <field name="name" axis="x" />
172
+ <field name="consum" operator="+" axis="y"/>
173
+ <field name="ajust" operator="+" axis="y" />
174
+ </graph>
175
+ `) as GraphChart;
176
+
177
+ const model = models.find((m) => m.key === "lectura");
178
+ expect(model).toBeDefined();
179
+ if (!model) {
180
+ throw new Error("Model not found");
181
+ }
182
+ const values = model.data;
183
+ const fields = model.fields;
184
+
185
+ const { data, xField, yFields, seriesFields } = processGraphData({
186
+ ooui: parsedGraph,
187
+ values: values as any,
188
+ fields: fields as any,
189
+ });
190
+
191
+ expect(xField).toBe("name");
192
+ expect(yFields!.length).toBe(2);
193
+ expect(yFields![0]).toBe("consum_sum");
194
+ expect(yFields![1]).toBe("ajust_sum");
195
+ expect(seriesFields).toBeUndefined();
196
+
197
+ expect(data.length).toBe(12);
198
+ expect(data.find((d) => d.name === "2020-09-30")).toBeTruthy();
199
+ const obj1 = data.find((d) => d.name === "2020-09-30")!;
200
+ expect(obj1.consum_sum).toBe(0);
201
+ expect(obj1.ajust_sum).toBe(0);
202
+ expect(data.find((d) => d.name === "2020-06-30")).toBeTruthy();
203
+ const obj2 = data.find((d) => d.name === "2020-06-30")!;
204
+ expect(obj2.consum_sum).toBe(150);
205
+ expect(obj2.ajust_sum).toBe(0);
206
+ expect(data.find((d) => d.name === "2016-04-04")).toBeTruthy();
207
+ const obj3 = data.find((d) => d.name === "2016-04-04")!;
208
+ expect(obj3.consum_sum).toBe(15);
209
+ expect(obj3.ajust_sum).toBe(15);
210
+ });
211
+ it("should do basic test with one y axis with label", () => {
212
+ const parsedGraph = parseGraph(`<?xml version="1.0"?>
213
+ <graph type="bar">
214
+ <field name="name" axis="x" />
215
+ <field name="consum" operator="+" label="periode" axis="y"/>
216
+ </graph>
217
+ `) as GraphChart;
218
+
219
+ const model = models.find((m) => m.key === "lectura");
220
+ expect(model).toBeDefined();
221
+ if (!model) {
222
+ throw new Error("Model not found");
223
+ }
224
+ const values = model.data;
225
+ const fields = model.fields;
226
+
227
+ const { data, xField, yFields, seriesFields } = processGraphData({
228
+ ooui: parsedGraph,
229
+ values: values as any,
230
+ fields: fields as any,
231
+ });
232
+
233
+ expect(xField).toBe("name");
234
+ expect(yFields!.length).toBe(1);
235
+ expect(yFields![0]).toBe("consum_sum");
236
+ expect(seriesFields!.length).toBe(1);
237
+ expect(seriesFields![0]).toBe("periode");
238
+
239
+ expect(data.length).toBe(15);
240
+ expect(data.find((d) => d.name === "2020-09-30")).toBeTruthy();
241
+ const obj1 = data.find((d) => d.name === "2020-09-30")!;
242
+ expect(obj1.consum_sum).toBe(0);
243
+ expect(obj1.periode).toBe("2.0A (P1)");
244
+ const obj2 = data.filter((d) => d.name === "2020-07-31")!;
245
+ expect(obj2.length).toBe(3);
246
+ expect(obj2.map((e) => e.periode).toString()).toBe(
247
+ "2.0A (P1),2.0DHA (P1),2.0DHA (P2)"
248
+ );
249
+ });
250
+ });