@opengis/bi
Version:
BI data visualization module
158 lines (131 loc) • 5.71 kB
JavaScript
import yaml from 'js-yaml';
import {
pgClients,
getTemplatePath,
getTemplate,
getPGAsync,
getMeta,
} from '@opengis/fastify-table/utils.js';
export default async function dashboard({
pg: pg1 = pgClients.client, params = {},
}) {
const time = Date.now();
const { id } = params;
if (!id) {
return { message: 'not enough params: dashboard required', status: 400 };
}
const dashboards = getTemplatePath('dashboard');
const fileDashboard = dashboards.find((el) => el[0] === id);
if (!fileDashboard) {
const sql = `select title, description, table_name, panels, grid, widgets, filters, style, words, db, public
from bi.dashboard where $1 in (dashboard_id, name)`;
const data = await pg1.query(sql, [id]).then(el => el.rows?.[0] || {});
let pg = pg1;
try {
pg = data.pg || (data.db ? await getPGAsync(data.db) : null) || pg1;
} catch (err) {
data.error = err.toString();
}
data.type = 'bd';
const { table_name: table } = data;
if (!table) {
return { message: 'not enough params: table required', status: 400 };
}
const loadTemplate = pg.pk?.['admin.doc_template'] ? await pg.query(
'select body from admin.doc_template where doc_type=5 and title=$1',
[table]
).then(el => el.rows?.[0]?.body) : null;
const sqlList = loadTemplate?.sql ? loadTemplate?.sql
?.filter?.(el => !el.disabled && el?.sql?.replace)
?.map?.((el, i) => `left join lateral (${el.sql.replace(/limit 1/ig, '')}) t${i} on 1=1`)?.join?.(' ') : '';
const { fields = [] } = table && pg.pk?.[loadTemplate?.table || table] ? await pg.query(`select * from ${loadTemplate?.table || table} t ${sqlList || ''} limit 0`) : {};
data?.widgets?.forEach?.(el => {
const { style, data = {}, type, title, x, metrics } = el;
el.yml = yaml.dump({ title, type, data: { x, metrics, ...data }, style, })
// el.yml = yaml.dump({ style, data, type, title });
});
data.panels?.forEach?.(el => {
const { title, type } = data?.widgets?.find(item => item.name === el.widget) || {};
Object.assign(el, { title, type });
});
const meta = table ? await getMeta({ pg, table: loadTemplate?.table || table }) : {};
const columnIndexes = meta?.columns?.reduce((acc, curr, idx) => Object.assign(acc, { [curr.name]: idx }), {}) || [];
const columns = fields?.map(el => ({ name: el.name, title: meta?.columns[columnIndexes[el.name]]?.title || el.name, type: pg.pgType?.[el.dataTypeID] }));
return {
...data || {},
widgets: data?.widgets?.filter?.(el => el?.widget_id) || [],
panels: data?.panels?.filter?.(el => el?.widget) || [],
geom: !meta?.geom,
error:
table && !pg.pk?.[loadTemplate?.table || table] ? `table pkey not found: ${loadTemplate?.table || table}` : undefined,
table_name: table,
templateTable: loadTemplate?.table,
time: Date.now() - time,
columns,
};
}
const fileData = await getTemplate('dashboard', id);
const index = fileData.find((el) => el[0] === 'index.yml')[1];
if (!index) {
return { message: `not found ${id}`, status: 404 };
}
const data = index;
data.type = 'file';
const { table } = data?.data || { table: data?.table_name };
let pg = pg1;
try {
pg = data.pg || (data.db ? await getPGAsync(data.db) : null) || pg1;
} catch (err) {
data.error = err.toString();
}
const { fields = [] } = table ? await pg.query(`select * from ${table} limit 0`) : {};
const meta = await getMeta({ pg: pg1, table });
const columns = meta?.columns
?.filter(el => fields.map(field => field.name).includes(el.name))
?.map(el => ({ name: el.name, title: el.title, type: pg.pgType?.[el.dataTypeID] }));
const checks = index?.filters?.filter((el) => el?.id && fields.map((el) => el?.name).includes(el?.id) && el?.type === 'Check');
if (checks?.length) {
await Promise.all(checks.map(async (el) => {
if (el?.data) {
const options = await getTemplate('cls', el.data);
Object.assign(el, { options });
} else if (index?.table_name || index?.table) {
const { rows = [] } = await pg.query(`select "${el.id}" as id, count(*) from ${index?.table_name || index?.table} group by "${el.id}"`);
Object.assign(el, { options: rows });
}
}));
}
const ranges = index?.filters?.filter((el) => el?.id && fields.map((el) => el?.name).includes(el?.id) && el?.type === 'Range');
if (ranges?.length) {
await Promise.all(ranges.map(async (el) => {
const rows = await pg.query(`select array[
percentile_cont(0) within group (order by ${el.id}), percentile_cont(0.25) within group (order by ${el.id}),
percentile_cont(0.5) within group (order by ${el.id}), percentile_cont(0.75) within group (order by ${el.id}),
percentile_cont(1.0) within group (order by ${el.id})
] from ${index?.table_name || index?.table}`).then(el => el.rows?.[0]?.array || []);
Object.assign(el, { options: rows });
}));
}
// console.log(fileData)
const widgets = fileData
.filter((el) => el[0] !== 'index.yml')
.map((el) =>
el[1].data
? {
name: el[0].split('.')[0],
type: el[1].type,
title: el[1].title,
style: el[1].style,
data: el[1].data,
}
: { name: el[0].split('.')[0], title: el[1] }
);
return {
...data,
geom: !!meta?.geom,
table_name: table,
time: Date.now() - time,
columns,
widgets,
};
}