@opengis/bi 1.2.32 → 1.2.34

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 (74) hide show
  1. package/README.md +92 -92
  2. package/dist/bi.js +1 -1
  3. package/dist/bi.umd.cjs +68 -68
  4. package/dist/{import-file-D-ISqB7l.js → import-file-BwxPX622.js} +1132 -1079
  5. package/dist/style.css +1 -1
  6. package/dist/{vs-funnel-bar-aoZzvriV.js → vs-funnel-bar-BV18EA4K.js} +3 -3
  7. package/dist/{vs-list-CBkyJSBj.js → vs-list-WrrWQ5KF.js} +32 -53
  8. package/dist/{vs-map-C3C11qmT.js → vs-map-CYd9vdrd.js} +3 -3
  9. package/dist/{vs-map-cluster-BsPmHIMx.js → vs-map-cluster-5TnGPuBH.js} +3 -3
  10. package/dist/{vs-number-d58ftpH5.js → vs-number-DUeRr7uz.js} +3 -3
  11. package/dist/{vs-table-BHa5Velm.js → vs-table-CZJnXfUv.js} +6 -6
  12. package/dist/{vs-text-Bq87gMTx.js → vs-text-D3nCPkgk.js} +4 -4
  13. package/package.json +77 -77
  14. package/plugin.js +21 -21
  15. package/server/helpers/mdToHTML.js +17 -17
  16. package/server/migrations/bi.dataset.sql +46 -46
  17. package/server/migrations/bi.sql +114 -114
  18. package/server/plugins/docs.js +48 -48
  19. package/server/plugins/hook.js +89 -89
  20. package/server/routes/dashboard/controllers/dashboard.import.js +103 -103
  21. package/server/routes/dashboard/controllers/dashboard.js +158 -158
  22. package/server/routes/dashboard/controllers/dashboard.list.js +60 -60
  23. package/server/routes/dashboard/controllers/utils/yaml.js +11 -11
  24. package/server/routes/dashboard/index.mjs +26 -26
  25. package/server/routes/data/controllers/data.js +230 -230
  26. package/server/routes/data/controllers/util/chartSQL.js +49 -49
  27. package/server/routes/data/controllers/util/normalizeData.js +65 -65
  28. package/server/routes/data/index.mjs +38 -38
  29. package/server/routes/dataset/controllers/bi.dataset.list.js +29 -29
  30. package/server/routes/dataset/controllers/bi.db.list.js +19 -19
  31. package/server/routes/dataset/controllers/comment.js +55 -55
  32. package/server/routes/dataset/controllers/createDatasetPost.js +134 -134
  33. package/server/routes/dataset/controllers/data.js +149 -149
  34. package/server/routes/dataset/controllers/dbTablePreview.js +58 -58
  35. package/server/routes/dataset/controllers/dbTables.js +34 -34
  36. package/server/routes/dataset/controllers/delete.js +40 -40
  37. package/server/routes/dataset/controllers/deleteDataset.js +52 -52
  38. package/server/routes/dataset/controllers/editDataset.js +90 -90
  39. package/server/routes/dataset/controllers/export.js +214 -214
  40. package/server/routes/dataset/controllers/form.js +99 -99
  41. package/server/routes/dataset/controllers/format.js +46 -46
  42. package/server/routes/dataset/controllers/insert.js +47 -47
  43. package/server/routes/dataset/controllers/table.js +68 -68
  44. package/server/routes/dataset/controllers/update.js +43 -43
  45. package/server/routes/dataset/index.mjs +132 -132
  46. package/server/routes/dataset/utils/convertJSONToCSV.js +17 -17
  47. package/server/routes/dataset/utils/convertJSONToXls.js +47 -47
  48. package/server/routes/dataset/utils/createTableQuery.js +59 -59
  49. package/server/routes/dataset/utils/datasetForms.js +1 -1
  50. package/server/routes/dataset/utils/descriptionList.js +45 -45
  51. package/server/routes/dataset/utils/downloadRemoteFile.js +58 -58
  52. package/server/routes/dataset/utils/executeQuery.js +46 -46
  53. package/server/routes/dataset/utils/getLayersData.js +106 -106
  54. package/server/routes/dataset/utils/getTableData.js +46 -46
  55. package/server/routes/dataset/utils/insertDataQuery.js +12 -12
  56. package/server/routes/dataset/utils/metaFormat.js +24 -24
  57. package/server/routes/edit/controllers/dashboard.add.js +36 -36
  58. package/server/routes/edit/controllers/dashboard.delete.js +39 -39
  59. package/server/routes/edit/controllers/dashboard.edit.js +61 -61
  60. package/server/routes/edit/controllers/widget.add.js +78 -78
  61. package/server/routes/edit/controllers/widget.del.js +58 -58
  62. package/server/routes/edit/controllers/widget.edit.js +115 -115
  63. package/server/routes/edit/index.mjs +33 -33
  64. package/server/routes/map/controllers/cluster.js +125 -125
  65. package/server/routes/map/controllers/clusterVtile.js +166 -166
  66. package/server/routes/map/controllers/geojson.js +127 -127
  67. package/server/routes/map/controllers/heatmap.js +118 -118
  68. package/server/routes/map/controllers/map.js +69 -69
  69. package/server/routes/map/controllers/utils/downloadClusterData.js +44 -44
  70. package/server/routes/map/controllers/vtile.js +183 -183
  71. package/server/routes/map/index.mjs +32 -32
  72. package/server/templates/page/login.html +58 -58
  73. package/server/utils/getWidget.js +118 -118
  74. package/utils.js +12 -12
