@opengis/bi 1.0.11 → 1.0.13

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 (76) hide show
  1. package/dist/assets/charts/bar.png +0 -0
  2. package/dist/assets/charts/funnel.png +0 -0
  3. package/dist/assets/charts/no_data.jpg +0 -0
  4. package/dist/assets/charts/number.png +0 -0
  5. package/dist/assets/charts/pie.png +0 -0
  6. package/dist/assets/charts/progress.png +0 -0
  7. package/dist/assets/charts/stat.png +0 -0
  8. package/dist/assets/images/bar.png +0 -0
  9. package/dist/assets/images/funnel.png +0 -0
  10. package/dist/assets/images/no_data.jpg +0 -0
  11. package/dist/assets/images/number.png +0 -0
  12. package/dist/assets/images/pie.png +0 -0
  13. package/dist/assets/images/progress.png +0 -0
  14. package/dist/assets/images/stat.png +0 -0
  15. package/dist/bi.js +1 -1
  16. package/dist/bi.umd.cjs +722 -113
  17. package/dist/{import-file-D6RYWvi_.js → import-file-1T7kpSzt.js} +43137 -43183
  18. package/dist/map-component-mixin-BLM9iEWA.js +18712 -0
  19. package/dist/style.css +1 -1
  20. package/dist/vs-calendar-WiK1hcHS.js +96 -0
  21. package/dist/vs-funnel-bar-CpPbYZ0_.js +92 -0
  22. package/dist/vs-heatmap-BG4eIROH.js +83 -0
  23. package/dist/vs-map-BRk6Fmks.js +66 -0
  24. package/dist/vs-map-cluster-Dfe9INqE.js +103 -0
  25. package/dist/vs-number-CJq-vi95.js +39 -0
  26. package/dist/{vs-text-UyIWGqQO.js → vs-text-DcrAdQ40.js} +60 -60
  27. package/package.json +11 -6
  28. package/server/migrations/bi.sql +27 -0
  29. package/server/plugins/hook.js +86 -0
  30. package/server/plugins/vite.js +7 -4
  31. package/server/routes/dashboard/controllers/dashboard.js +24 -4
  32. package/server/routes/data/controllers/data.js +48 -52
  33. package/server/routes/data/controllers/util/chartSQL.js +8 -7
  34. package/server/routes/data/controllers/util/normalizeData.js +22 -9
  35. package/server/routes/edit/controllers/dashboard.add.js +5 -1
  36. package/server/routes/edit/controllers/dashboard.edit.js +5 -1
  37. package/server/routes/edit/controllers/widget.add.js +23 -7
  38. package/server/routes/edit/controllers/widget.del.js +1 -1
  39. package/server/routes/edit/controllers/widget.edit.js +8 -5
  40. package/server/routes/map/controllers/cluster.js +75 -0
  41. package/server/routes/map/controllers/clusterVtile.js +143 -0
  42. package/server/routes/map/controllers/geojson.js +9 -23
  43. package/server/routes/map/controllers/map.js +57 -0
  44. package/server/routes/map/controllers/vtile.js +25 -32
  45. package/server/routes/map/index.mjs +7 -4
  46. package/server/utils/getWidget.js +56 -0
  47. package/utils.js +11 -0
  48. package/dist/vs-number-DKF5ptAP.js +0 -34
  49. package/server/templates/dashboard/erobota/bar_area.yml +0 -17
  50. package/server/templates/dashboard/erobota/bar_culture.yml +0 -18
  51. package/server/templates/dashboard/erobota/bar_grand.yml +0 -18
  52. package/server/templates/dashboard/erobota/count_grand.yml +0 -8
  53. package/server/templates/dashboard/erobota/index.yml +0 -47
  54. package/server/templates/dashboard/erobota/list_culture.yml +0 -12
  55. package/server/templates/dashboard/erobota/list_grant.yml +0 -12
  56. package/server/templates/dashboard/erobota/map.yml +0 -4
  57. package/server/templates/dashboard/erobota/pie_area.yml +0 -17
  58. package/server/templates/dashboard/erobota/pie_grant.yml +0 -17
  59. package/server/templates/dashboard/erobota/total_area.yml +0 -9
  60. package/server/templates/dashboard/erobota/total_grand.yml +0 -9
  61. package/server/templates/dashboard/sales/index.yml +0 -40
  62. package/server/templates/dashboard/sales/quarterly_revenue.yml +0 -19
  63. package/server/templates/dashboard/sales/quarterly_revenue_by_product_line.yml +0 -19
  64. package/server/templates/dashboard/sales/total_products_sold.yml +0 -9
  65. package/server/templates/dashboard/sales/total_products_sold_by_product_line.yml +0 -12
  66. package/server/templates/dashboard/sales/total_revenue.yml +0 -8
  67. package/server/templates/dashboard/sales/total_revenue_by_product_line.yml +0 -17
  68. package/server/templates/dashboard/sales/vehicle_sales_info.md +0 -17
  69. package/server/templates/dashboard/test3/index.yml +0 -29
  70. package/server/templates/dashboard/test3/quarterly_revenue.yml +0 -19
  71. package/server/templates/dashboard/test3/widget1.yml +0 -8
  72. package/server/templates/pt/vehicle_sales.md +0 -17
  73. package/server/templates/table/demo.cleaned_sales_data.table.json +0 -104
  74. package/server/templates/table/test.dataset.table.json +0 -16
  75. package/server/templates/widget/product_line.yml +0 -20
  76. package/server/templates/widget/test_vtile.yml +0 -7
