@things-factory/dataset 5.0.14 → 5.0.15

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 (96) hide show
  1. package/README.md +20 -0
  2. package/assets/data-report.jpg +0 -0
  3. package/assets/glue-table-indices.png +0 -0
  4. package/client/pages/data-archive/data-archive-list-page.js +290 -0
  5. package/client/pages/data-archive/data-archive-request-popup.js +198 -0
  6. package/client/pages/data-key-set/data-key-set-list-page.js +97 -2
  7. package/client/pages/data-report/data-report-embed-page.js +16 -12
  8. package/client/pages/data-report/data-report-list-page.js +16 -13
  9. package/client/pages/data-report/data-report-samples-page.js +186 -0
  10. package/client/pages/data-set/data-set-list-page.js +8 -0
  11. package/client/route.js +20 -12
  12. package/config/config.development.js +21 -0
  13. package/config/config.production.js +32 -11
  14. package/dist-server/controllers/create-data-sample.js +3 -1
  15. package/dist-server/controllers/create-data-sample.js.map +1 -1
  16. package/dist-server/controllers/jasper-report.js +17 -12
  17. package/dist-server/controllers/jasper-report.js.map +1 -1
  18. package/dist-server/controllers/shiny-report.js +44 -0
  19. package/dist-server/controllers/shiny-report.js.map +1 -0
  20. package/dist-server/routes.js +8 -0
  21. package/dist-server/routes.js.map +1 -1
  22. package/dist-server/service/data-archive/data-archive-mutation.js +211 -0
  23. package/dist-server/service/data-archive/data-archive-mutation.js.map +1 -0
  24. package/dist-server/service/data-archive/data-archive-query.js +85 -0
  25. package/dist-server/service/data-archive/data-archive-query.js.map +1 -0
  26. package/dist-server/service/data-archive/data-archive-type.js +74 -0
  27. package/dist-server/service/data-archive/data-archive-type.js.map +1 -0
  28. package/dist-server/service/data-archive/data-archive.js +80 -0
  29. package/dist-server/service/data-archive/data-archive.js.map +1 -0
  30. package/dist-server/service/data-archive/index.js +9 -0
  31. package/dist-server/service/data-archive/index.js.map +1 -0
  32. package/dist-server/service/data-key-set/data-key-item-type.js.map +1 -1
  33. package/dist-server/service/data-key-set/data-key-set-mutation.js.map +1 -1
  34. package/dist-server/service/data-key-set/data-key-set-query.js.map +1 -1
  35. package/dist-server/service/data-key-set/data-key-set-type.js +17 -0
  36. package/dist-server/service/data-key-set/data-key-set-type.js.map +1 -1
  37. package/dist-server/service/data-key-set/data-key-set.js +11 -0
  38. package/dist-server/service/data-key-set/data-key-set.js.map +1 -1
  39. package/dist-server/service/data-ooc/data-ooc-mutation.js.map +1 -1
  40. package/dist-server/service/data-ooc/data-ooc-query.js.map +1 -1
  41. package/dist-server/service/data-ooc/data-ooc-subscription.js.map +1 -1
  42. package/dist-server/service/data-ooc/data-ooc-type.js.map +1 -1
  43. package/dist-server/service/data-ooc/data-ooc.js.map +1 -1
  44. package/dist-server/service/data-sample/data-sample-mutation.js.map +1 -1
  45. package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
  46. package/dist-server/service/data-sample/data-sample-type.js.map +1 -1
  47. package/dist-server/service/data-sample/data-sample.js.map +1 -1
  48. package/dist-server/service/data-sensor/data-sensor-mutation.js.map +1 -1
  49. package/dist-server/service/data-sensor/data-sensor-query.js.map +1 -1
  50. package/dist-server/service/data-sensor/data-sensor-type.js.map +1 -1
  51. package/dist-server/service/data-sensor/data-sensor.js.map +1 -1
  52. package/dist-server/service/data-set/data-item-type.js.map +1 -1
  53. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  54. package/dist-server/service/data-set/data-set-query.js.map +1 -1
  55. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  56. package/dist-server/service/data-set/data-set.js +2 -0
  57. package/dist-server/service/data-set/data-set.js.map +1 -1
  58. package/dist-server/service/data-set-history/data-set-history-query.js.map +1 -1
  59. package/dist-server/service/data-set-history/data-set-history-type.js.map +1 -1
  60. package/dist-server/service/data-set-history/data-set-history.js.map +1 -1
  61. package/dist-server/service/data-set-history/event-subscriber.js.map +1 -1
  62. package/dist-server/service/data-spec/data-spec-query.js.map +1 -1
  63. package/dist-server/service/data-spec/data-spec.js.map +1 -1
  64. package/dist-server/service/index.js +5 -2
  65. package/dist-server/service/index.js.map +1 -1
  66. package/dist-server/tsconfig.tsbuildinfo +1 -1
  67. package/helps/dataset/data-archive.md +6 -0
  68. package/package.json +7 -7
  69. package/server/controllers/create-data-sample.ts +4 -0
  70. package/server/controllers/jasper-report.ts +17 -12
  71. package/server/controllers/shiny-report.ts +63 -0
  72. package/server/routes.ts +11 -0
  73. package/server/service/data-archive/data-archive-mutation.ts +234 -0
  74. package/server/service/data-archive/data-archive-query.ts +56 -0
  75. package/server/service/data-archive/data-archive-type.ts +49 -0
  76. package/server/service/data-archive/data-archive.ts +69 -0
  77. package/server/service/data-archive/index.ts +6 -0
  78. package/server/service/data-key-set/data-key-set-type.ts +13 -0
  79. package/server/service/data-key-set/data-key-set.ts +9 -0
  80. package/server/service/data-set/data-set.ts +3 -1
  81. package/server/service/index.ts +5 -2
  82. package/things-factory.config.js +20 -12
  83. package/translations/en.json +15 -1
  84. package/translations/ko.json +14 -1
  85. package/translations/ms.json +2 -0
  86. package/translations/zh.json +2 -0
  87. package/dist-server/service/data-item/data-item-mutation.js +0 -69
  88. package/dist-server/service/data-item/data-item-mutation.js.map +0 -1
  89. package/dist-server/service/data-item/data-item-query.js +0 -100
  90. package/dist-server/service/data-item/data-item-query.js.map +0 -1
  91. package/dist-server/service/data-item/data-item-type.js +0 -80
  92. package/dist-server/service/data-item/data-item-type.js.map +0 -1
  93. package/dist-server/service/data-item/data-item.js +0 -136
  94. package/dist-server/service/data-item/data-item.js.map +0 -1
  95. package/dist-server/service/data-item/index.js +0 -9
  96. package/dist-server/service/data-item/index.js.map +0 -1