@@ -1,65 +1,65 @@
1
- function normalizeData(data, query = {}, columnTypes = []) {
2
- const skip = [];
3
- ['x', 'groupby', 'granularity'].forEach((el) => {
4
- // console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
5
- if (!columnTypes.find((col) => col.name == query[el])) {
6
- if (query[el] && query[el] !== 'null') {
7
- if (el === 'granularity' && !['week', 'month', 'quarter', 'year'].includes(query[el])) {
8
- skip.push(`invalid granularity option: ${query[el]}`);
9
- } else if (el !== 'granularity') { skip.push(`column not found: ${query[el]}`); }
10
- }
11
- if (!(el === 'granularity' || (el === 'groupby' && query[el] === 'null'))) delete query[el];
12
- }
13
- });
14
-
15
- if (
16
- !columnTypes.find(
17
- (col) => col.type === 'numeric' && col.name == query.metric
18
- )
19
- ) {
20
- delete query.metric;
21
- }
22
-
23
- const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
24
- const xType = columnTypes.find((el) => el.name == xName)?.type;
25
-
26
- const granularity =
27
- xType === 'date' || xType?.includes('timestamp')
28
- ? query.granularity || data.granularity || 'year'
29
- : null;
30
-
31
- const x =
32
- (granularity
33
- ? `date_trunc('${granularity}',${xName})::date::text`
34
- : null) || xName;
35
-
36
- const metrics = Array.isArray(data.metrics || data.metric) ? (data.metrics || data.metric) : [data.metrics || data.metric];
37
- const metric =
38
- (query.metric ? `sum(${query.metric})` : null) ||
39
- (metrics.length
40
- ? (metrics
41
- ?.filter((el) => el && columnTypes.find((col) => col.name == (el?.name || el)))
42
- ?.map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)?.join(',') || 'count(*)')
43
- : 'count(*)');
44
-
45
- const yName = metrics?.[0]?.name || metrics?.[0];
46
- const yType = columnTypes.find((el) => el.name == yName)?.type;
47
-
48
- const { cls, groupbyCls, table, filterCustom } = data;
49
- const groupby = (query.groupby || data.groupby) === 'null' ? null : (query.groupby || data.groupby);
50
- // const orderby = query.orderby || data.orderby || 'count(*)';
51
-
52
- const custom = query?.filterCustom
53
- ?.split(',')
54
- ?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
55
- ?.filter((el) => el)
56
- ?.join(' and ');
57
- const where = `${data.query || '1=1'} and ${custom || 'true'}`;
58
-
59
- const tableSQL = data.tableSQL?.length
60
- ? `(select * from ${data?.table} t ${data.tableSQL || ''} where ${where})q`
61
- : undefined;
62
-
63
- return { x, cls, groupbyCls, metric, table, where, tableSQL, groupby, xName, xType, yName, yType, error: skip.length ? skip.join(',') : undefined };
64
- }
65
- export default normalizeData;
1
+ function normalizeData(data, query = {}, columnTypes = []) {
2
+ const skip = [];
3
+ ['x', 'groupby', 'granularity'].forEach((el) => {
4
+ // console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
5
+ if (!columnTypes.find((col) => col.name == query[el])) {
6
+ if (query[el] && query[el] !== 'null') {
7
+ if (el === 'granularity' && !['week', 'month', 'quarter', 'year'].includes(query[el])) {
8
+ skip.push(`invalid granularity option: ${query[el]}`);
9
+ } else if (el !== 'granularity') { skip.push(`column not found: ${query[el]}`); }
10
+ }
11
+ if (!(el === 'granularity' || (el === 'groupby' && query[el] === 'null'))) delete query[el];
12
+ }
13
+ });
14
+
15
+ if (
16
+ !columnTypes.find(
17
+ (col) => col.type === 'numeric' && col.name == query.metric
18
+ )
19
+ ) {
20
+ delete query.metric;
21
+ }
22
+
23
+ const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
24
+ const xType = columnTypes.find((el) => el.name == xName)?.type;
25
+
26
+ const granularity =
27
+ xType === 'date' || xType?.includes('timestamp')
28
+ ? query.granularity || data.granularity || 'year'
29
+ : null;
30
+
31
+ const x =
32
+ (granularity
33
+ ? `date_trunc('${granularity}',${xName})::date::text`
34
+ : null) || xName;
35
+
36
+ const metrics = Array.isArray(data.metrics || data.metric) ? (data.metrics || data.metric) : [data.metrics || data.metric];
37
+ const metric =
38
+ (query.metric ? `sum(${query.metric})` : null) ||
39
+ (metrics.length
40
+ ? (metrics
41
+ ?.filter((el) => el && columnTypes.find((col) => col.name == (el?.name || el)))
42
+ ?.map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)?.join(',') || 'count(*)')
43
+ : 'count(*)');
44
+
45
+ const yName = metrics?.[0]?.name || metrics?.[0];
46
+ const yType = columnTypes.find((el) => el.name == yName)?.type;
47
+
48
+ const { cls, groupbyCls, table, filterCustom } = data;
49
+ const groupby = (query.groupby || data.groupby) === 'null' ? null : (query.groupby || data.groupby);
50
+ // const orderby = query.orderby || data.orderby || 'count(*)';
51
+
52
+ const custom = query?.filterCustom
53
+ ?.split(',')
54
+ ?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
55
+ ?.filter((el) => el)
56
+ ?.join(' and ');
57
+ const where = `${data.query || '1=1'} and ${custom || 'true'}`;
58
+
59
+ const tableSQL = data.tableSQL?.length
60
+ ? `(select * from ${data?.table} t ${data.tableSQL || ''} where ${where})q`
61
+ : undefined;
62
+
63
+ return { x, cls, groupbyCls, metric, table, where, tableSQL, groupby, xName, xType, yName, yType, error: skip.length ? skip.join(',') : undefined };
64
+ }
65
+ export default normalizeData;
@@ -1,38 +1,38 @@
1
- import data from './controllers/data.js';
2
-
3
- const biSchema = {
4
- type: 'object',
5
- properties: {
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
-
22
- const policy = ['public'];
23
-
24
- export default async function route(fastify, opts) {
25
- fastify.route({
26
- method: 'GET',
27
- url: '/bi-data',
28
- schema: biSchema,
29
- config: {
30
- policy,
31
- rateLimit: {
32
- max: 3000,
33
- timeWindow: '1 minute',
34
- }
35
- },
36
- handler: data,
37
- });
38
- }
1
+ import data from './controllers/data.js';
2
+
3
+ const biSchema = {
4
+ type: 'object',
5
+ properties: {
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
+
22
+ const policy = ['public'];
23
+
24
+ export default async function route(fastify, opts) {
25
+ fastify.route({
26
+ method: 'GET',
27
+ url: '/bi-data',
28
+ schema: biSchema,
29
+ config: {
30
+ policy,
31
+ rateLimit: {
32
+ max: 3000,
33
+ timeWindow: '1 minute',
34
+ }
35
+ },
36
+ handler: data,
37
+ });
38
+ }
@@ -1,29 +1,29 @@
1
- import { pgClients } from "@opengis/fastify-table/utils.js";
2
-
3
- const maxLimit = 100;
4
-
5
- /**
6
- * Отримання списку наборів даних BI
7
- *
8
- * @method GET
9
- * @summary Отримання списку наборів даних BI
10
- * @priority 4
11
- * @alias biDatasetList
12
- * @type api
13
- * @tag bi
14
- * @errors 400,500
15
- * @returns {Number} status Номер помилки
16
- * @returns {String} error Опис помилки
17
- * @returns {Object} rows Масив з колонками таблиці
18
- */
19
-
20
- export default async function biDatasetList({ pg = pgClients.client, query = {} }) {
21
-
22
- const limit = Math.min(maxLimit, +(query.limit || 20));
23
- const offset = query.page && query.page > 0 ? (query.page - 1) * limit : 0;
24
-
25
- const { rows = [] } = await pg.query(`select dataset_id as id, name, source_type as source, table_name as table, dashboard_list
26
- from bi.dataset order by name limit ${limit} offset ${offset}`);
27
-
28
- return { rows };
29
- };
1
+ import { pgClients } from "@opengis/fastify-table/utils.js";
2
+
3
+ const maxLimit = 100;
4
+
5
+ /**
6
+ * Отримання списку наборів даних BI
7
+ *
8
+ * @method GET
9
+ * @summary Отримання списку наборів даних BI
10
+ * @priority 4
11
+ * @alias biDatasetList
12
+ * @type api
13
+ * @tag bi
14
+ * @errors 400,500
15
+ * @returns {Number} status Номер помилки
16
+ * @returns {String} error Опис помилки
17
+ * @returns {Object} rows Масив з колонками таблиці
18
+ */
19
+
20
+ export default async function biDatasetList({ pg = pgClients.client, query = {} }) {
21
+
22
+ const limit = Math.min(maxLimit, +(query.limit || 20));
23
+ const offset = query.page && query.page > 0 ? (query.page - 1) * limit : 0;
24
+
25
+ const { rows = [] } = await pg.query(`select dataset_id as id, name, source_type as source, table_name as table, dashboard_list
26
+ from bi.dataset order by name limit ${limit} offset ${offset}`);
27
+
28
+ return { rows };
29
+ };
@@ -1,19 +1,19 @@
1
- import { config } from '@opengis/fastify-table/utils.js';
2
-
3
- /**
4
- * Список БД
5
- *
6
- * @method POST
7
- * @priority 4
8
- * @alias biDbList
9
- * @type api
10
- * @tag bi
11
- * @errors 400,500
12
- * @returns {Number} status Номер помилки
13
- * @returns {String} error Опис помилки
14
- * @returns {Object} rows Масив з колонками таблиці
15
- */
16
-
17
- export default async function biDbList(req) {
18
- return { db_list: config.db_list?.map((el) => ({ id: el.key, text: el.title })) || [] };
19
- };
1
+ import { config } from '@opengis/fastify-table/utils.js';
2
+
3
+ /**
4
+ * Список БД
5
+ *
6
+ * @method POST
7
+ * @priority 4
8
+ * @alias biDbList
9
+ * @type api
10
+ * @tag bi
11
+ * @errors 400,500
12
+ * @returns {Number} status Номер помилки
13
+ * @returns {String} error Опис помилки
14
+ * @returns {Object} rows Масив з колонками таблиці
15
+ */
16
+
17
+ export default async function biDbList(req) {
18
+ return { db_list: config.db_list?.map((el) => ({ id: el.key, text: el.title })) || [] };
19
+ };
@@ -1,55 +1,55 @@
1
- import { pgClients } from '@opengis/fastify-table/utils.js';
2
-
3
- export default async function datasetEditComment({
4
- pg = pgClients.client,
5
- body = {},
6
- params = {},
7
- }) {
8
- if (!params?.id) {
9
- return { message: 'not enough params: id', status: 400 };
10
- }
11
-
12
- if (!Array.isArray(body.data) || !body.data?.length) {
13
- return { message: 'invalid param: body.data not an array / empty', status: 400 };
14
- }
15
-
16
- const dataset = await pg.query('select dataset_id as id, table_name as table from bi.dataset where dataset_id=$1', [params.id])
17
- .then(el => el.rows?.[0] || {});
18
-
19
- if (!dataset?.table) {
20
- return { message: dataset?.id ? 'dataset table not set' : 'dataset not found', status: 404 };
21
- }
22
-
23
- const { fields = [] } = pg.pk?.[dataset.table] ? await pg.query(`select * from ${dataset.table} limit 0`) : {};
24
-
25
- if (!fields.length) {
26
- return { message: `table not found: ${dataset.table}`, status: 404 };
27
- }
28
-
29
- const columnList = fields.map(el => el.name);
30
- const validData = body.data?.filter?.(el => el?.name && el?.comment && columnList.includes(el.name));
31
-
32
- const q1 = validData.map(el => `COMMENT ON COLUMN ${dataset.table}."${el.name.replace(/'/g, "''")}" IS '${el.comment.replace(/'/g, "''")}'`).join(';');
33
- if (q1) await pg.query(q1);
34
-
35
- const q2 = `select
36
- attrelid::regclass,
37
- attname,
38
- pg_catalog.col_description(attrelid,attnum) as title
39
- from pg_catalog.pg_attribute a
40
- where attnum > 0
41
- and attname not in ('editor_id','editor_date','cdate','geom','id','uid','cdate')
42
- and atttypid::regtype not in ('json','geometry')
43
- and pg_catalog.col_description(attrelid,attnum) is not null`;
44
-
45
- const { rows = [] } = await pg.query(q2);
46
-
47
- const title = rows
48
- .filter(el => el.attrelid === dataset.table)
49
- .reduce((p, el) => ({ ...p, [el.attname]: el.title }), {});
50
-
51
- const res = await pg.query(`update bi.dataset set setting=coalesce(setting::jsonb, '{}'::jsonb)||$1::jsonb
52
- where dataset_id=$2 returning dataset_id as id, setting`, [{ title }, dataset.id]);
53
-
54
- return res.rows?.[0];
55
- }
1
+ import { pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function datasetEditComment({
4
+ pg = pgClients.client,
5
+ body = {},
6
+ params = {},
7
+ }) {
8
+ if (!params?.id) {
9
+ return { message: 'not enough params: id', status: 400 };
10
+ }
11
+
12
+ if (!Array.isArray(body.data) || !body.data?.length) {
13
+ return { message: 'invalid param: body.data not an array / empty', status: 400 };
14
+ }
15
+
16
+ const dataset = await pg.query('select dataset_id as id, table_name as table from bi.dataset where dataset_id=$1', [params.id])
17
+ .then(el => el.rows?.[0] || {});
18
+
19
+ if (!dataset?.table) {
20
+ return { message: dataset?.id ? 'dataset table not set' : 'dataset not found', status: 404 };
21
+ }
22
+
23
+ const { fields = [] } = pg.pk?.[dataset.table] ? await pg.query(`select * from ${dataset.table} limit 0`) : {};
24
+
25
+ if (!fields.length) {
26
+ return { message: `table not found: ${dataset.table}`, status: 404 };
27
+ }
28
+
29
+ const columnList = fields.map(el => el.name);
30
+ const validData = body.data?.filter?.(el => el?.name && el?.comment && columnList.includes(el.name));
31
+
32
+ const q1 = validData.map(el => `COMMENT ON COLUMN ${dataset.table}."${el.name.replace(/'/g, "''")}" IS '${el.comment.replace(/'/g, "''")}'`).join(';');
33
+ if (q1) await pg.query(q1);
34
+
35
+ const q2 = `select
36
+ attrelid::regclass,
37
+ attname,
38
+ pg_catalog.col_description(attrelid,attnum) as title
39
+ from pg_catalog.pg_attribute a
40
+ where attnum > 0
41
+ and attname not in ('editor_id','editor_date','cdate','geom','id','uid','cdate')
42
+ and atttypid::regtype not in ('json','geometry')
43
+ and pg_catalog.col_description(attrelid,attnum) is not null`;
44
+
45
+ const { rows = [] } = await pg.query(q2);
46
+
47
+ const title = rows
48
+ .filter(el => el.attrelid === dataset.table)
49
+ .reduce((p, el) => ({ ...p, [el.attname]: el.title }), {});
50
+
51
+ const res = await pg.query(`update bi.dataset set setting=coalesce(setting::jsonb, '{}'::jsonb)||$1::jsonb
52
+ where dataset_id=$2 returning dataset_id as id, setting`, [{ title }, dataset.id]);
53
+
54
+ return res.rows?.[0];
55
+ }