@@ -0,0 +1,57 @@
1
+
2
+
3
+ import { getWidget } from '../../../../utils.js';
4
+
5
+
6
+ export default async function map({
7
+ pg, funcs = {}, query = {},
8
+ }) {
9
+
10
+ const { dashboard, widget } = query;
11
+
12
+ const { data, type, layers } = await getWidget({ dashboard, widget });
13
+
14
+
15
+ if (!['map'].includes(type)) {
16
+ return { message: 'access restricted: invalid widget type', status: 403 };
17
+ }
18
+ if (!data?.table) {
19
+ return { message: 'invalid widget: param table is required', status: 400 };
20
+ }
21
+ if (!pg.pk[data?.table]) {
22
+ return { message: 'invalid widget: table pkey not found', status: 400 };
23
+ }
24
+
25
+ const { q = '' } = await funcs.getFilterSQL({
26
+ pg, table: data?.table, filter: query.filter,
27
+ });
28
+
29
+ const res = {};
30
+ if (data?.color) {
31
+ const { rows = [] } = await pg.query(`select count(*), "${data.color}" as val from ${data.table} where ${data.query || '1=1'} group by "${data.color}"`);
32
+ Object.assign(res, { colors: rows }); // кольори для легенди
33
+ }
34
+ if (data?.metrics?.length) {
35
+ const metric = data?.metrics[0];
36
+ const q1 = `select PERCENTILE_CONT(0) WITHIN GROUP (ORDER BY "${metric}") as "0",
37
+ PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY "${metric}") as "25",
38
+ PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY "${metric}") as "50",
39
+ PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY "${metric}") as "75",
40
+ PERCENTILE_CONT(1) WITHIN GROUP (ORDER BY "${metric}") as "100" from ${data.table} where ${data.query || '1=1'} and ${q || '1=1'}`;
41
+ const sizes = await pg.query(q1).then((res1) => Object.values(res1.rows?.[0] || {}));
42
+ Object.assign(res, { sizes }); // розміри для легенди
43
+ }
44
+ const { bbox } = await pg.query(`select st_asgeojson(box2d(geom))::json as bbox from ${data.table} where ${data.query || '1=1'}`).then((res1) => res1.rows?.[0] || {});
45
+ Object.assign(res, {
46
+
47
+ layers,
48
+ columns: data.columns,
49
+ bbox, // Map bounds
50
+
51
+ top: [], // 10 найкращих
52
+ bottom: [], // 10 найгірших
53
+ });
54
+ return res;
55
+
56
+
57
+ }
@@ -4,6 +4,7 @@ import path from 'path';
4
4
  import { createHash } from 'crypto';
