@opengis/bi 1.0.21 → 1.0.23

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 (71) hide show
  1. package/dist/bi.js +1 -1
  2. package/dist/bi.umd.cjs +109 -106
  3. package/dist/{import-file-C8BY90-b.js → import-file-DgBd_UN1.js} +20484 -18794
  4. package/dist/{map-component-mixin-CFtShPun.js → map-component-mixin-NewmNy_M.js} +502 -483
  5. package/dist/style.css +1 -1
  6. package/dist/{vs-calendar-B9vXdsaG.js → vs-calendar-CPXj4hBh.js} +1 -1
  7. package/dist/vs-donut-CUmi2ir5.js +148 -0
  8. package/dist/{vs-funnel-bar-Cj0O8tIf.js → vs-funnel-bar-B3DpbtUl.js} +1 -1
  9. package/dist/{vs-heatmap-C9oFph_f.js → vs-heatmap-COwT3bHE.js} +1 -1
  10. package/dist/{vs-map-WOn0RAU7.js → vs-map-DwyQHLpN.js} +2 -2
  11. package/dist/{vs-map-cluster-RJa6sNfI.js → vs-map-cluster-CnZ9g6k-.js} +2 -2
  12. package/dist/{vs-number-BG0szZL-.js → vs-number-LwROg9Oe.js} +5 -5
  13. package/dist/vs-table-Dt_MSaCC.js +68 -0
  14. package/dist/{vs-text-Kwl3-0yy.js → vs-text-DgAf3Ids.js} +2 -2
  15. package/package.json +6 -5
  16. package/plugin.js +1 -1
  17. package/server/helpers/mdToHTML.js +17 -0
  18. package/server/migrations/bi.dataset.sql +13 -0
  19. package/server/migrations/bi.sql +2 -0
  20. package/server/routes/dashboard/controllers/dashboard.js +10 -11
  21. package/server/routes/dashboard/controllers/dashboard.list.js +6 -6
  22. package/server/routes/data/controllers/data.js +14 -6
  23. package/server/routes/data/controllers/util/chartSQL.js +6 -3
  24. package/server/routes/dataset/controllers/bi.dataset.list.js +3 -1
  25. package/server/routes/dataset/controllers/comment.js +55 -0
  26. package/server/routes/dataset/controllers/createDatasetPost.js +132 -0
  27. package/server/routes/dataset/controllers/data.js +145 -0
  28. package/server/routes/{db → dataset}/controllers/dbTablePreview.js +17 -24
  29. package/server/routes/{db → dataset}/controllers/dbTables.js +7 -11
  30. package/server/routes/dataset/controllers/delete.js +39 -0
  31. package/server/routes/dataset/controllers/{bi.dataset.edit.js → editDataset.js} +32 -26
  32. package/server/routes/dataset/controllers/export.js +213 -0
  33. package/server/routes/dataset/controllers/form.js +99 -0
  34. package/server/routes/dataset/controllers/format.js +44 -0
  35. package/server/routes/dataset/controllers/insert.js +46 -0
  36. package/server/routes/dataset/controllers/table.js +69 -0
  37. package/server/routes/dataset/controllers/update.js +42 -0
  38. package/server/routes/dataset/index.mjs +88 -43
  39. package/server/routes/dataset/utils/convertJSONToCSV.js +17 -0
  40. package/server/routes/dataset/utils/convertJSONToXls.js +49 -0
  41. package/server/routes/dataset/utils/createTableQuery.js +59 -0
  42. package/server/routes/dataset/utils/datasetForms.js +1 -0
  43. package/server/routes/dataset/utils/descriptionList.js +46 -0
  44. package/server/routes/dataset/utils/downloadRemoteFile.js +58 -0
  45. package/server/routes/dataset/utils/executeQuery.js +46 -0
  46. package/server/routes/dataset/utils/getLayersData.js +107 -0
  47. package/server/routes/dataset/utils/getTableData.js +47 -0
  48. package/server/routes/dataset/utils/insertDataQuery.js +12 -0
  49. package/server/routes/dataset/utils/metaFormat.js +24 -0
  50. package/server/routes/edit/controllers/dashboard.add.js +3 -3
  51. package/server/routes/edit/controllers/widget.add.js +8 -3
  52. package/server/routes/edit/controllers/widget.edit.js +23 -5
  53. package/server/routes/map/controllers/cluster.js +41 -41
  54. package/server/routes/map/controllers/clusterVtile.js +5 -5
  55. package/server/routes/map/controllers/geojson.js +6 -6
  56. package/server/routes/map/controllers/heatmap.js +118 -0
  57. package/server/routes/map/controllers/map.js +3 -3
  58. package/server/routes/map/controllers/utils/downloadClusterData.js +6 -4
  59. package/server/routes/map/controllers/vtile.js +3 -3
  60. package/server/routes/map/index.mjs +2 -0
  61. package/server/utils/getWidget.js +10 -6
  62. package/server/routes/dataset/controllers/bi.dataset.add.js +0 -86
  63. package/server/routes/dataset/controllers/bi.dataset.data.add.js +0 -49
  64. package/server/routes/dataset/controllers/bi.dataset.data.del.js +0 -54
  65. package/server/routes/dataset/controllers/bi.dataset.data.edit.js +0 -55
  66. package/server/routes/dataset/controllers/bi.dataset.data.list.js +0 -71
  67. package/server/routes/dataset/controllers/bi.dataset.del.js +0 -48
  68. package/server/routes/dataset/controllers/bi.dataset.demo.add.js +0 -97
  69. package/server/routes/dataset/controllers/util/create.table.js +0 -21
  70. package/server/routes/dataset/controllers/util/prepare.data.js +0 -49
  71. package/server/routes/db/index.mjs +0 -17