@@ -1,3 +1,7 @@
1
+ /**
2
+ * report page from 'data-set-list-page'
3
+ * datasetId is required
4
+ */
1
5
  import '@operato/data-grist'
2
6
  import '@things-factory/form-ui'
3
7
 
@@ -6,7 +10,7 @@ import { css, html } from 'lit'
6
10
  import { i18next, localize } from '@operato/i18n'
7
11
  import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
8
12
  import { PageView } from '@things-factory/shell'
9
- import { encodeFormParams, getCookie } from '@things-factory/utils'
13
+ import { encodeUrlParams, getCookie } from '@things-factory/utils'
10
14
 
11
15
  class DataReportEmbedPage extends localize(i18next)(PageView) {
12
16
  static get properties() {
@@ -57,7 +61,7 @@ class DataReportEmbedPage extends localize(i18next)(PageView) {
57
61
  }
58
62
  ]
59
63
  return {
60
- title: 'jasper-report',
64
+ title: 'data-report-embed',
61
65
  actions,
62
66
  filters
63
67
  }
@@ -68,7 +72,7 @@ class DataReportEmbedPage extends localize(i18next)(PageView) {
68
72
  .filters=${this.context.filters}
69
73
  @filters-change=${e => {
70
74
  console.log('filters changed', e.detail)
71
- this._reportTemplate(e.detail.filters)
75
+ this.reportTemplate(e.detail.filters)
72
76
  }}
73
77
  ?url-params-sensitive=${this.active}
74
78
  ></ox-filters-form-base>
@@ -82,12 +86,12 @@ class DataReportEmbedPage extends localize(i18next)(PageView) {
82
86
  }
83
87
  }
84
88
 
85
- async _reportTemplate(filters) {
89
+ async reportTemplate(filters) {
86
90
  // convert filters array to object
87
91
  const params = filters.reduce((acc, curr) => ((acc[curr.name] = curr.value), acc), {})
88
92
  Object.assign(params, this.lifecycle.params)
89
93
 
90
- const { workDateRange, workShift, reportView } = params
94
+ const { id, workDateRange, workShift, reportType, reportView } = params
91
95
 
92
96
  /** ignoring date conditions */
93
97
  if (!workDateRange[0] || !workDateRange[1] || workDateRange[0] > workDateRange[1]) {
@@ -97,16 +101,16 @@ class DataReportEmbedPage extends localize(i18next)(PageView) {
97
101
  /** urlencoded params including test values */
98
102
  const urlParams = {
99
103
  table: 'samples',
100
- dataSetId: this.lifecycle.resourceId,
101
- fromWorkDate: workDateRange[0],
102
- toWorkDate: workDateRange[1],
103
- token: getCookie('access_token'),
104
- workShift
104
+ dataSetId: id,
105
+ startDate: workDateRange[0],
106
+ endDate: workDateRange[1],
107
+ workShift,
108
+ reportView
105
109
  }
106
110
 
107
- const encodedUrlParams = encodeFormParams(urlParams)
111
+ const encodedUrlParams = encodeUrlParams(urlParams)
108
112
 
109
- this.shadowRoot.querySelector('#container').src = `${reportView}?params=${encodedUrlParams}`
113
+ this.shadowRoot.querySelector('#container').src = `/data-report/${reportType}/?${encodedUrlParams}`
110
114
  }
111
115
  }
112
116
 
@@ -13,6 +13,7 @@ import { openPopup } from '@operato/layout'
13
13
  import { navigate, PageView, store } from '@operato/shell'
14
14
  import { CommonGristStyles, ScrollbarStyles } from '@operato/styles'
15
15
  import { provider } from '@things-factory/board-ui'
16
+ import { encodeUrlParams } from '@things-factory/utils'
16
17
 
17
18
  const USECASE_OPTIONS = () => {
18
19
  return ['', ...OxDataUseCase.getUseCaseNames()].map(name => {
@@ -72,7 +73,8 @@ const showMonitorView = (columns, data, column, record, rowIndex) => {
72
73
  }
73
74
 
74
75
  const showReportView = (columns, data, column, record, rowIndex) => {
75
- const { id, name, reportType, reportView, reportTemplate } = record
76
+ const { id:datasetId, dataKeySet, name, reportType, reportView, reportTemplate } = record
77
+ const { id:dataKeySetId } = dataKeySet || { id: '' }
76
78
  const title = `${name} - ${i18next.t('title.data-report-view')}`
77
79
 
78
80
  switch (reportType) {
@@ -85,26 +87,23 @@ const showReportView = (columns, data, column, record, rowIndex) => {
85
87
  break
86
88
 
87
89
  case 'embed':
88
- const board = {
89
- id: reportView
90
- }
91
- navigate(`/data-report-embed/${id}?reportView=${encodeURIComponent(reportView)}`)
92
- // openPopup(html` <div style="background-color: white;">Under construction</div> `, {
93
- // closable: true,
94
- // backdrop: true,
95
- // size: 'large',
96
- // title
97
- // })
98
-
99
90
  break
100
91
 
101
92
  case 'page':
102
- navigate(reportView + `/${id}?template=${encodeURIComponent(reportTemplate)}`)
103
93
  break
104
94
 
105
95
  case 'external':
106
96
  window.open(reportView, '_blank')
107
97
  break
98
+
99
+ case 'jasper':
100
+ case 'shiny':
101
+ const encodedUrlParams = encodeUrlParams({ datasetId, dataKeySetId, reportType, reportView, reportTemplate })
102
+ navigate(`/data-report-samples/?${encodedUrlParams}`)
103
+ break
104
+
105
+ default:
106
+ break
108
107
  }
109
108
  }
110
109
 
@@ -366,6 +365,10 @@ export class DataReportListPage extends connect(store)(localize(i18next)(PageVie
366
365
  responses: dataSetsForReport(filters: $filters, pagination: $pagination, sortings: $sortings) {
367
366
  items {
368
367
  id
368
+ dataKeySet {
369
+ id
370
+ name
371
+ }
369
372
  name
370
373
  description
371
374
  partitionKeys
@@ -0,0 +1,186 @@
1
+ /**
2
+ * data sample report page from 'data-key-set-list-page'
3
+ * dateKeySetId is required
4
+ */
5
+ import '@operato/data-grist'
6
+
7
+ import gql from 'graphql-tag'
8
+ import { css, html } from 'lit'
9
+ import { connect } from 'pwa-helpers/connect-mixin'
10
+
11
+ import { client } from '@operato/graphql'
12
+ import { i18next, localize } from '@operato/i18n'
13
+ import { PageView, store } from '@operato/shell'
14
+ import { ScrollbarStyles } from '@operato/styles'
15
+
16
+ import { encodeUrlParams } from '@things-factory/utils'
17
+
18
+ export class DataReportSamplesPage extends connect(store)(localize(i18next)(PageView)) {
19
+ static get properties() {
20
+ return {
21
+ dataKeySetId: String,
22
+ dataKeySet: Object,
23
+ active: String
24
+ }
25
+ }
26
+
27
+ static get styles() {
28
+ return [
29
+ ScrollbarStyles,
30
+ css`
31
+ :host {
32
+ display: flex;
33
+ flex-direction: column;
34
+ padding: 0;
35
+ }
36
+
37
+ #container {
38
+ flex: 1;
39
+ padding: 0;
40
+ margin: 0;
41
+ border: 0;
42
+ }
43
+ `
44
+ ]
45
+ }
46
+
47
+ get context() {
48
+
49
+ const today = new Date().toISOString().split('T')[0]
50
+
51
+ const filters = [
52
+ {
53
+ name: 'workDateRange',
54
+ type: 'date',
55
+ label: i18next.t('field.work-date'),
56
+ operator: 'between',
57
+ value: [today, today] // not working...
58
+ },
59
+ ...this.getDataKeyFilters()
60
+ ]
61
+
62
+ return {
63
+ title: i18next.t('title.data-report samples'),
64
+ help: 'dataset/data-report-samples',
65
+ filters
66
+ }
67
+ }
68
+
69
+ getDataKeyFilters() {
70
+ return (
71
+ this.dataKeySet?.dataKeyItems.map((item, index) => {
72
+ return {
73
+ type: 'string',
74
+ name: `key_0${index + 1}`,
75
+ label: i18next.t(item.tKey),
76
+ operator: 'i_like',
77
+ width: 120
78
+ }
79
+ }) || []
80
+ )
81
+ }
82
+
83
+ render() {
84
+ // clear if pre-loaded
85
+ const container = this.shadowRoot.querySelector('#container')
86
+ if (container) {
87
+ container.src = ""
88
+ // @TODO: Clear filters
89
+ /** */
90
+ }
91
+
92
+ return html`
93
+ <ox-filters-form-base
94
+ .filters=${this.context.filters}
95
+ @filters-change=${e => {
96
+ console.log('filters changed', e.detail)
97
+ this.renderTemplate(e.detail.filters)
98
+ }}
99
+ ?url-params-sensitive=${this.active}
100
+ ></ox-filters-form-base>
101
+ <iframe id="container"></iframe>
102
+ `
103
+ }
104
+
105
+ renderTemplate(filters) {
106
+ const params = filters.reduce((acc, curr) => ((acc[curr.name] = curr.value), acc), {})
107
+ Object.assign(params, this.lifecycle.params)
108
+
109
+ const filterNames = this.getDataKeyFilters().map(x => x.name)
110
+ const dataKeyFilters = filters.filter(x => filterNames.includes(x.name))
111
+ const { workDateRange } = params
112
+
113
+ /** ignoring date conditions */
114
+ if (!workDateRange[0] || !workDateRange[1] || workDateRange[0] > workDateRange[1]) {
115
+ return
116
+ }
117
+
118
+ /** urlencoded params including test values */
119
+ const urlParams = {
120
+ reportType: params.reportType,
121
+ reportView: params.reportView,
122
+ reportTemplate: params.reportTemplate,
123
+ table: 'samples',
124
+ datasetId: params.datasetId,
125
+ dataKeySetId: params.dataKeySetId,
126
+ startDate: workDateRange[0],
127
+ endDate: workDateRange[1],
128
+ dataKeySetItems: JSON.stringify(this.dataKeySet?.dataKeyItems || []),
129
+ dataKeyFilters: JSON.stringify(dataKeyFilters)
130
+ }
131
+
132
+ const encodedUrlParams = encodeUrlParams(urlParams)
133
+
134
+ const reportUrl = `/data-report/${urlParams.reportType}/`
135
+ this.shadowRoot.querySelector('#container').src = `${reportUrl}?${encodedUrlParams}`
136
+ }
137
+
138
+ pageUpdated(changes, lifecycle) {
139
+ if (this.active) {
140
+ const { dataKeySetId } = lifecycle.params
141
+ this.dataKeySetId = dataKeySetId
142
+ return
143
+ }
144
+ }
145
+
146
+ async fetchHandler({ page, limit, sortings = [], filters = [] }) {
147
+
148
+ }
149
+
150
+ async updated(changes) {
151
+ if (changes.has('dataKeySetId')) {
152
+ if (!this.dataKeySetId) {
153
+ this.dataKeySet = null
154
+ return
155
+ }
156
+
157
+ const response = await client.query({
158
+ query: gql`
159
+ query ($id: String!) {
160
+ dataKeySet(id: $id) {
161
+ id
162
+ name
163
+ description
164
+ reportType
165
+ reportView
166
+ dataKeyItems {
167
+ name
168
+ description
169
+ dataKey
170
+ tKey
171
+ }
172
+ }
173
+ }
174
+ `,
175
+ variables: {
176
+ id: this.dataKeySetId
177
+ }
178
+ })
179
+
180
+ this.dataKeySet = response.data.dataKeySet
181
+ }
182
+ }
183
+
184
+ }
185
+
186
+ window.customElements.define('data-report-samples-page', DataReportSamplesPage)
@@ -86,6 +86,14 @@ const REPORT_TYPES = [
86
86
  {
87
87
  display: 'External URL',
88
88
  value: 'external'
89
+ },
90
+ {
91
+ display: 'Jasper',
92
+ value: 'jasper'
93
+ },
94
+ {
95
+ display: 'Shiny',
96
+ value: 'shiny'
89
97
  }
90
98
  ]
91
99
 
package/client/route.js CHANGED
@@ -16,6 +16,10 @@ export default function route(page) {
16
16
  import('./pages/data-sample/data-sample-search-page.js')
17
17
  return page
18
18
 
19
+ case 'data-report-samples':
20
+ import('./pages/data-report/data-report-samples-page.js')
21
+ return page
22
+
19
23
  case 'data-ooc-list':
20
24
  import('./pages/data-ooc/data-ooc-list-page.js')
21
25
  return page
@@ -27,22 +31,26 @@ export default function route(page) {
27
31
  case 'data-report-list':
28
32
  import('./pages/data-report/data-report-list-page.js')
29
33
  return page
30
-
31
- case 'jasper-report-samples':
32
- import('./pages/data-report/jasper-report-samples-page.js')
34
+
35
+ case 'data-archive-list':
36
+ import('./pages/data-archive/data-archive-list-page.js')
33
37
  return page
34
38
 
35
- case 'jasper-report-samples-crosstab':
36
- import('./pages/data-report/jasper-report-samples-crosstab-page.js')
37
- return page
39
+ // case 'jasper-report-samples':
40
+ // import('./pages/data-report/jasper-report-samples-page.js')
41
+ // return page
38
42
 
39
- case 'jasper-report-oocs':
40
- import('./pages/data-report/jasper-report-oocs-page.js')
41
- return page
43
+ // case 'jasper-report-samples-crosstab':
44
+ // import('./pages/data-report/jasper-report-samples-crosstab-page.js')
45
+ // return page
42
46
 
43
- case 'data-report-embed':
44
- import('./pages/data-report/data-report-embed-page')
45
- return page
47
+ // case 'jasper-report-oocs':
48
+ // import('./pages/data-report/jasper-report-oocs-page.js')
49
+ // return page
50
+
51
+ // case 'data-report-embed':
52
+ // import('./pages/data-report/data-report-embed-page')
53
+ // return page
46
54
 
47
55
  case 'data-key-set-list':
48
56
  import('./pages/data-key-set/data-key-set-list-page.js')
@@ -1,10 +1,31 @@
1
1
  module.exports = {
2
+ dataArchive: {
3
+ dataset: {
4
+ endpoint: {
5
+ host: 'localhost',
6
+ port: 80
7
+ },
8
+ datasource: {
9
+ database: ''
10
+ }
11
+ }
12
+ },
2
13
  dataReport: {
3
14
  jasper: {
4
15
  endpoint: {
5
16
  host: 'localhost',
6
17
  port: 8090
7
18
  },
19
+ datasource: {
20
+ database: ''
21
+ },
22
+ method: 'POST'
23
+ },
24
+ shiny: {
25
+ endpoint: {
26
+ host: 'localhost',
27
+ port: 3838
28
+ },
8
29
  datasource: {
9
30
  database: ''
10
31
  }
@@ -1,13 +1,34 @@
1
1
  module.exports = {
2
- dataReport: {
3
- jasper: {
4
- endpoint: {
5
- host: 'localhost',
6
- port: 8090
7
- },
8
- datasource: {
9
- database: ''
10
- }
11
- }
12
- }
2
+ dataArchive: {
3
+ dataset: {
4
+ endpoint: {
5
+ host: 'localhost',
6
+ port: 80
7
+ },
8
+ datasource: {
9
+ database: ''
10
+ }
11
+ }
12
+ },
13
+ dataReport: {
14
+ jasper: {
15
+ endpoint: {
16
+ host: 'localhost',
17
+ port: 8090
18
+ },
19
+ datasource: {
20
+ database: ''
21
+ },
22
+ method: 'POST'
23
+ },
24
+ shiny: {
25
+ endpoint: {
26
+ host: 'localhost',
27
+ port: 3838
28
+ },
29
+ datasource: {
30
+ database: ''
31
+ }
32
+ }
33
+ }
13
34
  }
@@ -9,6 +9,8 @@ const data_ooc_1 = require("../service/data-ooc/data-ooc");
9
9
  const data_sample_1 = require("../service/data-sample/data-sample");
10
10
  const data_set_1 = require("../service/data-set/data-set");
11
11
  const data_use_case_1 = require("./data-use-case");
12
+ // See README.md at ## Data Samples
13
+ process.env.TZ = 'UTC';
12
14
  const fillDataKeys = (dataKeySet, data) => {
13
15
  const keys = (dataKeySet === null || dataKeySet === void 0 ? void 0 : dataKeySet.dataKeyItems) || [];
14
16
  return keys.reduce((sum, key, index) => {
@@ -68,7 +70,7 @@ async function createDataSample(dataSample, context) {
68
70
  partitionKeys = replaceVariables(partitionKeys, dataSample.data);
69
71
  const dataKeys = fillDataKeys(dataSet === null || dataSet === void 0 ? void 0 : dataSet.dataKeySet, dataSample.data);
70
72
  const { ooc, oos, judgment } = data_use_case_1.DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {};
71
- const result = await tx.getRepository(data_sample_1.DataSample).save(Object.assign(Object.assign(Object.assign({ name: dataSet.name, description: dataSet.description, useCase: dataSet.useCase }, dataSample), dataKeys), { dataSetVersion: dataSet.version, domain,
73
+ const result = await tx.getRepository(data_sample_1.DataSample).save(Object.assign(Object.assign(Object.assign({ name: dataSet.name, description: dataSet.description, useCase: dataSet.useCase, type: dataSet.type }, dataSample), dataKeys), { dataSetVersion: dataSet.version, domain,
72
74
  partitionKeys,
73
75
  ooc,
74
76
  oos,
@@ -1 +1 @@
1
- {"version":3,"file":"create-data-sample.js","sourceRoot":"","sources":["../../server/controllers/create-data-sample.ts"],"names":[],"mappings":"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","sourcesContent":["import moment from 'moment-timezone'\nimport { EntityManager } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain, getRedirectSubdomainPath, pubsub } from '@things-factory/shell'\nimport { getWorkDateAndShift } from '@things-factory/work-shift'\n\nimport { DataOoc, DataOocStatus } from '../service/data-ooc/data-ooc'\nimport { DataSample } from '../service/data-sample/data-sample'\nimport { NewDataSample } from '../service/data-sample/data-sample-type'\nimport { DataSet } from '../service/data-set/data-set'\nimport { DataUseCase } from './data-use-case'\n\nconst fillDataKeys = (dataKeySet, data) => {\n const keys = dataKeySet?.dataKeyItems || []\n return keys.reduce((sum, key, index) => {\n const value = data[key.dataKey]\n if (value != null) {\n sum[`key0${index + 1}`] = value instanceof Array ? value[0] : value\n }\n return sum\n }, {})\n}\n\n// parse variable javascript string pattern\nconst replaceVariables = (keys, dic) => {\n for (const k in keys) {\n const matches = keys[k].match(/\\$\\{\\w*\\}/g)\n matches &&\n matches.forEach(m => {\n keys[k] = keys[k].replace(m, dic[m.slice(2, -1)])\n })\n }\n return keys\n}\n\n// It is required UTC date for Partitioning File System like AWS S3 from Athena.\n// ex) %YYYY, %MM, %DD\nconst formatDate = (keys, _moment) => {\n for (const k in keys) {\n const matches = keys[k].match(/%\\w*/g)\n matches &&\n matches.forEach(m => {\n keys[k] = keys[k].replace(m, _moment.format(m.substr(1)))\n })\n }\n return keys\n}\n\nexport async function createDataSample(\n dataSample: NewDataSample,\n context: { state: { domain: Domain; user: User; tx: EntityManager } }\n): Promise<DataSample> {\n const { domain, user, tx } = context.state\n\n const dataSet = await tx.getRepository(DataSet).findOne({\n where: { id: dataSample.dataSet.id },\n relations: ['dataKeySet']\n })\n\n const dataItems = dataSet.dataItems\n const collectedAt = dataSample.collectedAt || new Date()\n\n const timezone = dataSet.timezone || domain.timezone || 'UTC'\n const format = 'YYYY-MM-DD'\n\n // workDate ex) 2022-04-04\n const { workDate, workShift } = await getWorkDateAndShift(domain, collectedAt, { timezone, format })\n\n // local time dataSet timezone or domain timezone or default 'UTC'\n\n const localDateTz = moment(collectedAt).tz(timezone)\n const defaultPartitionKeys = {\n domain: domain.subdomain,\n datasetid: dataSample.dataSet.id /* It should not be 'data_set_id' as column name duplicated for Glue */,\n date: localDateTz.format(format) /* local time date */,\n workdate: workDate /* working date */,\n workshift: workShift\n }\n\n var partitionKeys = {\n ...defaultPartitionKeys,\n ...dataSet.partitionKeys\n }\n\n partitionKeys = formatDate(partitionKeys, localDateTz)\n partitionKeys = replaceVariables(partitionKeys, dataSample.data)\n\n const dataKeys = fillDataKeys(dataSet?.dataKeySet, dataSample.data)\n\n const { ooc, oos, judgment } = DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {}\n const result = await tx.getRepository(DataSample).save({\n name: dataSet.name,\n description: dataSet.description,\n useCase: dataSet.useCase,\n ...dataSample,\n ...dataKeys,\n dataSetVersion: dataSet.version,\n domain,\n partitionKeys,\n ooc,\n oos,\n judgment,\n collectedAt,\n workDate,\n workShift,\n creator: user,\n updater: user\n })\n\n if (ooc || oos) {\n const dataOoc = await tx.getRepository(DataOoc).save({\n ...result,\n history: [\n {\n user: {\n id: user.id,\n name: user.name\n },\n state: DataOocStatus.CREATED,\n timestamp: Date.now()\n }\n ],\n state: DataOocStatus.CREATED\n })\n\n pubsub.publish('data-ooc', {\n dataOoc,\n supervisoryRoleId: dataSet.supervisoryRoleId\n })\n\n pubsub.publish('notification', {\n notification: {\n domain,\n type: 'error',\n title: `Data OOC occurred on '${dataSet.name}'`,\n body: `Data OOC occurred on '${dataSet.name}'`,\n url: getRedirectSubdomainPath(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),\n timestamp: collectedAt\n }\n })\n }\n\n return result\n}\n"]}
1
+ {"version":3,"file":"create-data-sample.js","sourceRoot":"","sources":["../../server/controllers/create-data-sample.ts"],"names":[],"mappings":"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","sourcesContent":["import moment from 'moment-timezone'\nimport { EntityManager } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain, getRedirectSubdomainPath, pubsub } from '@things-factory/shell'\nimport { getWorkDateAndShift } from '@things-factory/work-shift'\n\nimport { DataOoc, DataOocStatus } from '../service/data-ooc/data-ooc'\nimport { DataSample } from '../service/data-sample/data-sample'\nimport { NewDataSample } from '../service/data-sample/data-sample-type'\nimport { DataSet } from '../service/data-set/data-set'\nimport { DataUseCase } from './data-use-case'\n\n// See README.md at ## Data Samples\nprocess.env.TZ = 'UTC'\n\nconst fillDataKeys = (dataKeySet, data) => {\n const keys = dataKeySet?.dataKeyItems || []\n return keys.reduce((sum, key, index) => {\n const value = data[key.dataKey]\n if (value != null) {\n sum[`key0${index + 1}`] = value instanceof Array ? value[0] : value\n }\n return sum\n }, {})\n}\n\n// parse variable javascript string pattern\nconst replaceVariables = (keys, dic) => {\n for (const k in keys) {\n const matches = keys[k].match(/\\$\\{\\w*\\}/g)\n matches &&\n matches.forEach(m => {\n keys[k] = keys[k].replace(m, dic[m.slice(2, -1)])\n })\n }\n return keys\n}\n\n// It is required UTC date for Partitioning File System like AWS S3 from Athena.\n// ex) %YYYY, %MM, %DD\nconst formatDate = (keys, _moment) => {\n for (const k in keys) {\n const matches = keys[k].match(/%\\w*/g)\n matches &&\n matches.forEach(m => {\n keys[k] = keys[k].replace(m, _moment.format(m.substr(1)))\n })\n }\n return keys\n}\n\nexport async function createDataSample(\n dataSample: NewDataSample,\n context: { state: { domain: Domain; user: User; tx: EntityManager } }\n): Promise<DataSample> {\n const { domain, user, tx } = context.state\n\n const dataSet = await tx.getRepository(DataSet).findOne({\n where: { id: dataSample.dataSet.id },\n relations: ['dataKeySet']\n })\n\n const dataItems = dataSet.dataItems\n const collectedAt = dataSample.collectedAt || new Date()\n\n const timezone = dataSet.timezone || domain.timezone || 'UTC'\n const format = 'YYYY-MM-DD'\n\n // workDate ex) 2022-04-04\n const { workDate, workShift } = await getWorkDateAndShift(domain, collectedAt, { timezone, format })\n\n // local time dataSet timezone or domain timezone or default 'UTC'\n\n const localDateTz = moment(collectedAt).tz(timezone)\n const defaultPartitionKeys = {\n domain: domain.subdomain,\n datasetid: dataSample.dataSet.id /* It should not be 'data_set_id' as column name duplicated for Glue */,\n date: localDateTz.format(format) /* local time date */,\n workdate: workDate /* working date */,\n workshift: workShift\n }\n\n var partitionKeys = {\n ...defaultPartitionKeys,\n ...dataSet.partitionKeys\n }\n\n partitionKeys = formatDate(partitionKeys, localDateTz)\n partitionKeys = replaceVariables(partitionKeys, dataSample.data)\n\n const dataKeys = fillDataKeys(dataSet?.dataKeySet, dataSample.data)\n\n const { ooc, oos, judgment } = DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {}\n const result = await tx.getRepository(DataSample).save({\n name: dataSet.name,\n description: dataSet.description,\n useCase: dataSet.useCase,\n type: dataSet.type,\n ...dataSample,\n ...dataKeys,\n dataSetVersion: dataSet.version,\n domain,\n partitionKeys,\n ooc,\n oos,\n judgment,\n collectedAt,\n workDate,\n workShift,\n creator: user,\n updater: user\n })\n\n if (ooc || oos) {\n const dataOoc = await tx.getRepository(DataOoc).save({\n ...result,\n history: [\n {\n user: {\n id: user.id,\n name: user.name\n },\n state: DataOocStatus.CREATED,\n timestamp: Date.now()\n }\n ],\n state: DataOocStatus.CREATED\n })\n\n pubsub.publish('data-ooc', {\n dataOoc,\n supervisoryRoleId: dataSet.supervisoryRoleId\n })\n\n pubsub.publish('notification', {\n notification: {\n domain,\n type: 'error',\n title: `Data OOC occurred on '${dataSet.name}'`,\n body: `Data OOC occurred on '${dataSet.name}'`,\n url: getRedirectSubdomainPath(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),\n timestamp: collectedAt\n }\n })\n }\n\n return result\n}\n"]}
@@ -96,20 +96,23 @@ function parseJsonDataField(rows) {
96
96
  }
97
97
  const athenaClient = new aws_base_1.AthenaController();
98
98
  async function queryAthena(params) {
99
- const { table, domain, dataSetId, fromWorkDate, toWorkDate, workShift, timezone } = params;
99
+ const { table, domain, datasetId, startDate, endDate, workShift, timezone } = params;
100
100
  const queryData = {
101
- sql: `SELECT name, description, data, spec, workdate, workshift,
101
+ sql: `SELECT ds.name, ds.description, ds.data, dsh.data_items as spec, ds.workdate, ds.workshift,
102
102
  DATE_FORMAT(
103
103
  FROM_UNIXTIME(collected_at / 1000 / 1000) AT TIME ZONE '${timezone || 'UTC'}',
104
104
  '%Y-%m-%d %H:%i:%s'
105
- ) AS collected_at
106
- FROM ${table}
107
- WHERE domain='${domain}'
108
- AND datasetid = '${dataSetId}'
109
- AND workdate >= '${fromWorkDate}'
110
- AND workdate <= '${toWorkDate}'
111
- ${workShift ? "AND workshift = '" + workShift + "'" : ''}
112
- ORDER BY collected_at`,
105
+ ) AS dscollected_at
106
+ FROM ${table} ds
107
+ JOIN data_set_histories dsh
108
+ ON (ds.datasetid = dsh.original_id
109
+ and ds.data_set_version = dsh.version)
110
+ WHERE ds.domain='${domain}'
111
+ AND ds.datasetid = '${datasetId}'
112
+ AND ds.workdate >= '${startDate}'
113
+ AND ds.workdate <= '${endDate}'
114
+ ${workShift ? "AND ds.workshift = '" + workShift + "'" : ''}
115
+ ORDER BY ds.collected_at`,
113
116
  db: DATABASE
114
117
  };
115
118
  // and json_extract_scalar(data, '$.dauid') = 'A8032AD81730'
@@ -117,7 +120,7 @@ async function queryAthena(params) {
117
120
  }
118
121
  async function renderJasperReport(context) {
119
122
  const { state: { domain }, query } = context;
120
- const template = await attachment_base_1.STORAGE.readFile(query['template'] || 'dynamic_header_sample.jrxml', 'utf-8');
123
+ const template = await attachment_base_1.STORAGE.readFile(query['reportTemplate'] || 'dynamic_header_sample.jrxml', 'utf-8');
121
124
  let templateType = query['templateType'] || 'crosstab';
122
125
  let parsedData = [];
123
126
  // @todo: get dataset timezone
@@ -147,7 +150,9 @@ async function renderJasperReport(context) {
147
150
  formData.append('template', template);
148
151
  formData.append('jsonString', JSON.stringify(parsedData));
149
152
  formData.append('parameters', JSON.stringify(parameters));
150
- const reportUrl = `${PROTOCOL || 'http'}://${HOST}:${PORT}/rest/report/show_html`;
153
+ const { reportView } = query;
154
+ const subpath = reportView ? reportView.split('/').filter(x => x).join('/') : '';
155
+ const reportUrl = `${PROTOCOL || 'http'}://${HOST}:${PORT}/${subpath}`;
151
156
  const response = await (0, node_fetch_1.default)(reportUrl, {
152
157
  method: 'POST',
153
158
  body: formData
@@ -1 +1 @@
1
- {"version":3,"file":"jasper-report.js","sourceRoot":"","sources":["../../server/controllers/jasper-report.ts"],"names":[],"mappings":"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","sourcesContent":["import FormData from 'form-data'\nimport fetch from 'node-fetch'\n\nimport { STORAGE } from '@things-factory/attachment-base'\nimport { AthenaController } from '@things-factory/aws-base'\nimport { config } from '@things-factory/env'\n\nconst dataReportConfig = config.get('dataReport')\nconst {\n jasper: {\n endpoint: { protocol: PROTOCOL, host: HOST, port: PORT },\n datasource: { database: DATABASE }\n }\n} = dataReportConfig || {\n jasper: {\n endpoint: {},\n datasource: {}\n }\n}\n\nfunction transformValuesToRows(queryResult) {\n var parseData = []\n let index = 1\n for (let i = 0; i < queryResult.Items.length; i++) {\n var j = 0\n const data = JSON.parse(queryResult.Items[i].data)\n const spec = JSON.parse(queryResult.Items[i].spec)\n\n for (let key in data) {\n if (Array.isArray(data[key])) {\n for (j = 0; j < data[key].length; j++) {\n for (let specKey in spec) {\n if (key === specKey) {\n parseData.push({\n item: spec[specKey].name,\n index: index + j,\n value: String(data[key][j])\n })\n }\n }\n }\n } else {\n parseData.push({\n item: key,\n index,\n value: String(data[key])\n })\n }\n }\n if (j !== 0) {\n index = index + j\n } else {\n index = index + 1\n }\n }\n}\n\n/** @todo considering trasformation in lambda, as massive dataset */\nfunction pivotData(rows) {\n let parsedData = []\n let index = 1\n for (let i = 0; i < rows.length; i++) {\n let j = 0\n const data = JSON.parse(rows[i].data)\n const spec = JSON.parse(rows[i].spec)\n\n for (let key in data) {\n /** @todo rule to display or not, about unspecified spec */\n const value = data[key]\n !spec[key]?.hidden &&\n parsedData.push({\n item: spec[key]?.name || key,\n index,\n value: Array.isArray(value) ? value.join(', ') : value\n })\n }\n if (j !== 0) {\n index = index + j\n } else {\n index = index + 1\n }\n }\n\n return parsedData\n}\n\nfunction parseJsonDataField(rows) {\n let parsedData = []\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i]\n const data = JSON.parse(row.data)\n for (let key in data) {\n if (Array.isArray(data[key])) {\n data[key] = data[key].toString()\n }\n }\n delete row.data\n parsedData.push({ ...row, ...data })\n }\n\n return parsedData\n}\n\nconst athenaClient = new AthenaController()\n\nasync function queryAthena(params) {\n const { table, domain, dataSetId, fromWorkDate, toWorkDate, workShift, timezone } = params\n const queryData = {\n sql: `SELECT name, description, data, spec, workdate, workshift, \n DATE_FORMAT(\n FROM_UNIXTIME(collected_at / 1000 / 1000) AT TIME ZONE '${timezone || 'UTC'}', \n '%Y-%m-%d %H:%i:%s'\n ) AS collected_at\n FROM ${table} \n WHERE domain='${domain}' \n AND datasetid = '${dataSetId}'\n AND workdate >= '${fromWorkDate}'\n AND workdate <= '${toWorkDate}'\n ${workShift ? \"AND workshift = '\" + workShift + \"'\" : ''}\n ORDER BY collected_at`,\n db: DATABASE\n }\n // and json_extract_scalar(data, '$.dauid') = 'A8032AD81730'\n\n return await athenaClient.query(queryData)\n}\n\nexport async function renderJasperReport(context: any) {\n const {\n state: { domain },\n query\n } = context\n\n const template = await STORAGE.readFile(query['template'] || 'dynamic_header_sample.jrxml', 'utf-8')\n let templateType = query['templateType'] || 'crosstab'\n let parsedData = []\n\n // @todo: get dataset timezone\n /**\n * const variables = await gql(dataSet(id:${dataSetId}) {\n * name, description, partition_keys, timezone\n * })\n */\n\n query['domain'] = domain?.subdomain\n query['timezone'] = domain?.timezone\n const queryResult = await queryAthena(query)\n const rows = queryResult.Items\n\n if (!rows.length) {\n return '<h3>Not found result.</h3>'\n } else {\n const firstRow = rows[0]\n // uses the first row values as data-set has no history data.\n const parameters = {\n name: firstRow.name,\n description: firstRow.description,\n ...query\n }\n\n if (templateType === 'crosstab') {\n parsedData = pivotData(rows)\n } else {\n parsedData = parseJsonDataField(rows)\n }\n\n const formData = new FormData()\n formData.append('template', template)\n formData.append('jsonString', JSON.stringify(parsedData))\n formData.append('parameters', JSON.stringify(parameters))\n\n const reportUrl = `${PROTOCOL || 'http'}://${HOST}:${PORT}/rest/report/show_html`\n const response = await fetch(reportUrl, {\n method: 'POST',\n body: formData\n })\n\n return await response.text()\n }\n}\n"]}
1
+ {"version":3,"file":"jasper-report.js","sourceRoot":"","sources":["../../server/controllers/jasper-report.ts"],"names":[],"mappings":"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","sourcesContent":["import FormData from 'form-data'\nimport fetch from 'node-fetch'\n\nimport { STORAGE } from '@things-factory/attachment-base'\nimport { AthenaController } from '@things-factory/aws-base'\nimport { config } from '@things-factory/env'\n\nconst dataReportConfig = config.get('dataReport')\nconst {\n jasper: {\n endpoint: { protocol: PROTOCOL, host: HOST, port: PORT },\n datasource: { database: DATABASE }\n }\n} = dataReportConfig || {\n jasper: {\n endpoint: {},\n datasource: {}\n }\n}\n\nfunction transformValuesToRows(queryResult) {\n var parseData = []\n let index = 1\n for (let i = 0; i < queryResult.Items.length; i++) {\n var j = 0\n const data = JSON.parse(queryResult.Items[i].data)\n const spec = JSON.parse(queryResult.Items[i].spec)\n\n for (let key in data) {\n if (Array.isArray(data[key])) {\n for (j = 0; j < data[key].length; j++) {\n for (let specKey in spec) {\n if (key === specKey) {\n parseData.push({\n item: spec[specKey].name,\n index: index + j,\n value: String(data[key][j])\n })\n }\n }\n }\n } else {\n parseData.push({\n item: key,\n index,\n value: String(data[key])\n })\n }\n }\n if (j !== 0) {\n index = index + j\n } else {\n index = index + 1\n }\n }\n}\n\n/** @todo considering trasformation in lambda, as massive dataset */\nfunction pivotData(rows) {\n let parsedData = []\n let index = 1\n for (let i = 0; i < rows.length; i++) {\n let j = 0\n const data = JSON.parse(rows[i].data)\n const spec = JSON.parse(rows[i].spec)\n\n for (let key in data) {\n /** @todo rule to display or not, about unspecified spec */\n const value = data[key]\n !spec[key]?.hidden &&\n parsedData.push({\n item: spec[key]?.name || key,\n index,\n value: Array.isArray(value) ? value.join(', ') : value\n })\n }\n if (j !== 0) {\n index = index + j\n } else {\n index = index + 1\n }\n }\n\n return parsedData\n}\n\nfunction parseJsonDataField(rows) {\n let parsedData = []\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i]\n const data = JSON.parse(row.data)\n for (let key in data) {\n if (Array.isArray(data[key])) {\n data[key] = data[key].toString()\n }\n }\n delete row.data\n parsedData.push({ ...row, ...data })\n }\n\n return parsedData\n}\n\nconst athenaClient = new AthenaController()\n\nasync function queryAthena(params) {\n const { table, domain, datasetId, startDate, endDate, workShift, timezone } = params\n const queryData = {\n sql: `SELECT ds.name, ds.description, ds.data, dsh.data_items as spec, ds.workdate, ds.workshift,\n DATE_FORMAT(\n FROM_UNIXTIME(collected_at / 1000 / 1000) AT TIME ZONE '${timezone || 'UTC'}', \n '%Y-%m-%d %H:%i:%s'\n ) AS dscollected_at\n FROM ${table} ds\n JOIN data_set_histories dsh \n ON (ds.datasetid = dsh.original_id\n and ds.data_set_version = dsh.version)\n WHERE ds.domain='${domain}' \n AND ds.datasetid = '${datasetId}'\n AND ds.workdate >= '${startDate}'\n AND ds.workdate <= '${endDate}'\n ${workShift ? \"AND ds.workshift = '\" + workShift + \"'\" : ''}\n ORDER BY ds.collected_at`,\n db: DATABASE\n }\n // and json_extract_scalar(data, '$.dauid') = 'A8032AD81730'\n\n return await athenaClient.query(queryData)\n}\n\nexport async function renderJasperReport(context: any) {\n const {\n state: { domain },\n query\n } = context\n\n const template = await STORAGE.readFile(query['reportTemplate'] || 'dynamic_header_sample.jrxml', 'utf-8')\n let templateType = query['templateType'] || 'crosstab'\n let parsedData = []\n\n // @todo: get dataset timezone\n /**\n * const variables = await gql(dataSet(id:${dataSetId}) {\n * name, description, partition_keys, timezone\n * })\n */\n\n query['domain'] = domain?.subdomain\n query['timezone'] = domain?.timezone\n const queryResult = await queryAthena(query)\n const rows = queryResult.Items\n\n if (!rows.length) {\n return '<h3>Not found result.</h3>'\n } else {\n const firstRow = rows[0]\n // uses the first row values as data-set has no history data.\n const parameters = {\n name: firstRow.name,\n description: firstRow.description,\n ...query\n }\n\n if (templateType === 'crosstab') {\n parsedData = pivotData(rows)\n } else {\n parsedData = parseJsonDataField(rows)\n }\n\n const formData = new FormData()\n formData.append('template', template)\n formData.append('jsonString', JSON.stringify(parsedData))\n formData.append('parameters', JSON.stringify(parameters))\n\n const { reportView } = query\n const subpath = reportView ? reportView.split('/').filter(x => x).join('/') : ''\n const reportUrl = `${PROTOCOL || 'http'}://${HOST}:${PORT}/${subpath}`\n const response = await fetch(reportUrl, {\n method: 'POST',\n body: formData\n })\n\n return await response.text()\n }\n}\n"]}