@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
@@ -0,0 +1,17 @@
1
+ import path from 'node:path';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+
4
+ export default async function convertJSONToCSV({
5
+ filepathJSON, send = () => { }, delimiter = ';',
6
+ }) {
7
+ if (!filepathJSON) return null;
8
+ const rows = JSON.parse(await readFile(filepathJSON) || '{}');
9
+ const values = rows.map((row) => Object.values(row).map((el) => `${el};`));
10
+ values.unshift(Object.keys(rows[0]).map((el) => `${el};`));
11
+
12
+ send('Конвертація успішно завершена. Формування файлу...');
13
+ await mkdir(path.dirname(filepathJSON), { recursive: true });
14
+ await writeFile(filepathJSON.replace('.json', '.csv'), `\ufeff${values.join('\n').replace(/;,/g, delimiter)}`, 'utf8');
15
+ send('Файл успішно сформовано. Оновіть сторінку або натисніть кнопку ще раз для завантаження. ', 1);
16
+ return { message: 'Файл успішно сформовано. Натистіть кнопку ще раз для завантаження даних', status: 200 };
17
+ }
@@ -0,0 +1,49 @@
1
+ import path from 'path';
2
+ import {
3
+ mkdir, readFile, writeFile,
4
+ } from 'fs/promises';
5
+
6
+ import { logger } from '@opengis/fastify-table/utils.js';
7
+
8
+ import { grpc } from '@opengis/fastify-file/utils.js';
9
+
10
+ const { jsonToXls } = grpc();
11
+
12
+ export default async function convertJSONToXls({
13
+ filepathJSON, colmodel, domain, source, send = () => { },
14
+ }) {
15
+ if (!filepathJSON) return null;
16
+
17
+ const rows = JSON.parse(await readFile(filepathJSON) || '{}');
18
+
19
+ if (!rows?.length) {
20
+ return { message: 'empty rows', status: 400 };
21
+ }
22
+
23
+ send('Конвертація файлу...');
24
+ try {
25
+ const { result } = await jsonToXls({
26
+ json: JSON.stringify(rows),
27
+ header: source,
28
+ subheader: `Завантажено з порталу ${domain} - ${new Date().toLocaleDateString()}. Всього: ${rows.length}`,
29
+ colmodel: JSON.stringify(colmodel),
30
+ });
31
+ send('Конвертація успішно завершена. Формування файлу...');
32
+ await mkdir(path.dirname(filepathJSON), { recursive: true });
33
+ await writeFile(filepathJSON.replace('.json', '.xlsx'), result, 'base64');
34
+ send('Файл успішно сформовано. Оновіть сторінку або натисніть кнопку ще раз для завантаження. ', 1);
35
+ return { message: 'Файл успішно сформовано. Натистіть кнопку ще раз для завантаження даних', status: 200 };
36
+ }
37
+ catch (err) {
38
+ send(`jsonToXLS err: ${err.toString()}`, 1);
39
+ logger.error({
40
+ name: 'export/table',
41
+ filepathJSON,
42
+ colmodel,
43
+ domain,
44
+ rows: rows.length,
45
+ error: err.toString(),
46
+ });
47
+ return { error: err.toString(), status: 500 };
48
+ }
49
+ }
@@ -0,0 +1,59 @@
1
+ import { randomBytes, createHash } from 'node:crypto';
2
+
3
+ const schema = 'data_user';
4
+
5
+ const columnType = {
6
+ text: 'text',
7
+ select: 'text',
8
+ date: 'date',
9
+ 'yes/no': 'boolean',
10
+ badge: 'text',
11
+ number: 'numeric',
12
+ tags: 'text[]',
13
+ geom: 'geom',
14
+ };
15
+
16
+ export default function createTable(columns, name) {
17
+ const tableName = randomBytes(64).toString('hex').substring(0, 24).replace(/^\d+/, '');
18
+ const table = `${schema}.${tableName}`;
19
+ const pkey = tableName.concat('_id');
20
+
21
+ columns?.forEach?.((el, i) => Object.assign(el, {
22
+ name: `col_${i}`, // el.name
23
+ type: columnType[el.format || 'text'] || 'text',
24
+ }));
25
+
26
+ const clsQuery = columns
27
+ ?.filter?.(col => col?.format === 'select' && col.data?.length)
28
+ ?.map(col => {
29
+ const data = JSON.stringify(col.data).replace(/'/g, "''");
30
+ Object.assign(col, { data: createHash('md5').update([tableName, col.name].join()).digest('hex') });
31
+ return `insert into admin.cls(clsid,name,type,module) values('${col.data}','${col.data}','json', '${table}');
32
+ insert into admin.cls(code,name,parent,icon,data,module)
33
+ select value->>'id',value->>'text','${col.data}',value->>'icon',value->>'data', '${table}'
34
+ from json_array_elements('${data}'::json)`;
35
+ })
36
+ ?.join(';');
37
+
38
+ const createQuery = `create table if not exists ${table} (
39
+ ${pkey} text not null default public.uuid_generate_v4(),
40
+ geom public.geometry, ${columns ? `
41
+ ${columns?.map?.((el) => `${el.name} ${el.type}`).join(', ')},` : ''}
42
+ cdate timestamp without time zone not null default now(),
43
+ editor_date timestamp without time zone,
44
+ uid text,
45
+ editor_id text,
46
+ files json,
47
+ constraint ${table.replace(/\./g, '_')}_constraint_pkey PRIMARY KEY (${pkey}) )`;
48
+
49
+ const commentQuery = columns
50
+ ?.filter?.((el) => el.title)
51
+ ?.map((el) => `comment on column ${table}.${el.name} is '${el.title}'`)
52
+ ?.join(';');
53
+
54
+ return {
55
+ table,
56
+ pkey,
57
+ sql: [createQuery, commentQuery, (name ? `comment on table ${table} is '${name}'` : undefined), clsQuery].filter((el) => el).join(';'),
58
+ };
59
+ }
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1,46 @@
1
+ import { handlebarsSync, handlebars } from '@opengis/fastify-table/utils.js'
2
+
3
+ function format(d, key, data) {
4
+ if (!key?.includes) return '';
5
+ if (d === true) return 'так';
6
+ if (d === false) return 'ні'
7
+
8
+ if (key.includes('{{')) {
9
+ return handlebarsSync.compile(key)(data);
10
+ }
11
+ if (!d) return '-';
12
+ return d;
13
+ }
14
+ export default async function descriptionList(data, opt) {
15
+ const { hash } = opt;
16
+
17
+ // no data
18
+ if (hash.nodata && !data) {
19
+ const noDataText = typeof hash.nodata == 'string' ? hash.nodata : 'no data';
20
+ return noDataText
21
+ }
22
+ if (!data) return 'empty data'
23
+ if (!hash.columns.split) return 'columns empty' + JSON.stringify(hash.columns)
24
+ const keys = hash.columns.split(hash.divider || '|').map(el => hash.comma ? el.trim().replace(new RegExp(hash.comma || '#', 'g'), ',') : el.trim());
25
+
26
+ const result = [];
27
+
28
+ for (let i = 0; i < keys.length; i += 2) {
29
+ const name = keys[i];
30
+ const nameHBS = name.includes('{{') ? handlebarsSync.compile(name)({ ...data, hash }) : false
31
+
32
+ if (!nameHBS && name.includes('{{')) continue;
33
+
34
+ const key = keys[i + 1];
35
+ const d1 = ['{{format', '{{select', '{{badge', '{{ifCond'].filter(el => key?.includes?.(el)).length ? await handlebars.compile(key)(data) || '-' : null
36
+ if (d1 || data[key]) {
37
+ result.push(`<div class="flex py-[16px] text-[14px] border-b border-[#E5E7EB">
38
+ <div class="text-[#6B7280] dark:text-white font-[600] w-[50%]">${nameHBS || name}</div>
39
+ <div class="font-[400] text-black dark:text-[#bdbdbd] w-[50%]">${d1 || format(data[key], key, data)}</div>
40
+ </div>
41
+ `);
42
+ }
43
+
44
+ }
45
+ return '<div class="w-full">' + result.join('') + '</div>';
46
+ }
@@ -0,0 +1,58 @@
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { rm, mkdir, writeFile } from 'node:fs/promises';
4
+ import { createHash } from 'node:crypto';
5
+
6
+ import { config, logger } from '@opengis/fastify-table/utils.js';
7
+
8
+ export default async function downloadRemoteFile({
9
+ rootDir, url, table,
10
+ }) {
11
+ if (!rootDir) {
12
+ return { error: 'param rootDir is required', status: 400 };
13
+ }
14
+ if (!url) {
15
+ return { error: 'param url is required', status: 400 };
16
+ }
17
+ if (!table) {
18
+ return { error: 'param table is required', status: 400 };
19
+ }
20
+
21
+ try {
22
+ const response = await fetch(url);
23
+
24
+ if (response?.status !== 200) {
25
+ return { message: 'file not found', status: response?.status };
26
+ }
27
+
28
+ const arrayBuffer = await response.arrayBuffer();
29
+ const buffer = Buffer.from(arrayBuffer);
30
+
31
+ const contentType = response.headers.get('Content-Type');
32
+
33
+ // eslint-disable-next-line newline-per-chained-call
34
+ const extName = contentType === 'text/plain' ? path.extname(url) : `.${response.headers.get('Content-Type')?.split(';')?.shift()?.split('/')?.pop()?.replace(/\+|\-|\./g, '')}`; // path.extname(url)
35
+
36
+ const filePath = `/files/tmp/${createHash('md5').update([url, table].join()).digest('hex')}${extName}`;
37
+ const fullPath = path.join(rootDir, filePath);
38
+
39
+ if (config?.local) {
40
+ console.log(url, fullPath, existsSync(fullPath));
41
+ }
42
+
43
+ await mkdir(path.dirname(fullPath), { recursive: true });
44
+
45
+ if (existsSync(fullPath) && config?.local) {
46
+ await rm(fullPath);
47
+ }
48
+
49
+ if (!existsSync(fullPath)) {
50
+ await writeFile(fullPath, buffer);
51
+ }
52
+ return { filePath };
53
+ }
54
+ catch (err) {
55
+ logger.file('dataset/create/error', { url, error: err.toString() });
56
+ return { error: err.toString(), status: 500 };
57
+ }
58
+ }
@@ -0,0 +1,46 @@
1
+ import { pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ import insertDataQuery from './insertDataQuery.js';
4
+
5
+ const insertDataset = `insert into bi.dataset
6
+ (name, table_name, dataset_file_path, column_list, pk, data_source, uid)
7
+ values($1,$2,$3,$4,$5,$6,$7) returning dataset_id`;
8
+
9
+ const updateAppeal = 'update datasets_appeal.appeal set dataset_id=$1, data_key=$2 where ap_id=$3';
10
+
11
+ export default async function executeQuery({
12
+ sql, data, name, id, table, relPath, columns, pkey, source, user, url, dataKey,
13
+ }) {
14
+ const client = await pgClients.client.connect();
15
+ try {
16
+ await client.query('BEGIN');
17
+
18
+ // create table
19
+ await client.query(sql);
20
+
21
+ // insert data
22
+ if (data) {
23
+ const insertData = insertDataQuery(columns, data, table);
24
+ await client.query(insertData);
25
+ }
26
+
27
+ // create dataset
28
+ const args = [name || id, table, relPath, columns ? JSON.stringify(columns) : null, pkey, JSON.stringify({ type: source, appeal: id, url }), user?.uid];
29
+ const datasetId = await client.query(insertDataset, args).then(el => el.rows?.[0]?.dataset_id);
30
+
31
+ if (id) {
32
+ // update appeal for public only, admin w/o appeal
33
+ await client.query(updateAppeal, [datasetId, dataKey, id]);
34
+ }
35
+
36
+ await client.query('COMMIT');
37
+ return { datasetId };
38
+ }
39
+ catch (err) {
40
+ await client.query('ROLLBACK');
41
+ return { error: err.toString(), status: 500 };
42
+ }
43
+ finally {
44
+ client.release();
45
+ }
46
+ }
@@ -0,0 +1,107 @@
1
+ import descriptionList from "./descriptionList.js";
2
+ import metaFormat from "./metaFormat.js";
3
+
4
+ async function getDataByLatLng({ pg, layer, table, id, lat, lng, time = [] }) {
5
+ // const rclient = getRedis();
6
+ // const redisKey = `${pg.options.database}:gis-format:${layer}:${lat}:${lng}`;
7
+ // const cacheData = await rclient.get(redisKey);
8
+ // if (cacheData && !config.local) return JSON.parse(cacheData);
9
+
10
+ const { rows: styles = [] } = await pg.query(`select style_id as id, dataset
11
+ from gis.style where style_id=any($1::text[])
12
+ union all
13
+ select dataset_id as id, json_build_array(json_build_object('pk', pk, 'table', table_name, 'query', query, 'card', setting->>'card', 'columns', column_list)) as dataset
14
+ from bi.dataset a where dataset_id=any($1::text[])`, [layer.split(',')]);
15
+ time.push(Date.now());
16
+
17
+ if (!styles?.length) {
18
+ return { message: `style not found: ${layer}`, status: 400 };
19
+ }
20
+
21
+ const { srids } = await pg.queryCache?.('select json_agg(srid) as srids from spatial_ref_sys').then((res1) => res1.rows?.[0] || {}) || {};
22
+
23
+ const layers = styles.reduce((acc, curr) => {
24
+ curr.dataset?.filter((el) => el.table && (el.pk || pg.pk?.[el.table]))
25
+ ?.forEach((el) => acc.push({
26
+ layer: curr.id,
27
+ pk: el.pk || pg.pk?.[el.table],
28
+ key: el.key,
29
+ table: el.table,
30
+ query: el.query,
31
+ template: el.card,
32
+ column_list: el.column_list,
33
+ columns: el.columns || el.column_list?.map((el) => el.name)?.join(','),
34
+ srid: el.srid || '4326',
35
+ geom: el.geom || 'geom',
36
+ step: srids?.includes(el.srid - 0) && el.srid !== '4326' ? 10 : 0.0002,
37
+ }));
38
+ return acc;
39
+ }, [])
40
+ .filter((el) => el.pk && el.layer)
41
+ .filter((el) => table ? el.table === table : true);
42
+
43
+ if (!layers.length) {
44
+ return { message: 'data not found', status: 404 };
45
+ }
46
+
47
+ const point = `srid=4326;point(${lng} ${lat})`;
48
+
49
+ // ${el.columns?.split?.filter?.((el) => el) ? `,${el.columns?.split?.filter?.((el) => el) || ''}` : ''}
50
+
51
+ const q = layers.map((el) => `select ${el.pk}, '${el.layer}' as layer,
52
+ row_to_json(t) as data,
53
+ st_asgeojson(${el.geom})::json as geom from ${el.table} t
54
+ where ${el.query || '1=1'} and ${id ? `${el.pk}::text='${id.replace(/'/g, "''")}'` : '1=1'}
55
+ and case
56
+ when ST_GeometryType(geom) in ('ST_Polygon','ST_MultiPolygon')
57
+ then st_intersects(geom,st_buffer('${point}',${el.step}))
58
+
59
+ when ST_GeometryType(geom) in ('ST_Line','ST_MultiLineString')
60
+ then st_distance(geom,'${point}') < ${el.step}
61
+ else false end`).join(' union all ');
62
+
63
+ // if (config.local && config.debug) console.log(q);
64
+ const { rows: objects = [] } = await pg.query(q) || {};
65
+ time.push(Date.now());
66
+
67
+ const rows = objects.map((row) => {
68
+ const layerData = layers.find((el) => el.layer === row?.layer) || {};
69
+ const rowData = layerData?.columns?.split?.filter?.((el) => el) ? layerData?.columns?.split?.filter?.((el) => el).reduce((acc, curr) => Object.assign(acc, { [curr]: row.data?.[curr] }), {}) : row.data;
70
+ return {
71
+ ...rowData,
72
+ ...row,
73
+ ...layerData,
74
+ data: undefined,
75
+ id: layerData.pk ? row?.[layerData.pk] : undefined,
76
+ geom: row.geom,
77
+ };
78
+ });
79
+
80
+ const { pk, layer: layerId, card, template } = rows[0] || {};
81
+
82
+ const dataset = layers.find((el) => el.layer === rows?.[0]?.layer) || {};
83
+
84
+ const columnList = (dataset.column_list || dataset.columns)?.reduce?.((acc, curr) => Object.assign(acc, { [curr.name]: curr.title || curr.ua || curr.name }), {}) || {};
85
+
86
+ const columns = card
87
+ ? Object.keys(card).map(el => `${el}| ${card[el]}`).join('|')
88
+ : Object.keys(columnList)?.map((key) => `${columnList[key]}| ${key}`).join('|');
89
+
90
+ const html = columns && rows?.[0] ? await descriptionList(rows[0], { hash: { columns } }) : undefined;
91
+ time.push(Date.now());
92
+
93
+ await Promise.all(rows.map(async () => {
94
+ const layerData = layers.find((el) => el.layer === rows?.[0]?.layer) || {};
95
+ const columns = layerData.column_list || layerData.columns;
96
+ if (columns?.length && Array.isArray(columns)) {
97
+ await metaFormat({ rows, columns });
98
+ }
99
+ }));
100
+ time.push(Date.now());
101
+
102
+ const resp = { id: rows?.[0]?.[pk], layers: layerId, template, data: rows?.[0], rows, html };
103
+
104
+ // await rclient.set(redisKey, JSON.stringify(resp), 'EX', 5 * 60);
105
+ return resp;
106
+ }
107
+ export default getDataByLatLng
@@ -0,0 +1,47 @@
1
+ import { handlebars } from "@opengis/fastify-table/utils.js";
2
+
3
+ import descriptionList from "./descriptionList.js";
4
+ // import metaFormat from "./metaFormat.js";
5
+
6
+ async function getDataById({ pg, table, id, time = [] }) {
7
+ const dataset = await pg.query(`select dataset_id as id, pk, query, table_name, column_list, setting
8
+ from bi.dataset where dataset_id=$1`, [table]).then((res) => res.rows[0] || {});
9
+ time.push(Date.now());
10
+
11
+ if (!dataset?.id) {
12
+ return { message: `dataset not found: ${table}`, status: 400 };
13
+ }
14
+
15
+ if (!dataset?.table_name) {
16
+ return { message: 'invalid dataset: empty params table', status: 400 };
17
+ }
18
+
19
+ const pk = dataset.pk || pg.pk?.[dataset.table_name];
20
+
21
+ const { rows = [] } = pk ? await pg.query(`select *, ${pk}, geom::json from ${dataset.table_name} where ${dataset.query || '1=1'} and ${pk}=$1`, [id]) || {} : {};
22
+ if (pk) rows.forEach((row) => Object.assign(row, { id: row?.[pk] }));
23
+ time.push(Date.now());
24
+
25
+ // sql columns
26
+ const sql = `select attname as name, pg_catalog.col_description(attrelid,attnum) as title, atttypid::regtype as type from pg_catalog.pg_attribute a
27
+ where attrelid='${dataset.table_name}'::regclass and attnum>0
28
+ and attname not in ('editor_id','editor_date','cdate','geom','id','uid','cdate')
29
+ and atttypid::regtype not in ('json','geometry')`;
30
+
31
+ // card or auto
32
+ const type = dataset.setting?.card_type || 'auto';
33
+ const list = dataset.setting?.card_list || [];
34
+ const { rows: all } = await pg.query(sql).then(el => el);
35
+ const columns = all.filter(el => type === 'list' ? list.includes(el.name) : true)
36
+ .map(el => `${el.title || el.name}| ${el.name}`).join('|')
37
+
38
+
39
+ const { body } = type === 'html' && dataset?.setting?.card ? await pg.query(`select body->>'body' as body from admin.doc_template where title=$1`, [dataset?.setting?.card]).then(el => el.rows[0] || {}) : {}
40
+
41
+ const html = body ? await handlebars.compile(body)(rows[0]) : await descriptionList(rows[0], { hash: { columns } });
42
+ time.push(Date.now());
43
+
44
+
45
+ return { id: rows?.[0]?.id, template: dataset?.setting?.card, rows, html, body };
46
+ }
47
+ export default getDataById
@@ -0,0 +1,12 @@
1
+ export default function insertDataQuery(columns, data, table) {
2
+ const columnList = columns?.map((el) => el.name)?.join(',');
3
+ const features = data?.features || [];
4
+ if (!features?.length) return null;
5
+
6
+ const insertData = `INSERT into ${table} (geom ${columns?.length ? ','.concat(columnList) : ''}) SELECT public.ST_GeomFromGeoJSON((features->>'geometry')::json) AS geom
7
+ ${columns?.length ? ','.concat(columns?.map((el) => `features->'properties'->>'${el.title}'`)?.join(',')) : ''} FROM
8
+ (SELECT json_array_elements('${JSON.stringify(features).replace(/'/g, "''")}'::json) AS features)q`;
9
+
10
+ // console.log(insertData);
11
+ return insertData;
12
+ }
@@ -0,0 +1,24 @@
1
+ import { getSelectVal } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function metaFormat({ rows, columns }) {
4
+ const selectCols = columns?.filter?.((el) => el.data);
5
+ if (!selectCols?.length) return rows;
6
+
7
+ await Promise.all(selectCols.map(async (attr) => {
8
+ const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => (typeof el === 'boolean' ? true : el));
9
+ if (!values.length) return null;
10
+
11
+ const cls = await getSelectVal({ name: attr.data, values });
12
+ if (!cls) return null;
13
+
14
+ rows.forEach(el => {
15
+ const val = el[attr.name]?.map?.(c => cls[c.toString()] || cls[c] || c) || cls[el[attr.name]?.toString()] || cls[el[attr.name]] || el[attr.name];
16
+ if (!val) return;
17
+ Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
18
+ });
19
+
20
+ return null;
21
+ }));
22
+
23
+ return rows;
24
+ }
@@ -16,9 +16,9 @@ export default async function widgetAdd({ pg, body }) {
16
16
 
17
17
  return {
18
18
  time: Date.now() - time,
19
- message: `Added new dashboard, ID: '${res.rows[0].title}'`,
20
- status: 200,
21
- rows: res.rows,
19
+ id: res.rows?.[0].dashboard_id,
20
+ title: res.rows?.[0].title,
21
+ name: res.rows?.[0].name,
22
22
  };
23
23
  } catch (err) {
24
24
  return { error: err.toString(), status: 500 };
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { dataInsert, dataUpdate } from "@opengis/fastify-table/utils.js";
2
3
 
3
4
  /* eslint-disable import/extensions */
@@ -48,24 +49,28 @@ export default async function widgetAdd({ pg, params = {}, body = {} }) {
48
49
  data?.metrics,
49
50
  });
