@things-factory/dataset 5.0.0-alpha.40 → 5.0.0-alpha.43

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 (36) hide show
  1. package/README.md +4 -8
  2. package/assets/data-samples.jpg +0 -0
  3. package/client/pages/data-ooc/data-ooc-list-page.js +43 -48
  4. package/client/pages/data-report/data-report-embed-page.js +113 -0
  5. package/client/pages/data-report/data-report-list-page.js +12 -10
  6. package/client/pages/data-report/jasper-report-oocs-page.js +120 -0
  7. package/client/pages/data-report/jasper-report-samples-crosstab-page.js +120 -0
  8. package/client/pages/data-report/jasper-report-samples-page.js +120 -0
  9. package/client/pages/data-sample/data-sample-list-page.js +43 -48
  10. package/client/pages/data-sensor/data-sensor-list-page.js +37 -53
  11. package/client/pages/data-set/data-set-list-page.js +30 -45
  12. package/client/route.js +16 -0
  13. package/config/config.development.js +13 -0
  14. package/config/config.production.js +1 -0
  15. package/dist-server/controllers/jasper-report.js +156 -0
  16. package/dist-server/controllers/jasper-report.js.map +1 -0
  17. package/dist-server/routes.js +4 -0
  18. package/dist-server/routes.js.map +1 -1
  19. package/dist-server/service/data-set/data-set-mutation.js +37 -7
  20. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  21. package/dist-server/service/data-set/data-set-query.js +23 -0
  22. package/dist-server/service/data-set/data-set-query.js.map +1 -1
  23. package/dist-server/service/data-set/data-set-type.js +13 -4
  24. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  25. package/dist-server/service/data-set/data-set.js +4 -0
  26. package/dist-server/service/data-set/data-set.js.map +1 -1
  27. package/package.json +17 -16
  28. package/server/controllers/jasper-report.ts +170 -0
  29. package/server/routes.ts +4 -0
  30. package/server/service/data-set/data-set-mutation.ts +51 -8
  31. package/server/service/data-set/data-set-query.ts +21 -0
  32. package/server/service/data-set/data-set-type.ts +7 -0
  33. package/server/service/data-set/data-set.ts +3 -0
  34. package/things-factory.config.js +17 -1
  35. package/translations/en.json +1 -0
  36. package/translations/ko.json +1 -0
@@ -9,20 +9,22 @@ import { client } from '@operato/graphql'
9
9
  import { i18next, localize } from '@operato/i18n'
10
10
  import { openPopup } from '@operato/layout'
11
11
  import { PageView, store } from '@operato/shell'
12
- import { ScrollbarStyles } from '@operato/styles'
12
+ import { CommonGristStyles, ScrollbarStyles } from '@operato/styles'
13
13
  import { isMobileDevice } from '@operato/utils'
14
14
 
