@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.
- package/README.md +4 -8
- package/assets/data-samples.jpg +0 -0
- package/client/pages/data-ooc/data-ooc-list-page.js +43 -48
- package/client/pages/data-report/data-report-embed-page.js +113 -0
- package/client/pages/data-report/data-report-list-page.js +12 -10
- package/client/pages/data-report/jasper-report-oocs-page.js +120 -0
- package/client/pages/data-report/jasper-report-samples-crosstab-page.js +120 -0
- package/client/pages/data-report/jasper-report-samples-page.js +120 -0
- package/client/pages/data-sample/data-sample-list-page.js +43 -48
- package/client/pages/data-sensor/data-sensor-list-page.js +37 -53
- package/client/pages/data-set/data-set-list-page.js +30 -45
- package/client/route.js +16 -0
- package/config/config.development.js +13 -0
- package/config/config.production.js +1 -0
- package/dist-server/controllers/jasper-report.js +156 -0
- package/dist-server/controllers/jasper-report.js.map +1 -0
- package/dist-server/routes.js +4 -0
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/data-set/data-set-mutation.js +37 -7
- package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
- package/dist-server/service/data-set/data-set-query.js +23 -0
- package/dist-server/service/data-set/data-set-query.js.map +1 -1
- package/dist-server/service/data-set/data-set-type.js +13 -4
- package/dist-server/service/data-set/data-set-type.js.map +1 -1
- package/dist-server/service/data-set/data-set.js +4 -0
- package/dist-server/service/data-set/data-set.js.map +1 -1
- package/package.json +17 -16
- package/server/controllers/jasper-report.ts +170 -0
- package/server/routes.ts +4 -0
- package/server/service/data-set/data-set-mutation.ts +51 -8
- package/server/service/data-set/data-set-query.ts +21 -0
- package/server/service/data-set/data-set-type.ts +7 -0
- package/server/service/data-set/data-set.ts +3 -0
- package/things-factory.config.js +17 -1
- package/translations/en.json +1 -0
- 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=${
|
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"
|
74
|
-
<
|
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
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
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
|
-
|
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=${
|
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"
|
85
|
-
<
|
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: '
|
277
|
-
header: i18next.t('field.
|
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
|
7
|
-
import { PageView, store } from '@operato/shell'
|
6
|
+
import gql from 'graphql-tag'
|
8
7
|
import { css, html } from 'lit'
|
9
|
-
import
|
10
|
-
import {
|
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
|
11
|
+
import { getEditor, getRenderer } from '@operato/data-grist'
|
14
12
|
import { OxDataUseCase } from '@operato/dataset'
|
15
13
|
import { client } from '@operato/graphql'
|
16
|
-
import {
|
17
|
-
import
|
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
|
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: '
|
81
|
-
value: '
|
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"
|
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 @@
|
|
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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"}
|
package/dist-server/routes.js
CHANGED
@@ -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
|