50
51
 
52
+ data.widget_id = createHash('md5').update([data?.name, dashboardId].join()).digest('hex').substr(0, 10);
53
+
51
54
  const res = await dataUpdate({
52
55
  table: 'bi.dashboard',
53
56
  id: dashboardId,
54
57
  data: {
55
58
  widgets: [data].concat(row.widgets || []),
56
- panels: [{ widget: data.name, col: data.col || 3 }].concat(
59
+ panels: [{ widget: data.name, col: data.col || 3, height: data.data?.height }].concat(
57
60
  row.panels || []
58
61
  ),
59
62
  },
60
63
  });
61
64
  const widgetData = { ...data, data, dashboard_id: dashboardId };
62
65
  if (body?.yml) Object.assign(widgetData, { yml: body.yml });
63
- await dataInsert({
66
+ const test = await dataInsert({
64
67
  table: 'bi.widget',
65
68
  data: widgetData,
66
69
  });
67
70
  return {
68
- message: `Added widget to ${dashboardName}`,
71
+ dashboard: dashboardName,
72
+ widgetId: data.widget_id,
73
+ widgetName: data.name,
69
74
  status: 200,
70
75
  rows: res,
71
76
  };
@@ -5,7 +5,8 @@ import { getWidget, yamlSafe } from '../../../../utils.js';
5
5
  export default async function widgetEdit({ pg, body, params, }) {
6
6
  const { widget: widgetName, name: dashboardName } = params;
7
7
  const data = body.yml && !body.style ? yamlSafe.load(body.yml) : body;
8
-
8
+ const yml = body.yml ?? yamlSafe.dump(body.data);
9
+ console.log(body.data, data, body.yml, yml);
9
10
 
10
11
 
11
12
  if (!widgetName || !dashboardName) {
@@ -15,8 +16,8 @@ export default async function widgetEdit({ pg, body, params, }) {
15
16
  };
16
17
  }
17
18
 
18
- const { widget_id: widgetId } = await pg.query(
19
- `select a.widget_id , b.dashboard_id from bi.widget a, bi.dashboard b
19
+ const { widget_id: widgetId, dashboard_id: dashboardId, widgets } = await pg.query(
20
+ `select a.widget_id , b.dashboard_id, b.widgets from bi.widget a, bi.dashboard b
20
21
  where $2 in (a.widget_id, a.name) and $1 in (b.dashboard_id, b.name) order by 1,2`,
21
22
  [dashboardName, widgetName]).then(res1 => res1.rows?.[0] || {});
22
23
 
@@ -35,8 +36,8 @@ export default async function widgetEdit({ pg, body, params, }) {
35
36
  return { message: 'bad params: table ' + tableName, status: 400 };
36
37
  }
37
38
 
38
- if (body.yml) {
39
- Object.assign(widgetData, { yml: body.yml })
39
+ if (yml) {
40
+ Object.assign(widgetData, { yml })
40
41
  }
41
42
 
42
43
  Object.assign(widgetData, { table_name: tableName });
@@ -47,6 +48,23 @@ export default async function widgetEdit({ pg, body, params, }) {
47
48
  data: widgetData,
48
49
  });
49
50
 
51
+ const idx = widgets?.findIndex?.(el => el.name === widgetName) || -1;
52
+ if (widgetData.hasOwnProperty('data') && idx > -1) {
53
+ widgets[idx].data = widgetData.data
54
+ }
55
+ if (widgetData.hasOwnProperty('yml') && idx > -1) {
56
+ widgets[idx].yml = widgetData.yml;
57
+ }
58
+ if (widgetData.hasOwnProperty('style') && idx > -1) {
59
+ widgets[idx].style = widgetData.style;
60
+ }
61
+
62
+ await dataUpdate({
63
+ table: 'bi.dashboard',
64
+ id: dashboardId,
65
+ data: { widgets },
66
+ });
67
+
50
68
  return {
51
69
  message: rows,
52
70
  status: 200,