15
15
  export class DataSampleListPage extends connect(store)(localize(i18next)(PageView)) {
16
16
  static get properties() {
17
17
  return {
18
18
  active: String,
19
- gristConfig: Object
19
+ gristConfig: Object,
20
+ mode: String
20
21
  }
21
22
  }
22
23
 
23
24
  static get styles() {
24
25
  return [
25
26
  ScrollbarStyles,
27
+ CommonGristStyles,
26
28
  css`
27
29
  :host {
28
30
  display: flex;
@@ -35,18 +37,6 @@ export class DataSampleListPage extends connect(store)(localize(i18next)(PageVie
35
37
  overflow-y: auto;
36
38
  flex: 1;
37
39
  }
38
-
39
- #filters {
40
- display: flex;
41
- flex-direction: row;
42
- justify-content: space-between;
43
-
44
- background-color: white;
45
- }
46
-
47
- #filters > * {
48
- padding: var(--padding-default) var(--padding-wide);
49
- }
50
40
  `
51
41
  ]
52
42
  }
@@ -64,14 +54,42 @@ export class DataSampleListPage extends connect(store)(localize(i18next)(PageVie
64
54
  }
65
55
 
66
56
  render() {
57
+ const mode = this.mode || (isMobileDevice() ? 'LIST' : 'GRID')
58
+
67
59
  return html`
68
60
  <ox-grist
69
- .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
61
+ .mode=${mode}
70
62
  .config=${this.gristConfig}
71
63
  .fetchHandler=${this.fetchHandler.bind(this)}
64
+ url-params-sensitive
72
65
  >
73
- <div slot="headroom" id="filters">
74
- <ox-filters-form></ox-filters-form>
66
+ <div slot="headroom">
67
+ <div id="filters">
68
+ <ox-filters-form></ox-filters-form>
69
+ </div>
70
+
71
+ <div id="sorters">
72
+ Sort
73
+ <mwc-icon
74
+ @click=${e => {
75
+ const target = e.currentTarget
76
+ this.renderRoot.querySelector('#sorter-control').open({
77
+ right: 0,
78
+ top: target.offsetTop + target.offsetHeight
79
+ })
80
+ }}
81
+ >expand_more</mwc-icon
82
+ >
83
+ <ox-popup id="sorter-control">
84
+ <ox-sorters-control> </ox-sorters-control>
85
+ </ox-popup>
86
+ </div>
87
+
88
+ <div id="modes">
89
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
90
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
91
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
92
+ </div>
75
93
  </div>
76
94
  </ox-grist>
77
95
  `
@@ -81,30 +99,7 @@ export class DataSampleListPage extends connect(store)(localize(i18next)(PageVie
81
99
  return this.renderRoot.querySelector('ox-grist')
82
100
  }
83
101
 
84
- // update with url params value
85
- _updateSearchConfig(lifecycle) {
86
- // this.searchConfig = this.searchConfig.map(conf => {
87
- // if (conf.name in lifecycle.params) {
88
- // conf.value = lifecycle.params[conf.name]
89
- // } else {
90
- // delete conf.value
91
- // }
92
- // return conf
93
- // })
94
- }
95
-
96
- // set default field value to record with searchConfig
97
- _setDefaultFieldsValue(fields) {
98
- // this.searchConfig.forEach(conf => {
99
- // if (!fields[conf.name] && conf.value) {
100
- // fields[conf.name] = conf.value
101
- // }
102
- // })
103
- }
104
-
105
102
  async pageInitialized(lifecycle) {
106
- this._updateSearchConfig(lifecycle)
107
-
108
103
  this.gristConfig = {
109
104
  list: { fields: ['dataSet', 'data', 'spec', 'updater', 'updatedAt'] },
110
105
  columns: [
@@ -292,15 +287,15 @@ export class DataSampleListPage extends connect(store)(localize(i18next)(PageVie
292
287
  this.grist.fetch()
293
288
  }
294
289
 
295
- async pageUpdated(changes, lifecycle) {
296
- if (this.active) {
297
- // update with url params value
298
- this._updateSearchConfig(lifecycle)
299
- await this.updateComplete
290
+ // async pageUpdated(changes, lifecycle) {
291
+ // if (this.active) {
292
+ // // update with url params value
293
+ // this._updateSearchConfig(lifecycle)
294
+ // await this.updateComplete
300
295
 
301
- this.grist.fetch()
302
- }
303
- }
296
+ // this.grist.fetch()
297
+ // }
298
+ // }
304
299
 
305
300
  async fetchHandler({ page, limit, sortings = [], filters = [] }) {
306
301
  const response = await client.query({
@@ -8,20 +8,22 @@ import { client } from '@operato/graphql'
8
8
  import { i18next, localize } from '@operato/i18n'
9
9
  import { notify } from '@operato/layout'
10
10
  import { PageView, store } from '@operato/shell'
11
- import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
11
+ import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
12
12
  import { isMobileDevice } from '@operato/utils'
13
13
 
14
14
  export class DataSensorListPage extends connect(store)(localize(i18next)(PageView)) {
15
15
  static get properties() {
16
16
  return {
17
17
  active: String,
18
- gristConfig: Object
18
+ gristConfig: Object,
19
+ mode: String
19
20
  }
20
21
  }
21
22
 
22
23
  static get styles() {
23
24
  return [
24
25
  ScrollbarStyles,
26
+ CommonGristStyles,
25
27
  css`
26
28
  :host {
27
29
  display: flex;
@@ -34,18 +36,6 @@ export class DataSensorListPage extends connect(store)(localize(i18next)(PageVie
34
36
  overflow-y: auto;
35
37
  flex: 1;
36
38
  }
37
-
38
- #filters {
39
- display: flex;
40
- flex-direction: row;
41
- justify-content: space-between;
42
-
43
- background-color: white;
44
- }
45
-
46
- #filters > * {
47
- padding: var(--padding-default) var(--padding-wide);
48
- }
49
39
  `
50
40
  ]
51
41
  }
@@ -75,14 +65,42 @@ export class DataSensorListPage extends connect(store)(localize(i18next)(PageVie
75
65
  }
76
66
 
77
67
  render() {
68
+ const mode = this.mode || (isMobileDevice() ? 'LIST' : 'GRID')
69
+
78
70
  return html`
79
71
  <ox-grist
80
- .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
72
+ .mode=${mode}
81
73
  .config=${this.gristConfig}
82
74
  .fetchHandler=${this.fetchHandler.bind(this)}
75
+ url-params-sensitive
83
76
  >
84
- <div slot="headroom" id="filters">
85
- <ox-filters-form></ox-filters-form>
77
+ <div slot="headroom">
78
+ <div id="filters">
79
+ <ox-filters-form></ox-filters-form>
80
+ </div>
81
+
82
+ <div id="sorters">
83
+ Sort
84
+ <mwc-icon
85
+ @click=${e => {
86
+ const target = e.currentTarget
87
+ this.renderRoot.querySelector('#sorter-control').open({
88
+ right: 0,
89
+ top: target.offsetTop + target.offsetHeight
90
+ })
91
+ }}
92
+ >expand_more</mwc-icon
93
+ >
94
+ <ox-popup id="sorter-control">
95
+ <ox-sorters-control> </ox-sorters-control>
96
+ </ox-popup>
97
+ </div>
98
+
99
+ <div id="modes">
100
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
101
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
102
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
103
+ </div>
86
104
  </div>
87
105
  </ox-grist>
88
106
  `
@@ -92,30 +110,7 @@ export class DataSensorListPage extends connect(store)(localize(i18next)(PageVie
92
110
  return this.renderRoot.querySelector('ox-grist')
93
111
  }
94
112
 
95
- // update with url params value
96
- _updateSearchConfig(lifecycle) {
97
- // this.searchConfig = this.searchConfig.map(conf => {
98
- // if (conf.name in lifecycle.params) {
99
- // conf.value = lifecycle.params[conf.name]
100
- // } else {
101
- // delete conf.value
102
- // }
103
- // return conf
104
- // })
105
- }
106
-
107
- // set default field value to record with searchConfig
108
- _setDefaultFieldsValue(fields) {
109
- // this.searchConfig.forEach(conf => {
110
- // if (!fields[conf.name] && conf.value) {
111
- // fields[conf.name] = conf.value
112
- // }
113
- // })
114
- }
115
-
116
113
  async pageInitialized(lifecycle) {
117
- this._updateSearchConfig(lifecycle)
118
-
119
114
  this.gristConfig = {
120
115
  list: { fields: ['name', 'description', 'active'] },
121
116
  columns: [
@@ -273,8 +268,8 @@ export class DataSensorListPage extends connect(store)(localize(i18next)(PageVie
273
268
  },
274
269
  {
275
270
  type: 'datetime',
276
- name: 'collectedAt',
277
- header: i18next.t('field.collected-at'),
271
+ name: 'updatedAt',
272
+ header: i18next.t('field.updated_at'),
278
273
  record: {
279
274
  editable: false
280
275
  },
@@ -299,16 +294,6 @@ export class DataSensorListPage extends connect(store)(localize(i18next)(PageVie
299
294
  this.grist.fetch()
300
295
  }
301
296
 
302
- async pageUpdated(changes, lifecycle) {
303
- if (this.active) {
304
- // update with url params value
305
- this._updateSearchConfig(lifecycle)
306
- await this.updateComplete
307
-
308
- this.grist.fetch()
309
- }
310
- }
311
-
312
297
  async fetchHandler({ page, limit, sortings = [], filters = [] }) {
313
298
  const response = await client.query({
314
299
  query: gql`
@@ -414,7 +399,6 @@ export class DataSensorListPage extends connect(store)(localize(i18next)(PageVie
414
399
  for (let key in dirtyFields) {
415
400
  patchField[key] = dirtyFields[key].after
416
401
  }
417
- this._setDefaultFieldsValue(patchField)
418
402
  patchField.cuFlag = patch.__dirty__
419
403
 
420
404
  return patchField
@@ -3,20 +3,20 @@ import './data-item-list.js'
3
3
  import './data-set-importer.js'
4
4
  import '../data-entry/data-entry-form.js'
5
5
 
6
- import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
7
- import { PageView, store } from '@operato/shell'
6
+ import gql from 'graphql-tag'
8
7
  import { css, html } from 'lit'
9
- import { getEditor, getRenderer } from '@operato/data-grist'
10
- import { i18next, localize } from '@operato/i18n'
11
- import { notify, openPopup } from '@operato/layout'
8
+ import moment from 'moment-timezone'
9
+ import { connect } from 'pwa-helpers/connect-mixin'
12
10
 
13
- import JSON5 from 'json5'
11
+ import { getEditor, getRenderer } from '@operato/data-grist'
14
12
  import { OxDataUseCase } from '@operato/dataset'
15
13
  import { client } from '@operato/graphql'
16
- import { connect } from 'pwa-helpers/connect-mixin'
17
- import gql from 'graphql-tag'
14
+ import { i18next, localize } from '@operato/i18n'
15
+ import { notify, openPopup } from '@operato/layout'
16
+ import { PageView, store } from '@operato/shell'
17
+ import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
18
18
  import { isMobileDevice } from '@operato/utils'
19
- import moment from 'moment-timezone'
19
+ import { FileSelector } from '@things-factory/attachment-ui'
20
20
 
21
21
  const DEFAULT_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone
22
22
  const TIMEZONE_OPTIONS = ['', DEFAULT_TZ, ...moment.tz.names().filter(tz => tz !== DEFAULT_TZ)]
@@ -77,8 +77,8 @@ const REPORT_TYPES = [
77
77
  value: 'generated'
78
78
  },
79
79
  {
80
- display: 'Custom',
81
- value: 'custom'
80
+ display: 'Embed',
81
+ value: 'embed'
82
82
  },
83
83
  {
84
84
  display: 'Page',
@@ -98,14 +98,11 @@ const USECASE_OPTIONS = () => {
98
98
  }
99
99
  })
100
100
  }
101
-
102
101
  export class DataSetListPage extends connect(store)(localize(i18next)(PageView)) {
103
102
  static get properties() {
104
103
  return {
105
104
  active: String,
106
105
  gristConfig: Object,
107
- filters: Object,
108
- sorters: Object,
109
106
  mode: String
110
107
  }
111
108
  }
@@ -165,11 +162,10 @@ export class DataSetListPage extends connect(store)(localize(i18next)(PageView))
165
162
  <ox-grist
166
163
  .mode=${mode}
167
164
  .config=${this.gristConfig}
168
- .filters=${this.filters}
169
- .orders=${this.orders}
170
165
  .fetchHandler=${this.fetchHandler.bind(this)}
166
+ url-params-sensitive
171
167
  >
172
- <div slot="headroom" id="filters">
168
+ <div slot="headroom">
173
169
  <div id="filters">
174
170
  <ox-filters-form></ox-filters-form>
175
171
  </div>
@@ -206,6 +202,7 @@ export class DataSetListPage extends connect(store)(localize(i18next)(PageView))
206
202
  }
207
203
 
208
204
  async pageInitialized(lifecycle) {
205
+ const fileSelector = new FileSelector()
209
206
  this.gristConfig = {
210
207
  list: {
211
208
  fields: ['name', 'description'],
@@ -469,6 +466,15 @@ export class DataSetListPage extends connect(store)(localize(i18next)(PageView))
469
466
  },
470
467
  width: 140
471
468
  },
469
+ {
470
+ type: 'file',
471
+ name: 'reportTemplate',
472
+ header: i18next.t('field.report-template'),
473
+ record: {
474
+ editable: true
475
+ },
476
+ width: 80
477
+ },
472
478
  {
473
479
  type: 'resource-object',
474
480
  name: 'updater',
@@ -507,34 +513,6 @@ export class DataSetListPage extends connect(store)(localize(i18next)(PageView))
507
513
  this.grist.fetch()
508
514
  }
509
515
 
510
- async pageUpdated(changes, lifecycle) {
511
- if (this.active) {
512
- await this.updateComplete
513
-
514
- var filters = lifecycle.params?.['filters']
515
- if (filters) {
516
- try {
517
- filters = JSON5.parse(filters)
518
- this.filters = filters
519
- } catch (e) {
520
- console.error(`filters parameter parsing error: ${e}`)
521
- }
522
- }
523
-
524
- var sorters = lifecycle.params?.['sorters']
525
- if (sorters) {
526
- try {
527
- sorters = JSON5.parse(sorters)
528
- this.sorters = sorters
529
- } catch (e) {
530
- console.error(`sorters parameter parsing error: ${e}`)
531
- }
532
- }
533
-
534
- this.grist.fetch()
535
- }
536
- }
537
-
538
516
  async fetchHandler({ page, limit, sortings = [], filters = [] }) {
539
517
  const response = await client.query({
540
518
  query: gql`
@@ -564,6 +542,7 @@ export class DataSetListPage extends connect(store)(localize(i18next)(PageView))
564
542
  monitorView
565
543
  reportType
566
544
  reportView
545
+ reportTemplate
567
546
  updater {
568
547
  id
569
548
  name
@@ -656,6 +635,9 @@ export class DataSetListPage extends connect(store)(localize(i18next)(PageView))
656
635
  for (let key in dirtyFields) {
657
636
  patchField[key] = dirtyFields[key].after
658
637
  }
638
+ if (patchField['reportTemplate'] instanceof FileList) {
639
+ patchField['reportTemplate'] = patchField['reportTemplate'][0]
640
+ }
659
641
  patchField.cuFlag = patch.__dirty__
660
642
 
661
643
  return patchField
@@ -671,6 +653,9 @@ export class DataSetListPage extends connect(store)(localize(i18next)(PageView))
671
653
  `,
672
654
  variables: {
673
655
  patches
656
+ },
657
+ context: {
658
+ hasUpload: true
674
659
  }
675
660
  })
676
661
 
package/client/route.js CHANGED
@@ -23,5 +23,21 @@ export default function route(page) {
23
23
  case 'data-report-list':
24
24
  import('./pages/data-report/data-report-list-page.js')
25
25
  return page
26
+
27
+ case 'jasper-report-samples':
28
+ import('./pages/data-report/jasper-report-samples-page.js')
29
+ return page
30
+
31
+ case 'jasper-report-samples-crosstab':
32
+ import('./pages/data-report/jasper-report-samples-crosstab-page.js')
33
+ return page
34
+
35
+ case 'jasper-report-oocs':
36
+ import('./pages/data-report/jasper-report-oocs-page.js')
37
+ return page
38
+
39
+ case 'data-report-embed':
40
+ import('./pages/data-report/data-report-embed-page')
41
+ return page
26
42
  }
27
43
  }
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ dataReport: {
3
+ jasper: {
4
+ endpoint: {
5
+ host: 'localhost',
6
+ port: 8090
7
+ },
8
+ datasource: {
9
+ database: 'things-factory'
10
+ }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ module.exports = {}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderJasperReport = void 0;
7
+ const form_data_1 = __importDefault(require("form-data"));
8
+ const node_fetch_1 = __importDefault(require("node-fetch"));
9
+ const attachment_base_1 = require("@things-factory/attachment-base");
10
+ const aws_base_1 = require("@things-factory/aws-base");
11
+ const env_1 = require("@things-factory/env");
12
+ const dataReportConfig = env_1.config.get('dataReport');
13
+ const { jasper: { endpoint: { protocol: PROTOCOL, host: HOST, port: PORT }, datasource: { database: DATABASE } } } = dataReportConfig;
14
+ /** author: ywnam123 */
15
+ function transformValuesToRows(queryResult) {
16
+ var parseData = [];
17
+ let index = 1;
18
+ for (let i = 0; i < queryResult.Items.length; i++) {
19
+ var j = 0;
20
+ const data = JSON.parse(queryResult.Items[i].data);
21
+ const spec = JSON.parse(queryResult.Items[i].spec);
22
+ for (let key in data) {
23
+ if (Array.isArray(data[key])) {
24
+ for (j = 0; j < data[key].length; j++) {
25
+ for (let specKey in spec) {
26
+ if (key === specKey) {
27
+ parseData.push({
28
+ item: spec[specKey].name,
29
+ index: index + j,
30
+ value: String(data[key][j])
31
+ });
32
+ }
33
+ }
34
+ }
35
+ }
36
+ else {
37
+ parseData.push({
38
+ item: key,
39
+ index,
40
+ value: String(data[key])
41
+ });
42
+ }
43
+ }
44
+ if (j !== 0) {
45
+ index = index + j;
46
+ }
47
+ else {
48
+ index = index + 1;
49
+ }
50
+ }
51
+ }
52
+ /** @todo considering trasformation in lambda, as massive dataset */
53
+ function pivotData(rows) {
54
+ var _a, _b;
55
+ let parsedData = [];
56
+ let index = 1;
57
+ for (let i = 0; i < rows.length; i++) {
58
+ let j = 0;
59
+ const data = JSON.parse(rows[i].data);
60
+ const spec = JSON.parse(rows[i].spec);
61
+ for (let key in data) {
62
+ /** @todo rule to display or not, about unspecified spec */
63
+ const value = data[key];
64
+ !((_a = spec[key]) === null || _a === void 0 ? void 0 : _a.hidden) && parsedData.push({
65
+ item: ((_b = spec[key]) === null || _b === void 0 ? void 0 : _b.name) || key,
66
+ index,
67
+ value: Array.isArray(value) ? value.join(', ') : value
68
+ });
69
+ }
70
+ if (j !== 0) {
71
+ index = index + j;
72
+ }
73
+ else {
74
+ index = index + 1;
75
+ }
76
+ }
77
+ return parsedData;
78
+ }
79
+ function parseJsonDataField(rows) {
80
+ let parsedData = [];
81
+ for (let i = 0; i < rows.length; i++) {
82
+ const row = rows[i];
83
+ const data = JSON.parse(row.data);
84
+ for (let key in data) {
85
+ if (Array.isArray(data[key])) {
86
+ data[key] = data[key].toString();
87
+ }
88
+ }
89
+ delete row.data;
90
+ parsedData.push(Object.assign(Object.assign({}, row), data));
91
+ }
92
+ return parsedData;
93
+ }
94
+ const athenaClient = new aws_base_1.AthenaController();
95
+ async function queryAthena(params) {
96
+ const { table, domain, dataSetId, fromWorkDate, toWorkDate, workShift, timezone } = params;
97
+ const queryData = {
98
+ sql: `SELECT name, description, data, spec, workdate, workshift,
99
+ DATE_FORMAT(
100
+ FROM_UNIXTIME(collected_at / 1000 / 1000) AT TIME ZONE '${timezone || 'UTC'}',
101
+ '%Y-%m-%d %H:%i:%s'
102
+ ) AS collected_at
103
+ FROM ${table}
104
+ WHERE domain='${domain}'
105
+ AND datasetid = '${dataSetId}'
106
+ AND workdate >= '${fromWorkDate}'
107
+ AND workdate <= '${toWorkDate}'
108
+ ${workShift ? 'AND workshift = \'' + workShift + '\'' : ''}
109
+ ORDER BY collected_at`,
110
+ db: DATABASE
111
+ };
112
+ // and json_extract_scalar(data, '$.dauid') = 'A8032AD81730'
113
+ return await athenaClient.query(queryData);
114
+ }
115
+ async function renderJasperReport(context) {
116
+ const { state: { domain }, query } = context;
117
+ const template = await attachment_base_1.STORAGE.readFile(query['template'] || 'dynamic_header_sample.jrxml', 'utf-8');
118
+ let templateType = query['templateType'] || 'crosstab';
119
+ let parsedData = [];
120
+ // @todo: get dataset timezone
121
+ /**
122
+ * const variables = await gql(dataSet(id:${dataSetId}) {
123
+ * name, description, partition_keys, timezone
124
+ * })
125
+ */
126
+ query['domain'] = domain === null || domain === void 0 ? void 0 : domain.subdomain;
127
+ query['timezone'] = domain === null || domain === void 0 ? void 0 : domain.timezone;
128
+ const queryResult = await queryAthena(query);
129
+ const rows = queryResult.Items;
130
+ if (!rows.length) {
131
+ return '<h3>Not found result.</h3>';
132
+ }
133
+ else {
134
+ const firstRow = rows[0];
135
+ // uses the first row values as data-set has no history data.
136
+ const parameters = Object.assign({ name: firstRow.name, description: firstRow.description }, query);
137
+ if (templateType === 'crosstab') {
138
+ parsedData = pivotData(rows);
139
+ }
140
+ else {
141
+ parsedData = parseJsonDataField(rows);
142
+ }
143
+ const formData = new form_data_1.default();
144
+ formData.append('template', template);
145
+ formData.append('jsonString', JSON.stringify(parsedData));
146
+ formData.append('parameters', JSON.stringify(parameters));
147
+ const reportUrl = `${PROTOCOL || 'http'}://${HOST}:${PORT}/rest/report/show_html`;
148
+ const response = await (0, node_fetch_1.default)(reportUrl, {
149
+ method: 'POST',
150
+ body: formData
151
+ });
152
+ return await response.text();
153
+ }
154
+ }
155
+ exports.renderJasperReport = renderJasperReport;
156
+ //# sourceMappingURL=jasper-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jasper-report.js","sourceRoot":"","sources":["../../server/controllers/jasper-report.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const typeorm_1 = require("typeorm");
4
4
  const auth_base_1 = require("@things-factory/auth-base");
5
5
  const create_data_sample_1 = require("./controllers/create-data-sample");
6
+ const jasper_report_1 = require("./controllers/jasper-report");
6
7
  const data_sensor_1 = require("./service/data-sensor/data-sensor");
7
8
  const debug = require('debug')('things-factory:dataset:routes');
8
9
  process.on('bootstrap-module-global-public-route', (app, globalPublicRouter) => {
@@ -70,5 +71,8 @@ process.on('bootstrap-module-domain-private-route', (app, domainPrivateRouter) =
70
71
  /*
71
72
  * can add domain private routes to application (auth required, tenancy required)
72
73
  */
74
+ domainPrivateRouter.get('/data-report/jasper', async (context, next) => {
75
+ context.body = await (0, jasper_report_1.renderJasperReport)(context);
76
+ });
73
77
  });
74
78
  //# sourceMappingURL=routes.js.map