5
5
  import { writeFile, mkdir } from 'fs/promises';
6
6
  import { existsSync, /*readdirSync, */ readFileSync } from 'fs';
7
+ import { getWidget } from '../../../../utils.js';
7
8
 
8
9
  import yaml from '../../dashboard/controllers/utils/yaml.js';
9
10
  import normalizeData from '../../data/controllers/util/normalizeData.js';
@@ -34,9 +35,9 @@ export default async function vtile(req, reply) {
34
35
  pg, params = {}, query = {}, funcs = {}, log,
35
36
  } = req;
36
37
 
37
- const { filter, widget, sql, cluster, type, nocache, geom = 'geom', pointZoom = 0 } = query;
38
+ const { filter, widget, dashboard, sql, cluster, type, nocache, geom = 'geom', pointZoom = 0 } = query;
39
+
38
40
 
39
- const { config = {} } = funcs;
40
41
 
41
42
  if (!widget) {
42
43
  return { message: 'not enough params: widget', status: 400 };
@@ -49,18 +50,11 @@ export default async function vtile(req, reply) {
49
50
  return { message: 'not enough params: xyz', status: 400 };
50
51
  }
51
52
 
52
- const cwd = process.cwd();
53
- const widgetDir = path.join(cwd, 'server/templates/widget');
54
-
55
- const filePath = path.join(widgetDir, widget + '.yml');
56
-
57
- if (!existsSync(filePath)) {
58
- return { message: { root: config?.local ? widgetDir : undefined, error: `not found`, widget }, status: 404 };
59
- }
53
+ const { data } = await getWidget({ widget, dashboard })
60
54
 
61
55
  const headers = {
62
56
  'Content-Type': 'application/x-protobuf',
63
- 'Cache-Control': nocache || sql || config?.local
57
+ 'Cache-Control': nocache || sql
64
58
  ? 'no-cache'
65
59
  : 'public, max-age=86400',
66
60
  };
@@ -71,12 +65,7 @@ export default async function vtile(req, reply) {
71
65
  const file = path.join(root, `/map/vtile/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}${z}/${x}/${y}.mvt`);
72
66
 
73
67
  try {
74
- const fileData = readFileSync(filePath, 'utf-8');
75
- const { data = {} } = filePath.includes('.yml') ? yaml.loadSafe(fileData) : {};
76
68
 
77
- if (!data?.table) {
78
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table not specified`, status: 400 };
79
- }
80
69
 
81
70
  const pkey = pg.pk?.[data?.table];
82
71
  if (!pkey) {
@@ -84,15 +73,18 @@ export default async function vtile(req, reply) {
84
73
  }
85
74
 
86
75
  // data param
87
- const { table, where = '1=1', xName = {} } = normalizeData(data, query);
76
+ const { table, where = '1=1', xName = {}, metric } = normalizeData(data, query);
88
77
 
89
- if (!xName?.name) {
90
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: x axis column not specified`, status: 400 };
91
- }
92
78
 
93
79
  // get sql
94
- const filterQ = filter ? await funcs.getFilterSQL({ pg, table, filter, query }) : undefined;
95
- const q = `select "${pkey}", "${xName?.name}", /* st_asgeojson(geom)::json as */ ${geom} as geom from ${filterQ ? `(${filterQ})` : table} q where ${where}`;
80
+ const columns = data.columns?.map(el => el.name || el)?.join(',') || '1';
81
+ const filterQ = filter ? await funcs.getFilterSQL({ pg, table, filter }) : undefined;
82
+ const q = `select "${pkey}",
83
+ ${data?.color ? `"${data?.color}"` : '0'} as x,
84
+ ${data.metrics?.[0] ? `"${data.metrics[0]}"::float` : '0'} as metric,
85
+ ${columns},
86
+ ${geom} as geom
87
+ from ${filterQ ? `(${filterQ})` : table} q where ${where}`;
96
88
 
97
89
  if (sql === '1') return q;
98
90
 
@@ -100,17 +92,18 @@ export default async function vtile(req, reply) {
100
92
  10: 0.1, 11: 0.05, 12: 0.005, 13: 0.0002, 14: 0.00005,
101
93
  }[z] || 0.000001;
102
94
 
103
- const geomCol = parseInt(z, 10) < parseInt(pointZoom, 10)
95
+ const geomCol = parseInt(z, 10) < parseInt(pointZoom, 10) || true
104
96
  ? `ST_Centroid(${geom})`
105
97
  : geom;
106
-
98
+
107
99
  const bbox = mercator.bbox(+y, +x, +z, false/* , '900913' */);
108
100
  const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
109
101
 
110
102
  const areaZoom = area[z] && false ? ` and (st_area(st_transform(${geom},3857)))>'${area[z]}'` : '';
111
103
 
104
+
112
105
  const q1 = cluster > z
113
- ? `SELECT ST_AsMVT(q, '${table}', 4096, 'geom','row') as tile
106
+ ? `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
114
107
  FROM (
115
108
  SELECT floor(random() * 100000 + 1)::int + row_number() over() as row, count(*) as point_count,
116
109
  ST_AsMVTGeom(st_transform(st_centroid(ST_Union(geom)),3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) as geom
@@ -122,14 +115,15 @@ export default async function vtile(req, reply) {
122
115
  ORDER BY 1 DESC
123
116
  )q`
124
117
 
125
- : `SELECT ST_AsMVT(q, '${table}', 4096, 'geom','row') as tile
118
+ : `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
126
119
  FROM (
127
120
  SELECT
128
121
  floor(random() * 100000 + 1)::int + row_number() over() as row,
129
122
 
130
123
  ${pkey} as id,
131
- ${xName?.name} as x,
132
-
124
+ x,
125
+ metric,
126
+ ${columns},
133
127
  ST_AsMVTGeom(st_transform(${geomCol}, 3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) as geom
134
128
 
135
129
  FROM (select * from (${q})q where ${geom} && ${bbox2d}
@@ -144,9 +138,8 @@ export default async function vtile(req, reply) {
144
138
  ) q`;
145
139
 
146
140
  if (sql === '2') return q1;
147
-
148
- // auto Index
149
- funcs.autoIndex({ table, columns: [xName.name] });
141
+
142
+
150
143
 
151
144
  const { rows = [] } = await pg.query(q1);
152
145
 
@@ -154,7 +147,7 @@ export default async function vtile(req, reply) {
154
147
 
155
148
  const buffer = Buffer.concat(rows.map((el) => Buffer.from(el.tile)));
156
149
 
157
- if (!nocache && !config.local) {
150
+ if (!nocache) {
158
151
  await mkdir(path.dirname(file), { recursive: true });
159
152
  await writeFile(file, buffer, 'binary');
160
153
  }
@@ -1,8 +1,10 @@
1
- // import config from '../../../config.js';
2
-
1
+ import map from './controllers/map.js';
3
2
  import geojson from './controllers/geojson.js';
4
3
  import vtile from './controllers/vtile.js';
5
4
 
5
+ import cluster from './controllers/cluster.js';
6
+ import clusterVtile from './controllers/clusterVtile.js';
7
+
6
8
  const biSchema = {
7
9
  querystring: {
8
10
  widget: { type: 'string', pattern: '^([\\d\\w]+)$' },
@@ -14,9 +16,10 @@ const biSchema = {
14
16
  },
15
17
  };
16
18
 
17
-
18
19
  export default async function route(fastify, opts) {
19
- // const prefix = opts?.prefix || config.prefix || '/api';
20
+ fastify.get('/bi-map', { schema: biSchema }, map);
20
21
  fastify.get('/bi-geojson', { schema: biSchema }, geojson);
21
22
  fastify.get('/bi-vtile/:z/:y/:x', { schema: biSchema }, vtile);
23
+ fastify.get('/bi-cluster', { schema: biSchema }, cluster);
24
+ fastify.get('/bi-cluster-vtile/:z/:y/:x', { schema: biSchema }, clusterVtile);
22
25
  }
@@ -0,0 +1,56 @@
1
+ import { getTemplate } from '@opengis/fastify-table/utils.js';
2
+ import pgClients from '@opengis/fastify-table/pg/pgClients.js'
3
+ const pg = pgClients.client;
4
+
5
+ async function getWidget({ dashboard, widget }) {
6
+
7
+ const dashboardData = dashboard ? await getTemplate('dashboard', dashboard) : null;
8
+
9
+ const { id, tableName } = !dashboardData && pg.pk['bi.dashboard'] && dashboard ?
10
+ await pg.query(`select dashboard_id as id, table_name as "tableName" from bi.dashboard where $1 in (dashboard_id, name)`, [dashboard]).then((res1) => res1.rows?.[0] || {}) : {};
11
+
12
+ if (!dashboardData && dashboard && !id) {
13
+ return { message: `dashboard not found: ${dashboard}`, status: 404 };
14
+ }
15
+
16
+ dashboardData?.forEach(el => {
17
+ el[2] = el[0].split('.')[0]
18
+ })
19
+ const dashboardIndex = dashboardData?.find(el => el[2] == 'index')?.[1];
20
+
21
+ const widgetData = dashboard ? dashboardData?.find(el => el[2] === (widget || 'index'))?.[1] : await getTemplate('widget', widget);
22
+ if (typeof widgetData === 'string') {
23
+ return { source: widgetData, status: 200 }
24
+ }
25
+ if (!id && !dashboardData && !widgetData) {
26
+ return { message: `not found ${widget} ${dashboard}`, status: 404 };
27
+ }
28
+
29
+ const q = `select *, coalesce(data::jsonb, '{}'::jsonb) || jsonb_build_object('table', table_name) as data from bi.widget where dashboard_id=$1 and name=$2`;
30
+
31
+ const { type, text, data = {}, controls, style, options } = widgetData || await pg.query(q, [id || dashboard, widget]).then((res1) => res1.rows?.[0] || {});
32
+
33
+ if (!type) {
34
+ return { message: `widget not found: ${widget}`, status: 404 };
35
+ }
36
+
37
+ Object.assign(data, {
38
+ table: data.table
39
+ || tableName
40
+ || widgetData?.table_name
41
+ || dashboardIndex?.table
42
+ || dashboardIndex?.table_name,
43
+ db: dashboardIndex?.db || pgClients.client?.options?.database,
44
+ });
45
+ const main = { ...dashboardIndex || {}, ...widgetData, ...data };
46
+
47
+ if (!main?.table) {
48
+ return { message: /* json.error || */ `invalid ${widget ? 'widget' : 'dashboard'}: 1`, status: 404 };
49
+ }
50
+
51
+ const tableSQL = main?.sql?.map((el, i) => `left join lateral(${el})t${i + 1} on 1=1`);
52
+
53
+ return { ...main, tableSQL, data, type, text, controls, style, options };
54
+
55
+ }
56
+ export default getWidget;
package/utils.js ADDED
@@ -0,0 +1,11 @@
1
+ // This file contains code that we reuse
2
+ // between our tests.
3
+
4
+ // import getTemplatePath from '@opengis/fastify-table/table/controllers/utils/getTemplatePath.js';
5
+ import getWidget from './server/utils/getWidget.js';
6
+
7
+ export default null;
8
+ export {
9
+ // getTemplatePath,
10
+ getWidget
11
+ };
@@ -1,34 +0,0 @@
1
- import { _ as t, f as o } from "./import-file-D6RYWvi_.js";
2
- import { openBlock as s, createElementBlock as n, toDisplayString as m } from "vue";
3
- const c = {
4
- props: ["source", "dimensions"],
5
- data() {
6
- return {
7
- number: ""
8
- };
9
- },
10
- computed: {
11
- formattedNumber() {
12
- return o(this.number);
13
- }
14
- },
15
- mounted() {
16
- this.getNumber();
17
- },
18
- methods: {
19
- async getNumber() {
20
- try {
21
- this.number = this.source[0][this.dimensions[0]];
22
- } catch (e) {
23
- console.error(e);
24
- }
25
- }
26
- }
27
- }, a = { class: "font-[Inter,_Helvetica,_Arial] p-4 w-full text-[28px] pt-[10px] min-h-[130px] border-0" };
28
- function i(e, u, p, d, f, r) {
29
- return s(), n("div", a, " $" + m(r.formattedNumber), 1);
30
- }
31
- const b = /* @__PURE__ */ t(c, [["render", i]]);
32
- export {
33
- b as default
34
- };
@@ -1,17 +0,0 @@
1
- type: bar
2
- text: Quarterly Revenue (By Product Line)
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: obj_area
10
- operator: sum
11
- title: obj_area
12
-
13
- x: region
14
-
15
- controls:
16
- style:
17
- stack: true
@@ -1,18 +0,0 @@
1
- type: bar
2
- text: Розподіл культур за регіонами
3
-
4
- data:
5
- table: demo.parcel_object_culture # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: obj_area
10
- operator: sum
11
- title: obj_area
12
-
13
- x: region
14
- groupby: culture
15
-
16
- controls:
17
- style:
18
- stack: true
@@ -1,18 +0,0 @@
1
- type: bar
2
- text: Сума виплачених грантів
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: grant_sum
10
- operator: sum
11
- title: grant_sum
12
-
13
- x: region
14
- orderby: region
15
-
16
- controls:
17
- style:
18
-
@@ -1,8 +0,0 @@
1
- type: number
2
- text: Кількість грантів
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
- metrics: object_id
8
- controls:
@@ -1,47 +0,0 @@
1
- title: Гранти на розвиток садівництва та тепличного господарства
2
- description: Ініціатива реалізована в межах Програми USAID з аграрного і сільського розвитку (АГРО)
3
- table_name: demo.parcel
4
- panels:
5
- - type: column
6
- col: 4
7
- widgets:
8
- - total_area
9
-
10
- - widget: total_grand
11
- col: 4
12
-
13
- - widget: count_grand
14
- col: 4
15
-
16
- - widget: pie_area
17
- col: 4
18
-
19
- - widget: bar_area
20
- col: 8
21
-
22
- - widget: pie_grant
23
- col: 4
24
-
25
- - widget: bar_grand
26
- col: 8
27
-
28
- - widget: bar_culture
29
- col: 12
30
-
31
- - widget: list_culture
32
- col: 6
33
-
34
- - widget: list_grant
35
- col: 6
36
-
37
-
38
-
39
-
40
- style:
41
- color:
42
- stack: true # only for bar
43
- orientation: vertical # only for bar, line
44
- date_format: smart_date # d3 format number
45
- number_format: smart_date # d3 format number
46
- label: fff
47
- tooltip: 333
@@ -1,12 +0,0 @@
1
- type: listbar
2
- text: Розподіл за культурами. га
3
-
4
- data:
5
- table: demo.parcel_object_culture # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: obj_area
10
- operator: sum
11
- title: obj_area
12
- x: culture
@@ -1,12 +0,0 @@
1
- type: listbar
2
- text: Сума виплачених грантів, тис. грн
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: grant_sum
10
- operator: sum
11
- title: grant_sum
12
- x: region
@@ -1,4 +0,0 @@
1
- type: map
2
- text: Регіональний розподіл
3
-
4
- data:
@@ -1,17 +0,0 @@
1
- type: pie
2
- text: Площа, га
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: obj_area
10
- operator: sum
11
- title: obj_area
12
- x: object_type
13
-
14
- style:
15
- donut: true
16
- innerRadius: 30
17
- outerRadius: 70
@@ -1,17 +0,0 @@
1
- type: pie
2
- text: Виплата грантів
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: grant_sum
10
- operator: sum
11
- title: grant_sum
12
- x: object_type
13
-
14
- style:
15
- donut: true
16
- innerRadius: 30
17
- outerRadius: 70
@@ -1,9 +0,0 @@
1
- type: number
2
- text: Площа, га
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
- metrics: obj_area
8
- controls:
9
-
@@ -1,9 +0,0 @@
1
- type: number
2
- text: Сума грантів, тис. грн
3
-
4
- data:
5
- table: demo.parcel_object # Назва таблиці
6
- query: 1=1 # Запит
7
- metrics: grant_sum
8
- controls:
9
-
@@ -1,40 +0,0 @@
1
- title: Sales Dashboard
2
- description: This example dashboard provides insight into the business operations of vehicle seller
3
- table_name: demo.cleaned_sales_data
4
- panels:
5
- # row 1
6
- - type: column
7
- col: 3
8
- widgets:
9
- - total_revenue
10
- - total_products_sold
11
-
12
- - widget: quarterly_revenue
13
- col: 6
14
- - widget: vehicle_sales_info
15
- col: 3
16
-
17
- # row 2
18
- - widget: total_products_sold_by_product_line
19
- col: 3
20
- - widget: quarterly_revenue_by_product_line
21
- col: 6
22
- - widget: total_revenue_by_product_line
23
- col: 3
24
-
25
- grid: # сітка якщо є
26
- - { "x": 0, "y": 0, "w": 2, "h": 2, "i": 0, widget: "quarterly_revenue" }
27
- - { "x": 1, "y": 0, "w": 2, "h": 2, "i": 0, widget: "total_revenue" }
28
-
29
- widgets: null # самі віджети
30
-
31
- filters: null # фільтри
32
-
33
- style:
34
- color:
35
- stack: true # only for bar
36
- orientation: vertical # only for bar, line
37
- date_format: smart_date # d3 format number
38
- number_format: smart_date # d3 format number
39
- label: 222
40
- tooltip: 333
@@ -1,19 +0,0 @@
1
- type: bar
2
- text: Quarterly Revenue
3
-
4
- data:
5
- table: demo.cleaned_sales_data # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: sales
10
- operator: sum
11
- title: sales
12
-
13
- x: order_date
14
- granularity: quarter
15
- orderby: year
16
-
17
- controls:
18
- style:
19
- horizontal: false
@@ -1,19 +0,0 @@
1
- type: bar
2
- text: Quarterly Revenue (By Product Line)
3
-
4
- data:
5
- table: demo.cleaned_sales_data # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: sales
10
- operator: sum
11
- title: sales
12
-
13
- x: order_date
14
- granularity: quarter
15
- groupby: product_line
16
-
17
- controls:
18
- style:
19
- stack: true
@@ -1,9 +0,0 @@
1
- type: number
2
- text: Total Products Sold
3
-
4
- data:
5
- table: demo.cleaned_sales_data # Назва таблиці
6
- query: 1=1 # Запит
7
- metrics:
8
- - quantity_ordered
9
- controls:
@@ -1,12 +0,0 @@
1
- type: listbar
2
- text: Total Products Sold (By Product Line)
3
-
4
- data:
5
- table: demo.cleaned_sales_data # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: quantity_ordered
10
- operator: sum
11
- title: quantity_sold
12
- x: product_line
@@ -1,8 +0,0 @@
1
- type: number
2
- text: Total Revenue
3
-
4
- data:
5
- table: demo.cleaned_sales_data # Назва таблиці
6
- query: 1=1 # Запит
7
- metrics: sales
8
- controls:
@@ -1,17 +0,0 @@
1
- type: pie
2
- text: Total Revenue (By Product Line)
3
-
4
- data:
5
- table: demo.cleaned_sales_data # Назва таблиці
6
- query: 1=1 # Запит
7
-
8
- metrics: # Групування
9
- - name: sales
10
- operator: sum
11
- title: sales
12
- x: product_line
13
-
14
- style:
15
- donut: true
16
- innerRadius: 30
17
- outerRadius: 70