@opengis/bi 1.0.14 → 1.0.15

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 (53) hide show
  1. package/README.md +50 -50
  2. package/config.js +12 -12
  3. package/dist/bi.js +1 -1
  4. package/dist/bi.umd.cjs +63 -63
  5. package/dist/{import-file-DUp3rsNI.js → import-file-CRC0sYYT.js} +8055 -7987
  6. package/dist/{map-component-mixin-CGM0P5ub.js → map-component-mixin-BCtWEvzv.js} +3795 -2116
  7. package/dist/style.css +1 -1
  8. package/dist/{vs-calendar-cOoinEwc.js → vs-calendar-5ot79n0N.js} +20 -9
  9. package/dist/{vs-funnel-bar-kLkPoIhJ.js → vs-funnel-bar-CLo6gXI_.js} +2 -2
  10. package/dist/{vs-heatmap-3XAVGTSo.js → vs-heatmap-DHGA8dRk.js} +3 -4
  11. package/dist/{vs-map-cluster-BWJPx7wE.js → vs-map-cluster-CNgX6JVF.js} +2 -2
  12. package/dist/{vs-map-B1tr6V5_.js → vs-map-pIn5wS4G.js} +2 -2
  13. package/dist/{vs-number-CrU7LmkV.js → vs-number-DYfok8VU.js} +19 -12
  14. package/dist/{vs-text-DRPx3aID.js → vs-text-Dckykz09.js} +19 -14
  15. package/package.json +107 -97
  16. package/plugin.js +14 -13
  17. package/server/migrations/bi.dataset.sql +26 -0
  18. package/server/migrations/bi.sql +93 -93
  19. package/server/plugins/docs.js +48 -48
  20. package/server/plugins/hook.js +89 -89
  21. package/server/plugins/vite.js +69 -69
  22. package/server/routes/dashboard/controllers/dashboard.delete.js +38 -37
  23. package/server/routes/dashboard/controllers/dashboard.js +118 -114
  24. package/server/routes/dashboard/controllers/dashboard.list.js +30 -36
  25. package/server/routes/dashboard/controllers/utils/yaml.js +11 -11
  26. package/server/routes/dashboard/index.mjs +25 -25
  27. package/server/routes/data/controllers/data.js +167 -156
  28. package/server/routes/data/controllers/util/chartSQL.js +42 -39
  29. package/server/routes/data/controllers/util/normalizeData.js +59 -56
  30. package/server/routes/data/index.mjs +29 -24
  31. package/server/routes/dataset/controllers/bi.dataset.demo.add.js +97 -0
  32. package/server/routes/dataset/controllers/bi.dataset.import.js +67 -0
  33. package/server/routes/dataset/controllers/util/create.table.js +22 -0
  34. package/server/routes/dataset/controllers/util/prepare.data.js +49 -0
  35. package/server/routes/dataset/index.mjs +19 -0
  36. package/server/routes/db/controllers/dbTablePreview.js +63 -63
  37. package/server/routes/db/controllers/dbTables.js +36 -36
  38. package/server/routes/db/index.mjs +17 -17
  39. package/server/routes/edit/controllers/dashboard.add.js +26 -24
  40. package/server/routes/edit/controllers/dashboard.edit.js +46 -44
  41. package/server/routes/edit/controllers/widget.add.js +75 -73
  42. package/server/routes/edit/controllers/widget.del.js +69 -70
  43. package/server/routes/edit/controllers/widget.edit.js +52 -103
  44. package/server/routes/edit/index.mjs +31 -31
  45. package/server/routes/map/controllers/cluster.js +109 -104
  46. package/server/routes/map/controllers/clusterVtile.js +166 -213
  47. package/server/routes/map/controllers/geojson.js +127 -127
  48. package/server/routes/map/controllers/map.js +60 -57
  49. package/server/routes/map/controllers/utils/downloadClusterData.js +43 -0
  50. package/server/routes/map/controllers/vtile.js +183 -182
  51. package/server/routes/map/index.mjs +25 -25
  52. package/server/utils/getWidget.js +85 -83
  53. package/utils.js +12 -12