@@ -1,4 +1,4 @@
1
- import { getFilterSQL } from '@opengis/fastify-table/utils.js';
1
+ import { getFilterSQL, logger, pgClients } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  import { getWidget } from '../../../../utils.js';
4
4
 
@@ -6,58 +6,58 @@ import downloadClusterData from './utils/downloadClusterData.js';
6
6
 
7
7
  const clusterExists = {};
8
8
 
9
- export default async function cluster({ pg, query = {}, log }) {
9
+ export default async function cluster({ query = {} }) {
10
10
  const { widget, filter, dashboard, search } = query;
11
11
 
12
12
  if (!widget) {
13
13
  return { message: 'not enough params: widget', status: 400 };
14
14
  }
15
15
 
16
- const { data } = await getWidget({ dashboard, widget });
16
+ const { pg = pgClients.client, data } = await getWidget({ dashboard, widget });
17
17
 
18
- try {
19
- const pkey = pg.pk?.[data?.table];
20
-
21
- if (!pkey) {
22
- return {
23
- message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
24
- status: 400,
25
- };
26
- }
18
+ const pkey = pg.pk?.[data?.table];
27
19
 
28
- // data param
29
- const {
30
- table,
31
- query: where = '1=1',
32
- metrics = [],
33
- cluster,
34
- clusterTable = {},
35
- } = data;
20
+ if (!pkey) {
21
+ return {
22
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
23
+ status: 400,
24
+ };
25
+ }
36
26
 
37
- if (!cluster) {
38
- return {
39
- message: `invalid ${widget ? 'widget' : 'dashboard'}: cluster column not specified`,
40
- status: 400,
41
- };
42
- }
27
+ // data param
28
+ const {
29
+ table,
30
+ query: where = '1=1',
31
+ metrics = [],
32
+ cluster,
33
+ clusterTable = {},
34
+ } = data;
35
+
36
+ if (!cluster) {
37
+ return {
38
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: cluster column not specified`,
39
+ status: 400,
40
+ };
41
+ }
43
42
 
44
- if (!metrics.length) {
45
- return {
46
- message: `invalid ${widget ? 'widget' : 'dashboard'}: metric columns not found`,
47
- status: 400,
48
- };
49
- }
43
+ if (!metrics.length) {
44
+ return {
45
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: metric columns not found`,
46
+ status: 400,
47
+ };
48
+ }
50
49
 
51
- if (!clusterTable?.name) {
52
- Object.assign(clusterTable, {
53
- name: 'bi.cluster',
54
- title: 'title',
55
- query: `type='${cluster}'`,
56
- });
57
- }
50
+ if (!clusterTable?.name) {
51
+ Object.assign(clusterTable, {
52
+ name: 'bi.cluster',
53
+ title: 'title',
54
+ query: `type='${cluster}'`,
55
+ });
56
+ }
58
57
 
58
+ try {
59
59
  if (cluster && !clusterExists[cluster]) {
60
- const res = await downloadClusterData({ pg, log, cluster });
60
+ const res = await downloadClusterData({ pg, cluster });
61
61
  if (res) return res;
62
62
  clusterExists[cluster] = 1;
63
63
  }
@@ -103,7 +103,7 @@ export default async function cluster({ pg, query = {}, log }) {
103
103
  ];
104
104
  return { sizes, rows, bounds, extent, count: rows.length, total: rows?.reduce((acc, curr) => (curr.metric || 0) + acc, 0) };
105
105
  } catch (err) {
106
- log.error('bi/cluster', { error: err.toString(), query });
106
+ logger.file('bi/cluster/error', { error: err.toString(), query });
107
107
  return { error: err.toString(), status: 500 };
108
108
  }
109
109
  }
@@ -4,7 +4,7 @@ import path from 'path';
4
4
  import { createHash } from 'crypto';
5
5
  import { writeFile, mkdir } from 'fs/promises';
6
6
 
7
- import { getFolder, getFilterSQL, autoIndex } from '@opengis/fastify-table/utils.js';
7
+ import { getFolder, getFilterSQL, autoIndex, pgClients } from '@opengis/fastify-table/utils.js';
8
8
 
9
9
  import { getWidget } from '../../../../utils.js';
10
10
 
@@ -15,7 +15,7 @@ const mercator = new Sphericalmercator({ size: 256 });
15
15
  const clusterExists = {};
16
16
 
17
17
  export default async function clusterVtile(req, reply) {
18
- const { pg, params = {}, query = {}, log } = req;
18
+ const { params = {}, query = {} } = req;
19
19
  const { z, y } = params;
20
20
  const x = params.x?.split('.')[0] - 0;
21
21
 
@@ -30,7 +30,7 @@ export default async function clusterVtile(req, reply) {
30
30
  return { message: 'not enough params: widget', status: 400 };
31
31
  }
32
32
 
33
- const { data } = await getWidget({ dashboard, widget });
33
+ const { pg = pgClients.client, data } = await getWidget({ dashboard, widget });
34
34
 
35
35
  const headers = {
36
36
  'Content-Type': 'application/x-protobuf',
@@ -80,7 +80,7 @@ export default async function clusterVtile(req, reply) {
80
80
  }
81
81
 
82
82
  if (cluster && !clusterExists[data.cluster]) {
83
- const res = await downloadClusterData({ pg, log, cluster });
83
+ const res = await downloadClusterData({ pg, cluster });
84
84
  if (res) return res;
85
85
  clusterExists[cluster] = 1;
86
86
  }
@@ -160,7 +160,7 @@ export default async function clusterVtile(req, reply) {
160
160
 
161
161
  return reply.headers(headers).send(buffer);
162
162
  } catch (err) {
163
- log.error('bi/clusterVtile', { error: err.toString(), query, params });
163
+ logger.file('bi/clusterVtile/error', { error: err.toString(), query, params });
164
164
  return { error: err.toString(), status: 500 };
165
165
  }
166
166
  }
@@ -4,7 +4,7 @@ import { writeFile, mkdir, readFile, stat } from 'fs/promises';
4
4
  import { existsSync, /* readdirSync, */ readFileSync } from 'fs';
5
5
 
6
6
 
7
- import { getFolder, getFilterSQL, autoIndex, logger } from '@opengis/fastify-table/utils.js';
7
+ import { getFolder, getFilterSQL, autoIndex, logger, pgClients } from '@opengis/fastify-table/utils.js';
8
8
 
9
9
  import normalizeData from '../../data/controllers/util/normalizeData.js';
10
10
 
@@ -17,7 +17,7 @@ const types = {
17
17
  const hourMs = 3.6e6;
18
18
 
19
19
  export default async function geojson(req, reply) {
20
- const { pg, query = {} } = req;
20
+ const { query = {} } = req;
21
21
 
22
22
  const {
23
23
  filter,
@@ -38,6 +38,7 @@ export default async function geojson(req, reply) {
38
38
  const data = await getWidget({ dashboard, widget });
39
39
  if (data.status) return data;
40
40
 
41
+ const pg = data.pg || pgClients.client;
41
42
  const hash = [pointZoom, filter].filter((el) => el).join();
42
43
 
43
44
  const root = getFolder(req);
@@ -92,10 +93,9 @@ export default async function geojson(req, reply) {
92
93
 
93
94
  const q1 = `SELECT 'FeatureCollection' As type, json_agg(f) As features FROM (
94
95
  SELECT 'Feature' As type, row_number() over() as id,
95
- st_asgeojson(st_force2d(${
96
- query.srid
97
- ? `st_transform(${type === 'centroid' ? `st_centroid(${geom})` : geom},${query.srid})`
98
- : `${type === 'centroid' || query.point || query.centroid ? `st_centroid(${geom})` : geom}`
96
+ st_asgeojson(st_force2d(${query.srid
97
+ ? `st_transform(${type === 'centroid' ? `st_centroid(${geom})` : geom},${query.srid})`
98
+ : `${type === 'centroid' || query.point || query.centroid ? `st_centroid(${geom})` : geom}`
99
99
  }), 6, 0)::json as geometry,
100
100
  (select row_to_json(tc) from (select ${'' ? `${''} as status, ` : ''}
101
101
  ${xName ? `${xName},` : ''}
@@ -0,0 +1,118 @@
1
+ import path from 'node:path';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync } from 'node:fs';
4
+ import { readFile, writeFile, mkdir, stat } from 'node:fs/promises';
5
+
6
+ import { pgClients, getFilterSQL, getMeta, getFolder } from '@opengis/fastify-table/utils.js';
7
+
8
+ import { getWidget } from '../../../../utils.js';
9
+
10
+ const hourMs = 3.6e6;
11
+ const maxLimit = 2500;
12
+
13
+ export default async function heatmap(req, reply) {
14
+ const { query = {}, user = {} } = req;
15
+
16
+ const { widget, dashboard, filter, search, size = 0.1 } = query;
17
+
18
+ if (query.size && (+query.size || 0) <= 0) {
19
+ return { message: 'param size is invalid', status: 400 };
20
+ }
21
+
22
+ if (!dashboard || !widget) {
23
+ return { message: 'not enough params: dashboard / widget', status: 400 };
24
+ }
25
+
26
+ const { data } = await getWidget({ widget, dashboard });
27
+
28
+ if (!data?.table) {
29
+ return { message: 'widget not found: ' + widget, status: 400 };
30
+ }
31
+
32
+ const limit = Math.min(+query.limit || maxLimit, maxLimit);
33
+ const hash = [search, filter, limit].filter((el) => el).join();
34
+
35
+ const root = getFolder(req, 'local');
36
+ const file = path.join(
37
+ root,
38
+ `/map/geojson/heatmap/${data.table}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}.geojson`
39
+ );
40
+
41
+ if (existsSync(file) && !query.nocache && !query.sql) {
42
+ const timeNow = Date.now();
43
+ const stats = await stat(file);
44
+ const birthTime = new Date(stats.birthtime).getTime();
45
+ if (!(birthTime - timeNow > hourMs * 24)) {
46
+ const geojson = JSON.parse((await readFile(file, 'utf-8')) || {});
47
+ return geojson;
48
+ }
49
+ }
50
+
51
+ const pg = data.pg || pgClients.client;
52
+
53
+ if (!pg.pk?.[data.table]) {
54
+ return { message: `table not found: ${data.table}`, status: 404 };
55
+ }
56
+
57
+ const metric = query.metric || data.metrics?.[0]?.name || (Array.isArray(data.metrics) ? data.metrics?.[0] : data.metrics);
58
+
59
+ const operator = metric
60
+ ? (['sum', 'min', 'max', 'avg'].find(el => el === query.operator) || 'sum')
61
+ : undefined;
62
+
63
+ const aggregator = metric
64
+ ? `${operator}(${metric})`
65
+ : 'count(*)';
66
+
67
+ const { geom, columns } = await getMeta({ pg, table: data.table });
68
+
69
+ const { dataTypeID } = columns.find(col => col.name === metric) || {};
70
+
71
+ if (metric && !dataTypeID) {
72
+ return { message: `metric column not found: ${metric}`, status: 404 };
73
+ }
74
+
75
+ if (!['integer', 'numeric', 'double precision'].includes(pg.pgType[dataTypeID])) {
76
+ return { message: `metric column invalid type: ${metric} (${pg.pgType[dataTypeID]})`, status: 404 };
77
+ }
78
+
79
+ if (!geom) {
80
+ return { message: `geometry column not found: ${data.table}`, status: 404 };
81
+ }
82
+
83
+ const { optimizedSQL = `select * from ${data.table} where 1=1` } = hash ? await getFilterSQL({ pg, table: data.table, filter, search }) : {};
84
+
85
+ const subQuery = `SELECT ${aggregator} AS metric, hex.geom FROM (
86
+ SELECT ST_SetSRID( (ST_HexagonGrid(${size}, ST_Extent(q.${geom})) ).geom, 4326 ) as geom FROM ( ${optimizedSQL})q
87
+ )hex
88
+ LEFT JOIN ( ${optimizedSQL} )pts
89
+ ON ST_Within(pts.${geom}, hex.geom)
90
+
91
+ JOIN ( SELECT ST_ConvexHull(ST_Collect(${geom})) AS mask FROM ( ${optimizedSQL} )q )point_mask
92
+ ON ST_Intersects(hex.geom, point_mask.mask)
93
+
94
+ WHERE 1=1 /*and pts.${geom} is not null AND st_srid(pts.${geom}) > 0*/
95
+ GROUP BY hex.geom
96
+ limit ${limit}`;
97
+
98
+ if (query.sql === '1' && user?.user_type?.includes('admin')) return subQuery;
99
+
100
+ const q = `SELECT 'FeatureCollection' As type, json_agg(f) As features FROM (
101
+ SELECT
102
+ 'Feature' As type,
103
+ row_number() over() as id,
104
+ st_asgeojson(geom, 6, 0)::json as geometry,
105
+ json_build_object( 'metric', metric ) as properties
106
+ from (${subQuery})sq
107
+ )f`;
108
+
109
+ if (query.sql === '2' && user?.user_type?.includes('admin')) return q;
110
+
111
+ const geojson = await pg.query(q)
112
+ .then(el => el.rows?.[0] || {});
113
+
114
+ await mkdir(path.dirname(file), { recursive: true });
115
+ await writeFile(file, JSON.stringify(geojson));
116
+
117
+ return geojson;
118
+ }
@@ -1,11 +1,11 @@
1
- import { getFilterSQL } from '@opengis/fastify-table/utils.js';
1
+ import { pgClients, getFilterSQL } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  import { getWidget } from '../../../../utils.js';
4
4
 
5
- export default async function map({ pg, query = {} }) {
5
+ export default async function map({ query = {} }) {
6
6
  const { dashboard, widget } = query;
7
7
 
8
- const { data, type, layers } = await getWidget({ dashboard, widget });
8
+ const { pg = pgClients.client, data, type, layers } = await getWidget({ dashboard, widget });
9
9
 
10
10
  if (!['map'].includes(type)) {
11
11
  return { message: 'access restricted: invalid widget type', status: 403 };
@@ -1,5 +1,7 @@
1
- export default async function downloadClusterData({ pg, log, cluster }) {
2
- if (!pg || !log || !cluster) return null;
1
+ import { logger, pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function downloadClusterData({ pg = pgClients.client, cluster }) {
4
+ if (!pg || !cluster) return null;
3
5
  const res = await fetch(`https://cdn.softpro.ua/data/bi/${cluster}-ua.geojson`);
4
6
  if (res?.status !== 200) {
5
7
  return {
@@ -28,10 +30,10 @@ export default async function downloadClusterData({ pg, log, cluster }) {
28
30
 
29
31
  const { rowCount } = await pg.query(`insert into bi.cluster (title,type,geom)
30
32
  values ${values} on conflict(title,type) do update set geom=excluded.geom`);
31
- log.info('bi/clusterVtile', { cluster, rowCount });
33
+ logger.file('bi/clusterVtile', { cluster, rowCount });
32
34
  }
33
35
  } catch (err) {
34
- log.error('bi/clusterVtile', {
36
+ logger.file('bi/clusterVtile/error', {
35
37
  error: err.toString(),
36
38
  filename: `${cluster}-ua.json`,
37
39
  });
@@ -6,7 +6,7 @@ import { writeFile, mkdir } from 'fs/promises';
6
6
  import { existsSync, /* readdirSync, */ readFileSync } from 'fs';
7
7
  import { getWidget } from '../../../../utils.js';
8
8
 
9
- import { getFolder, getFilterSQL, logger } from '@opengis/fastify-table/utils.js';
9
+ import { getFolder, getFilterSQL, logger, pgClients } from '@opengis/fastify-table/utils.js';
10
10
 
11
11
  import normalizeData from '../../data/controllers/util/normalizeData.js';
12
12
 
@@ -34,7 +34,7 @@ const area = {
34
34
  };
35
35
 
36
36
  export default async function vtile(req, reply) {
37
- const { pg, params = {}, query = {} } = req;
37
+ const { params = {}, query = {} } = req;
38
38
 
39
39
  const {
40
40
  filter,
@@ -59,7 +59,7 @@ export default async function vtile(req, reply) {
59
59
  return { message: 'not enough params: xyz', status: 400 };
60
60
  }
61
61
 
62
- const { data } = await getWidget({ widget, dashboard });
62
+ const { pg = pgClients.client, data } = await getWidget({ widget, dashboard });
63
63
 
64
64
  const headers = {
65
65
  'Content-Type': 'application/x-protobuf',
@@ -4,6 +4,7 @@ import vtile from './controllers/vtile.js';
4
4
 
5
5
  import cluster from './controllers/cluster.js';
6
6
  import clusterVtile from './controllers/clusterVtile.js';
7
+ import heatmap from './controllers/heatmap.js';
7
8
 
8
9
  const biSchema = {
9
10
  querystring: {
@@ -22,4 +23,5 @@ export default async function route(fastify, opts) {
22
23
  fastify.get('/bi-vtile/:z/:y/:x', { schema: biSchema }, vtile);
23
24
  fastify.get('/bi-cluster', { schema: biSchema }, cluster);
24
25
  fastify.get('/bi-cluster-vtile/:z/:y/:x', { schema: biSchema }, clusterVtile);
26
+ fastify.get('/bi-heatmap', {}, heatmap);
25
27
  }
@@ -1,6 +1,6 @@
1
- import { getTemplate, pgClients } from '@opengis/fastify-table/utils.js';
1
+ import { getTemplate, getPGAsync, pgClients } from '@opengis/fastify-table/utils.js';
2
2
 
3
- const pg = pgClients.client;
3
+ import mdToHTML from '../helpers/mdToHTML.js';
4
4
 
5
5
  async function getWidget({ dashboard, widget }) {
6
6
  if (!dashboard && !widget) {
@@ -10,6 +10,9 @@ async function getWidget({ dashboard, widget }) {
10
10
  ? await getTemplate('dashboard', dashboard)
11
11
  : null;
12
12
 
13
+ const dashboardIndex = dashboardData?.find((el) => el[0] == 'index.yml')?.[1]; // dashboardData?.find((el) => el[2] == 'index')?.[1]
14
+ const pg = dashboardIndex?.db ? await getPGAsync(dashboardIndex?.db) : pgClients.client;
15
+
13
16
  const { id, tableName } =
14
17
  !dashboardData && pg.pk['bi.dashboard'] && dashboard
15
18
  ? await pg
@@ -27,13 +30,13 @@ async function getWidget({ dashboard, widget }) {
27
30
  dashboardData?.forEach((el) => {
28
31
  el[2] = el[0].split('.')[0];
29
32
  });
30
- const dashboardIndex = dashboardData?.find((el) => el[2] == 'index')?.[1];
31
33
 
32
34
  const widgetData = dashboard
33
35
  ? dashboardData?.find((el) => el[2] === (widget || 'index'))?.[1]
34
36
  : await getTemplate('widget', widget);
35
37
  if (typeof widgetData === 'string') {
36
- return { source: widgetData, status: 200 };
38
+ const html = mdToHTML(widgetData);
39
+ return { source: html, status: 200 };
37
40
  }
38
41
  if (!id && !dashboardData && !widgetData) {
39
42
  return { message: `not found ${widget} ${dashboard}`, status: 404 };
@@ -65,9 +68,10 @@ async function getWidget({ dashboard, widget }) {
65
68
  widgetData?.table_name ||
66
69
  dashboardIndex?.table ||
67
70
  dashboardIndex?.table_name,
68
- db: dashboardIndex?.db || pgClients.client?.options?.database,
71
+ db: dashboardIndex?.db || widgetData?.db || pgClients.client?.options?.database,
69
72
  });
70
73
  const main = { ...(dashboardIndex || {}), ...widgetData, ...data, ...data?.data || {} };
74
+ const pg1 = widgetData?.db ? await getPGAsync(widgetData?.db) : pg;
71
75
 
72
76
  if (!main?.table) {
73
77
  return {
@@ -80,6 +84,6 @@ async function getWidget({ dashboard, widget }) {
80
84
  (el, i) => `left join lateral(${el})t${i + 1} on 1=1`
81
85
  );
82
86
 
83
- return { ...main, tableSQL, data, type, text, controls, style, options, yml };
87
+ return { ...main, pg: pg1, tableSQL, data, type, text, controls, style, options, yml };
84
88
  }
85
89
  export default getWidget;
@@ -1,86 +0,0 @@
1
- import { randomBytes } from 'node:crypto';
2
-
3
- import { dataInsert, getPG, pgClients, initPG } from '@opengis/fastify-table/utils.js';
4
-
5
- import createTableFunc from './util/create.table.js';
6
- import prepareData from './util/prepare.data.js';
7
-
8
- async function updatePGMeta(pg, table) {
9
- if (!pg.tlist) await initPG(pg);
10
- if (!pg.tlist.includes(table)) {
11
- const { pks } = await pg.query(`SELECT json_object_agg(c.conrelid::regclass, a.attname) as pks FROM pg_constraint c
12
- left join pg_attribute a on c.conrelid=a.attrelid and a.attnum = c.conkey[1] WHERE c.contype='p'::"char"`).then(el => el.rows[0]) || {};
13
- pg.pk[table] = pks[table];
14
- pg.tlist.push(table);
15
- }
16
- }
17
-
18
- /**
19
- * Додавання нового набору даних BI
20
- *
21
- * @method POST
22
- * @summary Додавання нового набору даних BI
23
- * @priority 4
24
- * @alias biDatasetAdd
25
- * @type api
26
- * @tag bi
27
- * @param {Object} params.id Dashboard ID
28
- * @errors 400,500
29
- * @returns {Number} status Номер помилки
30
- * @returns {String} error Опис помилки
31
- * @returns {Object} rows Масив з колонками таблиці
32
- */
33
-
34
- export default async function biDatasetAdd(req) {
35
- const { body = {}, session = {} } = req;
36
- const { uid } = session?.passport?.user || {};
37
-
38
- if (!uid) {
39
- return { message: 'access restricted', status: 403 };
40
- }
41
-
42
- if (body.table_name) {
43
- const result = await dataInsert({
44
- table: 'bi.dataset',
45
- data: { ...body, source_type: 'db' },
46
- uid,
47
- }).then(el => el.rows?.[0]);
48
-
49
- const pg = body.db ? getPG({ db: body.db }) : pgClients.client;
50
- await updatePGMeta(pg,body.table_name);
51
-
52
- return { message: { id: result?.dataset_id, action: 'assign', table: body.table_name }, status: 200 };
53
- }
54
-
55
- if (!Array.isArray(body.data) && body.data?.features?.[0]?.type !== 'Feature' && !body.file_path) {
56
- return { message: 'body data param is invalid', status: 400 };
57
- }
58
-
59
- const tableName = randomBytes(64).toString('hex').substring(0, 24).replace(/^\d+/, '');
60
- const pkey = tableName.concat('_id');
61
- const table = `bi_data.${tableName}`;
62
- const { columns, insertData } = await prepareData({ table, file_path: body.file_path, data: body?.data });
63
- const createTable = createTableFunc({ table, pkey, columns, name: body.name });
64
-
65
- const q = ['create extension if not exists postgis;create schema if not exists bi_data', createTable, insertData].join(';');
66
-
67
- const pg = body.db ? getPG({ db: body.db }) : pgClients.client;
68
- if (!pg.pk) await initPG(pg);
69
-
70
- const results = await pg.query(q);
71
-
72
- const result = await dataInsert({
73
- table: 'bi.dataset',
74
- data: { ...body, table_name: table, source_type: body.data ? 'user' : 'file' },
75
- uid,
76
- }).then(el => el.rows?.[0]);
77
-
78
- await updatePGMeta(pg,table);
79
-
80
- return {
81
- message: {
82
- id: result?.dataset_id, action: 'import', total: results?.find((el) => el.command === 'INSERT')?.rowCount, table,
83
- },
84
- status: 200,
85
- };
86
- };
@@ -1,49 +0,0 @@
1
- import { dataInsert, getPG, initPG, pgClients } from "@opengis/fastify-table/utils.js";
2
-
3
- /**
4
- * Внесення даних до набору BI
5
- *
6
- * @method POST
7
- * @summary Внесення даних до набору BI
8
- * @priority 4
9
- * @alias biDatasetDataAdd
10
- * @type api
11
- * @tag bi
12
- * @param {Object} params.id Dataset ID
13
- * @errors 400,500
14
- * @returns {Number} status Номер помилки
15
- * @returns {String} error Опис помилки
16
- * @returns {Object} rows Масив з колонками таблиці
17
- */
18
-
19
- export default async function biDatasetDataAdd(req) {
20
- const { params = {}, session = {}, body = {} } = req;
21
- const { uid } = session?.passport?.user || {};
22
-
23
- if (!uid) {
24
- return { message: 'access restricted', status: 403 };
25
- }
26
-
27
- if (!params?.id) {
28
- return { message: 'not enough params: id', status: 400 };
29
- }
30
-
31
- const { id, table, db } = await pgClients.client.query('select db, dataset_id as id, table_name as table from bi.dataset where dataset_id=$1', [params.id])
32
- .then(el => el.rows[0]);
33
-
34
- if (!id) {
35
- return { message: 'dataset not found', status: 404 };
36
- }
37
-
38
- const pg = db ? getPG({ db }) : pgClients.client;
39
- if (!pg.pk) await initPG(pg);
40
-
41
- const result = await dataInsert({
42
- pg,
43
- table,
44
- data: body,
45
- uid,
46
- }).then(el => el.rows[0]);
47
-
48
- return { id, action: 'insert', result };
49
- };
@@ -1,54 +0,0 @@
1
- import { dataDelete, getPG, initPG, pgClients } from "@opengis/fastify-table/utils.js";
2
-
3
- /**
4
- * Видалення змін до даних набору BI
5
- *
6
- * @method DELETE
7
- * @summary Внесення змін до даних набору BI
8
- * @priority 4
9
- * @alias biDatasetDataDel
10
- * @type api
11
- * @tag bi
12
- * @param {Object} params.id Dataset ID
13
- * @param {Object} params.objectId Dataset object ID
14
- * @errors 400,500
15
- * @returns {Number} status Номер помилки
16
- * @returns {String} error Опис помилки
17
- * @returns {Object} rows Масив з колонками таблиці
18
- */
19
-
20
- export default async function biDatasetDataDel(req) {
21
- const { params = {}, session = {} } = req;
22
- const { uid } = session?.passport?.user || {};
23
-
24
- if (!uid) {
25
- return { message: 'access restricted', status: 403 };
26
- }
27
-
28
- if (!params?.id) {
29
- return { message: 'not enough params: id', status: 400 };
30
- }
31
-
32
- if (!params?.objectId) {
33
- return { message: 'not enough params: objectId', status: 400 };
34
- }
35
-
36
- const { id, table, db } = await pgClients.client.query('select db, dataset_id as id, table_name as table from bi.dataset where dataset_id=$1', [params.id])
37
- .then(el => el.rows[0]);
38
-
39
- if (!id) {
40
- return { message: 'dataset not found', status: 404 };
41
- }
42
-
43
- const pg = db ? getPG({ db }) : pgClients.client;
44
- if (!pg.pk) await initPG(pg);
45
-
46
- const result = await dataDelete({
47
- pg,
48
- table,
49
- id: params.objectId,
50
- uid,
51
- });
52
-
53
- return { id, action: 'delete', result };
54
- };