@@ -1,157 +1,168 @@
1
- // import autoIndex from '@opengis/fastify-table/pg/funcs/autoIndex.js';
2
- // import pgClients from '@opengis/fastify-table/pg/pgClients.js';
3
- import yaml from 'js-yaml';
4
-
5
- import {
6
- getPGAsync,
7
- autoIndex,
8
- pgClients,
9
- getSelectVal,
10
- } from '@opengis/fastify-table/utils.js';
11
- import chartSQL from './util/chartSQL.js';
12
- import normalizeData from './util/normalizeData.js';
13
-
14
- import { getWidget } from '../../../../utils.js';
15
-
16
- export default async function dataAPI({ funcs = {}, query = {} }) {
17
- const time = Date.now();
18
- const { dashboard, widget, filter, search, samples } = query;
19
-
20
- const widgetData = await getWidget({ dashboard, widget });
21
-
22
- if (widgetData.status) return widgetData;
23
-
24
- const { type, text, data = {}, controls, style, options } = widgetData;
25
-
26
- const pg = data.db ? await getPGAsync(data.db) : pgClients.client;
27
-
28
- const { fields: cols } = await pg.query(
29
- `select * from ${data.table} limit 0`
30
- );
31
- const columnTypes = cols.map((el) => ({
32
- name: el.name,
33
- type: pg.pgType?.[el.dataTypeID],
34
- }));
35
-
36
- // data param
37
- const { x, cls, metric, table, where, tableSQL, groupby, xName } =
38
- normalizeData(widgetData, query, columnTypes);
39
-
40
- // auto Index
41
- if (pg.pk?.[data.table]) {
42
- autoIndex({
43
- table: data.table,
44
- pg,
45
- columns: [data?.time]
46
- .concat([xName])
47
- .concat([groupby])
48
- .filter((el) => el),
49
- }).catch((err) => console.log(err));
50
- }
51
-
52
- // get group
53
- const groupData = groupby
54
- ? await pg
55
- .query(
56
- `select ${groupby} as name ,count(*) from ${tableSQL || table} group by ${groupby} order by count(*) desc limit 20`
57
- )
58
- .then((el) => el.rows)
59
- : null;
60
-
61
- if (query.sql === '2') return { x, metric, table, tableSQL, data, groupData };
62
-
63
- const order = data.order || (type === 'listbar' ? 'metric desc' : null);
64
-
65
- const { optimizedSQL = `select * from ${tableSQL || table}` } =
66
- filter || search
67
- ? await funcs.getFilterSQL({
68
- pg,
69
- table,
70
- filter,
71
- search,
72
- })
73
- : {};
74
-
75
- const sql = (chartSQL[type] || chartSQL.chart)({
76
- where,
77
- metric,
78
- table: `(${optimizedSQL})q`,
79
- x,
80
- groupData,
81
- groupby,
82
- order,
83
- samples,
84
- });
85
-
86
- if (query.sql) return sql;
87
-
88
- if (!sql || sql?.includes('undefined')) {
89
- return {
90
- message: {
91
- error: 'invalid sql',
92
- type,
93
- sql,
94
- where,
95
- metric,
96
- table: `(${optimizedSQL})q`,
97
- x,
98
- groupData,
99
- groupby,
100
- },
101
- status: 500,
102
- };
103
- }
104
-
105
- const { rows, fields } = await pg.query(sql); // test with limit
106
-
107
- if (cls) {
108
- const values = rows
109
- .map((row) => row[x])
110
- ?.filter((el, idx, arr) => el && arr.indexOf(el) === idx);
111
- const vals = await getSelectVal({ pg, name: cls, values });
112
- rows
113
- .filter((row) => row[x])
114
- .forEach((row) => {
115
- Object.assign(row, { [x]: vals?.[row[x]] || row[x] });
116
- });
117
- }
118
-
119
- const yml = widgetData.yml || yaml.dump(extractYml(widgetData));
120
- const dimensions = fields.map((el) => el.name);
121
-
122
- const res = {
123
- time: Date.now() - time,
124
- dimensions,
125
-
126
- dimensionsType: fields.map((el) => pg.pgType?.[el.dataTypeID]),
127
- type,
128
-
129
- text: text || widgetData?.title || data.text,
130
- // data: query.format === 'data' ? dimensions.map(el => rows.map(r => r[el])) : undefined,
131
- source:
132
- query.format === 'array'
133
- ? dimensions.map((el) => rows.map((r) => r[el]))
134
- : rows,
135
- style,
136
- options,
137
- controls,
138
- yml,
139
- params: {
140
- x,
141
- cls,
142
- metric,
143
- table,
144
- tableSQL,
145
- where,
146
- groupby,
147
- sql,
148
- },
149
- columnTypes,
150
- };
151
- return res;
152
- }
153
-
154
- function extractYml(sourceData) {
155
- const { title, description, type, data, style, controls } = sourceData;
156
- return { title, description, type, data, style, controls };
1
+ import yaml from 'js-yaml';
2
+
3
+ import {
4
+ config,
5
+ getPGAsync,
6
+ autoIndex,
7
+ pgClients,
8
+ getSelectVal,
9
+ getFilterSQL,
10
+ } from '@opengis/fastify-table/utils.js';
11
+
12
+ import chartSQL from './util/chartSQL.js';
13
+ import normalizeData from './util/normalizeData.js';
14
+
15
+ import { getWidget } from '../../../../utils.js';
16
+
17
+ export default async function dataAPI({ funcs = {}, query = {} }) {
18
+ const time = Date.now();
19
+
20
+ query.metric = Array.isArray(query.metric) ? query.metric.pop() : query.metric;
21
+
22
+ const { dashboard, widget, filter, search, samples } = query;
23
+
24
+ const widgetData = await getWidget({ dashboard, widget });
25
+
26
+ if (widgetData.status) return widgetData;
27
+
28
+ const { type, text, data = {}, controls, style, options } = widgetData;
29
+
30
+ const pg = data.db ? await getPGAsync(data.db) : pgClients.client;
31
+
32
+ const { fields: cols } = await pg.query(
33
+ `select * from ${data.table} limit 0`
34
+ );
35
+ const columnTypes = cols.map((el) => ({
36
+ name: el.name,
37
+ type: pg.pgType?.[el.dataTypeID],
38
+ }));
39
+
40
+ // data param
41
+ const { x, cls, metric, table, where, tableSQL, groupby, xName, yName, xType, yType } =
42
+ normalizeData(widgetData, query, columnTypes);
43
+
44
+ // auto Index
45
+ if (pg.pk?.[data.table]) {
46
+ autoIndex({
47
+ table: data.table,
48
+ pg,
49
+ columns: [data?.time]
50
+ .concat([xName])
51
+ .concat([groupby])
52
+ .filter((el) => el),
53
+ }).catch((err) => console.log(err));
54
+ }
55
+
56
+ // get group
57
+ const groupData = groupby
58
+ ? await pg
59
+ .query(
60
+ `select ${groupby} as name ,count(*) from ${tableSQL || table} group by ${groupby} order by count(*) desc limit 20`
61
+ )
62
+ .then((el) => el.rows)
63
+ : null;
64
+
65
+ if (query.sql === '2') return { x, metric, table, tableSQL, data, groupData };
66
+
67
+ const order = data.order || (type === 'listbar' ? 'metric desc' : null);
68
+
69
+ const { optimizedSQL = `select * from ${tableSQL || table}` } =
70
+ filter || search
71
+ ? await getFilterSQL({
72
+ pg,
73
+ table,
74
+ filter,
75
+ search,
76
+ })
77
+ : {};
78
+
79
+ if (type?.includes('bar') && !metric?.length) {
80
+ return { message: 'empty widget params: metrics', status: 400 };
81
+ }
82
+
83
+ const sql = (chartSQL[type] || chartSQL.chart)({
84
+ where,
85
+ metric,
86
+ table: `(${optimizedSQL} ${samples ? 'limit 10' : ''})q`,
87
+ x,
88
+ groupData,
89
+ groupby,
90
+ order,
91
+ samples,
92
+ xType,
93
+ });
94
+
95
+ if (query.sql) return sql;
96
+
97
+ if (!sql || sql?.includes('undefined')) {
98
+ return {
99
+ message: {
100
+ error: 'invalid sql',
101
+ type,
102
+ sql,
103
+ where,
104
+ metric,
105
+ table: `(${optimizedSQL})q`,
106
+ x,
107
+ groupData,
108
+ groupby,
109
+ },
110
+ status: 500,
111
+ };
112
+ }
113
+
114
+ const { rows, fields } = await pg.query(sql); // test with limit
115
+
116
+ if (cls) {
117
+ const values = rows
118
+ .map((row) => row[x])
119
+ ?.filter((el, idx, arr) => el && arr.indexOf(el) === idx);
120
+ const vals = await getSelectVal({ pg, name: cls, values });
121
+ rows
122
+ .filter((row) => row[x])
123
+ .forEach((row) => {
124
+ Object.assign(row, { [x]: vals?.[row[x]] || row[x] });
125
+ });
126
+ }
127
+
128
+ const yml = widgetData.yml || yaml.dump(extractYml(widgetData));
129
+ const dimensions = fields.map((el) => el.name);
130
+
131
+ const res = {
132
+ time: Date.now() - time,
133
+ dimensions,
134
+ filter: xName,
135
+ dimensionsType: [xType, yType].filter((el) => el)?.length
136
+ ? [xType, yType].filter((el) => el)
137
+ : fields.map((el) => pg.pgType?.[el.dataTypeID]),
138
+ type,
139
+
140
+ text: text || widgetData?.title || data.text,
141
+ // data: query.format === 'data' ? dimensions.map(el => rows.map(r => r[el])) : undefined,
142
+ source:
143
+ query.format === 'array'
144
+ ? dimensions.map((el) => rows.map((r) => r[el]))
145
+ : rows,
146
+ style,
147
+ options,
148
+ controls,
149
+ yml,
150
+ params: config?.local ? {
151
+ x,
152
+ cls,
153
+ metric,
154
+ table,
155
+ tableSQL,
156
+ where,
157
+ groupby,
158
+ sql,
159
+ } : undefined,
160
+ columnTypes,
161
+ };
162
+ return res;
163
+ }
164
+
165
+ function extractYml(sourceData) {
166
+ const { title, description, type, data, style, controls } = sourceData;
167
+ return { title, description, type, data, style, controls };
157
168
  }
@@ -1,39 +1,42 @@
1
- function number({ metric, where, table, samples }) {
2
- const sql = `select ${metric} from ${table} where ${where} ${samples ? 'limit 10' : ''}`;
3
- return sql;
4
- }
5
- function table({ columns, table, where, samples }) {
6
- return `select ${columns.map((el) => el.name || el)}::text from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
7
- }
8
-
9
- function chart({
10
- metric,
11
- where,
12
- table,
13
- x,
14
- groupby,
15
- groupData,
16
- order,
17
- samples,
18
- }) {
19
- const metricData =
20
- groupData
21
- ?.map(
22
- (el) =>
23
- `${metric} filter (where ${groupby}='${el.name}') as "${el.name}"`
24
- )
25
- .join(',') || `${metric} as metric`;
26
- const sql = `select ${x}, ${metricData}
27
- from ${table}
28
- where ${where}
29
- group by ${x}
30
- order by ${order || x}
31
- ${samples ? 'limit 10' : 'limit 100'}`;
32
- return sql;
33
- }
34
-
35
- function text() {
36
- return undefined;
37
- }
38
-
39
- export default { number, chart };
1
+ function number({ metric, where, table, samples }) {
2
+ const sql = `select ${metric} from ${table} where ${where} ${samples ? 'limit 10' : ''}`;
3
+ return sql;
4
+ }
5
+ function table({ columns, table, where, samples }) {
6
+ return `select ${columns.map((el) => el.name || el)}::text from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
7
+ }
8
+
9
+ function chart({
10
+ metric,
11
+ where,
12
+ table,
13
+ x,
14
+ groupby,
15
+ groupData,
16
+ order,
17
+ samples,
18
+ xType,
19
+ }) {
20
+ const xCol = x && xType?.includes('[]') ? `unnest(${x})` : x;
21
+
22
+ const metricData =
23
+ groupData
24
+ ?.map(
25
+ (el) =>
26
+ `${metric} filter (where ${groupby}='${el.name}') as "${el.name}"`
27
+ )
28
+ .join(',') || `${metric} as metric`;
29
+ const sql = `select ${xCol}, ${metricData}
30
+ from ${table}
31
+ where ${where}
32
+ ${xCol ? `group by ${xCol}` : ''}
33
+ ${order || xCol ? `order by ${order || xCol}` : ''}
34
+ ${samples ? 'limit 10' : 'limit 100'}`;
35
+ return sql;
36
+ }
37
+
38
+ function text() {
39
+ return undefined;
40
+ }
41
+
42
+ export default { number, chart };
@@ -1,56 +1,59 @@
1
- function normalizeData(data, query = {}, columnTypes = []) {
2
- ['x', 'groupby', 'granularity'].forEach((el) => {
3
- // console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
4
- if (!columnTypes.find((col) => col.name == query[el])) {
5
- delete query[el];
6
- }
7
- });
8
-
9
- if (
10
- !columnTypes.find(
11
- (col) => col.type === 'numeric' && col.name == query.metric
12
- )
13
- ) {
14
- delete query.metric;
15
- }
16
-
17
- const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
18
- const xTYpe = columnTypes.find((el) => el.name == xName)?.type;
19
-
20
- const granularity =
21
- xTYpe === 'date' || xTYpe?.includes('timestamp')
22
- ? query.granularity || data.granularity || 'year'
23
- : null;
24
-
25
- const x =
26
- (granularity
27
- ? `date_trunc('${granularity}',${xName})::date::text`
28
- : null) || xName;
29
-
30
- const metrics = Array.isArray(data.metrics) ? data.metrics : [data.metrics];
31
- const metric =
32
- (query.metric ? `sum(${query.metric})` : null) ||
33
- (metrics.length
34
- ? metrics
35
- ?.filter((el) => el)
36
- .map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)
37
- : 'count(*)');
38
-
39
- const { cls, table, filterCustom } = data;
40
- const groupby = query.groupby || data.groupby;
41
- // const orderby = query.orderby || data.orderby || 'count(*)';
42
-
43
- const custom = query?.filterCustom
44
- ?.split(',')
45
- ?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
46
- ?.filter((el) => el)
47
- ?.join(' and ');
48
- const where = `${data.query || '1=1'} and ${custom || 'true'}`;
49
-
50
- const tableSQL = data.tableSQL?.length
51
- ? `(select * from ${data?.table} t ${data.tableSQL.join(' \n ')} where ${where})q`
52
- : undefined;
53
-
54
- return { x, cls, metric, table, where, tableSQL, groupby, xName };
55
- }
56
- export default normalizeData;
1
+ function normalizeData(data, query = {}, columnTypes = []) {
2
+ ['x', 'groupby', 'granularity'].forEach((el) => {
3
+ // console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
4
+ if (!columnTypes.find((col) => col.name == query[el])) {
5
+ delete query[el];
6
+ }
7
+ });
8
+
9
+ if (
10
+ !columnTypes.find(
11
+ (col) => col.type === 'numeric' && col.name == query.metric
12
+ )
13
+ ) {
14
+ delete query.metric;
15
+ }
16
+
17
+ const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
18
+ const xType = columnTypes.find((el) => el.name == xName)?.type;
19
+
20
+ const granularity =
21
+ xType === 'date' || xType?.includes('timestamp')
22
+ ? query.granularity || data.granularity || 'year'
23
+ : null;
24
+
25
+ const x =
26
+ (granularity
27
+ ? `date_trunc('${granularity}',${xName})::date::text`
28
+ : null) || xName;
29
+
30
+ const metrics = Array.isArray(data.metrics) ? data.metrics : [data.metrics];
31
+ const metric =
32
+ (query.metric ? `sum(${query.metric})` : null) ||
33
+ (metrics.length
34
+ ? (metrics
35
+ ?.filter((el) => el && columnTypes.find((col) => col.name == (el?.name || el)))
36
+ ?.map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)?.join(',') || 'count(*)')
37
+ : 'count(*)');
38
+
39
+ const yName = metrics?.[0]?.name || metrics?.[0];
40
+ const yType = columnTypes.find((el) => el.name == yName)?.type;
41
+
42
+ const { cls, table, filterCustom } = data;
43
+ const groupby = query.groupby || data.groupby;
44
+ // const orderby = query.orderby || data.orderby || 'count(*)';
45
+
46
+ const custom = query?.filterCustom
47
+ ?.split(',')
48
+ ?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
49
+ ?.filter((el) => el)
50
+ ?.join(' and ');
51
+ const where = `${data.query || '1=1'} and ${custom || 'true'}`;
52
+
53
+ const tableSQL = data.tableSQL?.length
54
+ ? `(select * from ${data?.table} t ${data.tableSQL.join(' \n ')} where ${where})q`
55
+ : undefined;
56
+
57
+ return { x, cls, metric, table, where, tableSQL, groupby, xName, xType, yName, yType };
58
+ }
59
+ export default normalizeData;
@@ -1,24 +1,29 @@
1
- import config from '../../../config.js';
2
-
3
- import data from './controllers/data.js';
4
-
5
- const biSchema = {
6
- querystring: {
7
- widget: { type: 'string', pattern: '^([\\d\\w]+)$' },
8
- dashboard: { type: 'string', pattern: '^([\\d\\w]+)$' },
9
- sql: { type: 'string', pattern: '^([\\d])$' },
10
- },
11
- params: {
12
- id: { type: 'string', pattern: '^([\\d\\w]+)$' },
13
- },
14
- };
15
-
16
- export default async function route(fastify, opts) {
17
- const prefix = opts?.prefix || config.prefix || '/api';
18
- fastify.route({
19
- method: 'GET',
20
- url: '/bi-data',
21
- schema: biSchema,
22
- handler: data,
23
- });
24
- }
1
+ import config from '../../../config.js';
2
+
3
+ import data from './controllers/data.js';
4
+
5
+ const biSchema = {
6
+ querystring: {
7
+ widget: { type: 'string', pattern: '^([\\d\\w_]+)$' },
8
+ dashboard: { type: 'string', pattern: '^([\\d\\w_]+)$' },
9
+ sql: { type: 'string', pattern: '^([\\d])$' },
10
+ // metric: { type: 'string', pattern: '^([\\d\\w_]+)$' },
11
+ x: { type: 'string', pattern: '^([\\d\\w_]+)$' },
12
+ granularity: { type: 'string', pattern: '^(week|month|quarter|year)$' },
13
+ groupby: { type: 'string', pattern: '^([\\d\\w_]+)$' },
14
+ filterCustom: { type: 'string', pattern: '^([\\d\\w_,]+)$' },
15
+ },
16
+ params: {
17
+ id: { type: 'string', pattern: '^([\\d\\w]+)$' },
18
+ },
19
+ };
20
+
21
+ export default async function route(fastify, opts) {
22
+ const prefix = opts?.prefix || config.prefix || '/api';
23
+ fastify.route({
24
+ method: 'GET',
25
+ url: '/bi-data',
26
+ schema: biSchema,
27
+ handler: data,
28
+ });
29
+ }