@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
package/README.md CHANGED
@@ -1,13 +1,9 @@
1
1
  # dataset
2
- ## partition keys
2
+ ## Architecture for data-samples
3
+ ![Architecture for data-samples](./assets/data-samples.jpg)
4
+ ## Partition Keys
3
5
  At the early stage, partition keys are desinged for dynamic partitioning for Athena. But It will be required so many AWS Glue crawlers also by each 'data-sets'.
4
- Now partition keys are fixed with some cases. Default is daily dataset, which has S3 Key like 'domain={domain}/datasetid={datasetid}/year=2022/month=3/day=25'. It will be set default with empty value in 'data-sets'.
5
6
 
6
- In another case, it is necessary to divide by hours or minutes and store them. This datetime item must be included in the S3 key. It will be required a new Glue Crawler and a Glue Catalog Table. For example if you need to separate directory hourly, you should add "%H" for 'hour' to partition_keys for this. and also should add a new crawler. And it would be needed a column to know it is daily or hourly or minutely for reporting client side. It's not developed yet.
7
- Look at the below picture for understanding.
8
-
9
- ![Architecture for partitioning](./assets/data-samples.jpg)
10
-
11
- These tasks are related for S3 request limitations.
7
+ Partition keys are related for S3 request limitations.
12
8
  > __3,500 PUT/COPY/POST/DELETE or 5,500 GET/HEAD requests per second per prefix in a bucket__
13
9
  - https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html
Binary file
@@ -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 DataOocListPage 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 DataOocListPage extends connect(store)(localize(i18next)(PageView))
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
  }
@@ -70,14 +60,42 @@ export class DataOocListPage extends connect(store)(localize(i18next)(PageView))
70
60
  }
71
61
 
72
62
  render() {
63
+ const mode = this.mode || (isMobileDevice() ? 'LIST' : 'GRID')
64
+
73
65
  return html`
74
66
  <ox-grist
75
- .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
67
+ .mode=${mode}
76
68
  .config=${this.gristConfig}
77
69
  .fetchHandler=${this.fetchHandler.bind(this)}
70
+ url-params-sensitive
78
71
  >
79
- <div slot="headroom" id="filters">
80
- <ox-filters-form></ox-filters-form>
72
+ <div slot="headroom">
73
+ <div id="filters">
74
+ <ox-filters-form></ox-filters-form>
75
+ </div>
76
+
77
+ <div id="sorters">
78
+ Sort
79
+ <mwc-icon
80
+ @click=${e => {
81
+ const target = e.currentTarget
82
+ this.renderRoot.querySelector('#sorter-control').open({
83
+ right: 0,
84
+ top: target.offsetTop + target.offsetHeight
85
+ })
86
+ }}
87
+ >expand_more</mwc-icon
88
+ >
89
+ <ox-popup id="sorter-control">
90
+ <ox-sorters-control> </ox-sorters-control>
91
+ </ox-popup>
92
+ </div>
93
+
94
+ <div id="modes">
95
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
96
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
97
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
98
+ </div>
81
99
  </div>
82
100
  </ox-grist>
83
101
  `
@@ -87,30 +105,7 @@ export class DataOocListPage extends connect(store)(localize(i18next)(PageView))
87
105
  return this.renderRoot.querySelector('ox-grist')
88
106
  }
89
107
 
90
- // update with url params value
91
- _updateSearchConfig(lifecycle) {
92
- // this.searchConfig = this.searchConfig.map(conf => {
93
- // if (conf.name in lifecycle.params) {
94
- // conf.value = lifecycle.params[conf.name]
95
- // } else {
96
- // delete conf.value
97
- // }
98
- // return conf
99
- // })
100
- }
101
-
102
- // set default field value to record with searchConfig
103
- _setDefaultFieldsValue(fields) {
104
- // this.searchConfig.forEach(conf => {
105
- // if (!fields[conf.name] && conf.value) {
106
- // fields[conf.name] = conf.value
107
- // }
108
- // })
109
- }
110
-
111
108
  async pageInitialized(lifecycle) {
112
- this._updateSearchConfig(lifecycle)
113
-
114
109
  this.gristConfig = {
115
110
  list: {
116
111
  fields: ['dataSet', 'data', 'spec', 'correctiveAction', 'corrector', 'correctedAt', 'collectedAt', 'creator']
@@ -321,15 +316,15 @@ export class DataOocListPage extends connect(store)(localize(i18next)(PageView))
321
316
  this.grist.fetch()
322
317
  }
323
318
 
324
- async pageUpdated(changes, lifecycle) {
325
- if (this.active) {
326
- // update with url params value
327
- this._updateSearchConfig(lifecycle)
328
- await this.updateComplete
319
+ // async pageUpdated(changes, lifecycle) {
320
+ // if (this.active) {
321
+ // // update with url params value
322
+ // this._updateSearchConfig(lifecycle)
323
+ // await this.updateComplete
329
324
 
330
- this.grist.fetch()
331
- }
332
- }
325
+ // this.grist.fetch()
326
+ // }
327
+ // }
333
328
 
334
329
  async fetchHandler({ page, limit, sortings = [], filters = [] }) {
335
330
  const response = await client.query({
@@ -0,0 +1,113 @@
1
+ import { css, html } from 'lit'
2
+ import '@operato/data-grist'
3
+ import '@things-factory/form-ui'
4
+
5
+ import { i18next, localize } from '@operato/i18n'
6
+ import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
7
+ import { PageView } from '@things-factory/shell'
8
+ import { getCookie, encodeFormParams } from '@things-factory/utils'
9
+
10
+ class DataReportEmbedPage extends localize(i18next)(PageView) {
11
+ static get properties() {
12
+ return {
13
+ _grnNo: String,
14
+ _status: String,
15
+ resourceId: String,
16
+ params: Object
17
+ }
18
+ }
19
+
20
+ static get styles() {
21
+ return [
22
+ ScrollbarStyles,
23
+ css`
24
+ :host {
25
+ display: flex;
26
+ flex-direction: column;
27
+ padding: 0;
28
+ }
29
+
30
+ #container {
31
+ flex: 1;
32
+ padding: 0;
33
+ margin: 0;
34
+ border: 0;
35
+ }
36
+ `
37
+ ]
38
+ }
39
+
40
+ get context() {
41
+ const filters = [
42
+ {
43
+ name: 'workDateRange',
44
+ type: 'date',
45
+ label: 'date',
46
+ operator: 'between'
47
+ }
48
+ ]
49
+ var actions = [
50
+ {
51
+ title: i18next.t('button.print'),
52
+ action: () => {
53
+ this.renderRoot.querySelector('iframe').contentWindow.print()
54
+ },
55
+ ...CommonButtonStyles.print
56
+ }
57
+ ]
58
+ return {
59
+ title: 'jasper-report',
60
+ actions,
61
+ filters
62
+ }
63
+ }
64
+ render() {
65
+ return html`
66
+ <ox-filters-form-base
67
+ .filters=${this.context.filters}
68
+ @filters-change=${e => {
69
+ console.log('filters changed', e.detail)
70
+ this._reportTemplate(e.detail.filters)
71
+ }}
72
+ url-params-sensitive
73
+ ></ox-filters-form-base>
74
+ <iframe id="container" frameborder="0" style="border:0"></iframe>
75
+ `
76
+ }
77
+
78
+ async pageUpdated(changes, lifecycle) {
79
+ if (this.active) {
80
+ this.updateContext()
81
+ }
82
+ }
83
+
84
+ async _reportTemplate(filters) {
85
+
86
+ // convert filters array to object
87
+ const params = filters.reduce((acc,curr) => (acc[curr.name] = curr.value, acc), {})
88
+ Object.assign(params, this.lifecycle.params)
89
+
90
+ const { workDateRange, workShift, reportView } = params
91
+
92
+ /** ignoring date conditions */
93
+ if (!workDateRange[0] || !workDateRange[1] || (workDateRange[0] > workDateRange[1])) {
94
+ return
95
+ }
96
+
97
+ /** urlencoded params including test values */
98
+ const urlParams = {
99
+ table: 'samples',
100
+ dataSetId: this.lifecycle.resourceId,
101
+ fromWorkDate: workDateRange[0],
102
+ toWorkDate: workDateRange[1],
103
+ token: getCookie('access_token'),
104
+ workShift
105
+ }
106
+
107
+ const encodedUrlParams = encodeFormParams(urlParams)
108
+
109
+ this.shadowRoot.querySelector('#container').src = `${reportView}?params=${encodedUrlParams}`
110
+ }
111
+ }
112
+
113
+ window.customElements.define('data-report-embed-page', DataReportEmbedPage)
@@ -25,7 +25,7 @@ const USECASE_OPTIONS = () => {
25
25
  }
26
26
 
27
27
  const showMonitorView = (columns, data, column, record, rowIndex) => {
28
- const { name, monitorType, monitorView } = record
28
+ const { id, name, monitorType, monitorView } = record
29
29
  const title = `${name} - ${i18next.t('title.data-monitor-view')}`
30
30
 
31
31
  switch (monitorType) {
@@ -73,7 +73,7 @@ const showMonitorView = (columns, data, column, record, rowIndex) => {
73
73
  }
74
74
 
75
75
  const showReportView = (columns, data, column, record, rowIndex) => {
76
- const { name, reportType, reportView } = record
76
+ const { id, name, reportType, reportView, reportTemplate } = record
77
77
  const title = `${name} - ${i18next.t('title.data-report-view')}`
78
78
 
79
79
  switch (reportType) {
@@ -85,21 +85,22 @@ const showReportView = (columns, data, column, record, rowIndex) => {
85
85
  })
86
86
  break
87
87
 
88
- case 'custom':
88
+ case 'embed':
89
89
  const board = {
90
90
  id: reportView
91
91
  }
92
- openPopup(html` <div style="background-color: white;">Under construction</div> `, {
93
- closable: true,
94
- backdrop: true,
95
- size: 'large',
96
- title
97
- })
92
+ navigate(`/data-report-embed/${id}?reportView=${encodeURIComponent(reportView)}`)
93
+ // openPopup(html` <div style="background-color: white;">Under construction</div> `, {
94
+ // closable: true,
95
+ // backdrop: true,
96
+ // size: 'large',
97
+ // title
98
+ // })
98
99
 
99
100
  break
100
101
 
101
102
  case 'page':
102
- navigate(reportView)
103
+ navigate(reportView + `/${id}?template=${encodeURIComponent(reportTemplate)}`)
103
104
  break
104
105
 
105
106
  case 'external':
@@ -421,6 +422,7 @@ export class DataReportListPage extends connect(store)(localize(i18next)(PageVie
421
422
  }
422
423
  reportType
423
424
  reportView
425
+ reportTemplate
424
426
  updater {
425
427
  id
426
428
  name
@@ -0,0 +1,120 @@
1
+ import { css, html } from 'lit'
2
+ import '@operato/data-grist'
3
+ import '@operato/form'
4
+
5
+ import { i18next, localize } from '@operato/i18n'
6
+ import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
7
+ import { PageView } from '@things-factory/shell'
8
+
9
+ class JasperReportOocsPage extends localize(i18next)(PageView) {
10
+ static get properties() {
11
+ return {
12
+ _grnNo: String,
13
+ _status: String,
14
+ resourceId: String,
15
+ params: Object
16
+ }
17
+ }
18
+
19
+ static get styles() {
20
+ return [
21
+ ScrollbarStyles,
22
+ css`
23
+ :host {
24
+ display: flex;
25
+ flex-direction: column;
26
+ padding: 0;
27
+ }
28
+
29
+ #container {
30
+ flex: 1;
31
+ padding: 0;
32
+ margin: 0;
33
+ border: 0;
34
+ }
35
+ `
36
+ ]
37
+ }
38
+
39
+ get context() {
40
+ const filters = [
41
+ {
42
+ name: 'workDateRange',
43
+ type: 'date',
44
+ label: 'date',
45
+ operator: 'between'
46
+ }
47
+ ]
48
+ var actions = [
49
+ {
50
+ title: i18next.t('button.print'),
51
+ action: () => {
52
+ this.renderRoot.querySelector('iframe').contentWindow.print()
53
+ },
54
+ ...CommonButtonStyles.print
55
+ }
56
+ ]
57
+ return {
58
+ title: 'jasper-report',
59
+ actions,
60
+ filters
61
+ }
62
+ }
63
+
64
+ render() {
65
+ return html`
66
+ <ox-filters-form-base
67
+ .filters=${this.context.filters}
68
+ @filters-change=${e => {
69
+ console.log('filters changed', e.detail)
70
+ this._reportTemplate(e.detail.filters)
71
+ }}
72
+ url-params-sensitive
73
+ ></ox-filters-form-base>
74
+ <iframe id="container"></iframe>
75
+ `
76
+ }
77
+
78
+ get searchForm() {
79
+ return this.renderRoot.querySelector('ox-filters-form-base')
80
+ }
81
+ async pageUpdated(changes, lifecycle) {
82
+ if (this.active) {
83
+ this.updateContext()
84
+ }
85
+ }
86
+
87
+ async _reportTemplate(filters) {
88
+ const params = filters.reduce((acc,curr) => (acc[curr.name] = curr.value, acc), {})
89
+ Object.assign(params, this.lifecycle.params)
90
+
91
+ const { workDateRange, workShift, template } = params
92
+
93
+ /** ignoring date conditions */
94
+ if (!workDateRange[0] || !workDateRange[1] || (workDateRange[0] > workDateRange[1])) {
95
+ return
96
+ }
97
+
98
+ /** urlencoded params including test values */
99
+ const urlParams = {
100
+ table: 'ooc',
101
+ dataSetId: this.lifecycle.resourceId,
102
+ fromWorkDate: workDateRange[0],
103
+ toWorkDate: workDateRange[1],
104
+ workShift,
105
+ template,
106
+ templateType: 'normal'
107
+ }
108
+
109
+ const encodedUrlParams = Object.keys(urlParams).filter(key => {
110
+ // ignore empty
111
+ return !!urlParams[key]
112
+ }).map(key =>
113
+ `${key}=${encodeURIComponent(urlParams[key])}`
114
+ ).join('&')
115
+
116
+ this.shadowRoot.querySelector('#container').src = `/data-report/jasper?${encodedUrlParams}`
117
+ }
118
+ }
119
+
120
+ window.customElements.define('jasper-report-oocs-page', JasperReportOocsPage)
@@ -0,0 +1,120 @@
1
+ import { css, html } from 'lit'
2
+ import '@operato/data-grist'
3
+ import '@operato/form'
4
+
5
+ import { i18next, localize } from '@operato/i18n'
6
+ import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
7
+ import { PageView } from '@things-factory/shell'
8
+
9
+ class JasperReportSamplesCrosstabPage extends localize(i18next)(PageView) {
10
+ static get properties() {
11
+ return {
12
+ _grnNo: String,
13
+ _status: String,
14
+ resourceId: String,
15
+ params: Object
16
+ }
17
+ }
18
+
19
+ static get styles() {
20
+ return [
21
+ ScrollbarStyles,
22
+ css`
23
+ :host {
24
+ display: flex;
25
+ flex-direction: column;
26
+ padding: 0;
27
+ }
28
+
29
+ #container {
30
+ flex: 1;
31
+ padding: 0;
32
+ margin: 0;
33
+ border: 0;
34
+ }
35
+ `
36
+ ]
37
+ }
38
+
39
+ get context() {
40
+ const filters = [
41
+ {
42
+ name: 'workDateRange',
43
+ type: 'date',
44
+ label: 'date',
45
+ operator: 'between'
46
+ }
47
+ ]
48
+ var actions = [
49
+ {
50
+ title: i18next.t('button.print'),
51
+ action: () => {
52
+ this.renderRoot.querySelector('iframe').contentWindow.print()
53
+ },
54
+ ...CommonButtonStyles.print
55
+ }
56
+ ]
57
+ return {
58
+ title: 'jasper-report',
59
+ actions,
60
+ filters
61
+ }
62
+ }
63
+
64
+ render() {
65
+ return html`
66
+ <ox-filters-form-base
67
+ .filters=${this.context.filters}
68
+ @filters-change=${e => {
69
+ console.log('filters changed', e.detail)
70
+ this._reportTemplate(e.detail.filters)
71
+ }}
72
+ url-params-sensitive
73
+ ></ox-filters-form-base>
74
+ <iframe id="container"></iframe>
75
+ `
76
+ }
77
+
78
+ get searchForm() {
79
+ return this.renderRoot.querySelector('ox-filters-form-base')
80
+ }
81
+ async pageUpdated(changes, lifecycle) {
82
+ if (this.active) {
83
+ this.updateContext()
84
+ }
85
+ }
86
+
87
+ async _reportTemplate(filters) {
88
+ const params = filters.reduce((acc,curr) => (acc[curr.name] = curr.value, acc), {})
89
+ Object.assign(params, this.lifecycle.params)
90
+
91
+ const { workDateRange, workShift, template } = params
92
+
93
+ /** ignoring date conditions */
94
+ if (!workDateRange[0] || !workDateRange[1] || (workDateRange[0] > workDateRange[1])) {
95
+ return
96
+ }
97
+
98
+ /** urlencoded params including test values */
99
+ const urlParams = {
100
+ table: 'samples',
101
+ dataSetId: this.lifecycle.resourceId,
102
+ fromWorkDate: workDateRange[0],
103
+ toWorkDate: workDateRange[1],
104
+ workShift,
105
+ template,
106
+ templateType: 'crosstab'
107
+ }
108
+
109
+ const encodedUrlParams = Object.keys(urlParams).filter(key => {
110
+ // ignore empty
111
+ return !!urlParams[key]
112
+ }).map(key =>
113
+ `${key}=${encodeURIComponent(urlParams[key])}`
114
+ ).join('&')
115
+
116
+ this.shadowRoot.querySelector('#container').src = `/data-report/jasper?${encodedUrlParams}`
117
+ }
118
+ }
119
+
120
+ window.customElements.define('jasper-report-samples-crosstab-page', JasperReportSamplesCrosstabPage)
@@ -0,0 +1,120 @@
1
+ import { css, html } from 'lit'
2
+ import '@operato/data-grist'
3
+ import '@operato/form'
4
+
5
+ import { i18next, localize } from '@operato/i18n'
6
+ import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
7
+ import { PageView } from '@things-factory/shell'
8
+
9
+ class JasperReportSamplesPage extends localize(i18next)(PageView) {
10
+ static get properties() {
11
+ return {
12
+ _grnNo: String,
13
+ _status: String,
14
+ resourceId: String,
15
+ params: Object
16
+ }
17
+ }
18
+
19
+ static get styles() {
20
+ return [
21
+ ScrollbarStyles,
22
+ css`
23
+ :host {
24
+ display: flex;
25
+ flex-direction: column;
26
+ padding: 0;
27
+ }
28
+
29
+ #container {
30
+ flex: 1;
31
+ padding: 0;
32
+ margin: 0;
33
+ border: 0;
34
+ }
35
+ `
36
+ ]
37
+ }
38
+
39
+ get context() {
40
+ const filters = [
41
+ {
42
+ name: 'workDateRange',
43
+ type: 'date',
44
+ label: 'date',
45
+ operator: 'between'
46
+ }
47
+ ]
48
+ var actions = [
49
+ {
50
+ title: i18next.t('button.print'),
51
+ action: () => {
52
+ this.renderRoot.querySelector('iframe').contentWindow.print()
53
+ },
54
+ ...CommonButtonStyles.print
55
+ }
56
+ ]
57
+ return {
58
+ title: 'jasper-report',
59
+ actions,
60
+ filters
61
+ }
62
+ }
63
+
64
+ render() {
65
+ return html`
66
+ <ox-filters-form-base
67
+ .filters=${this.context.filters}
68
+ @filters-change=${e => {
69
+ console.log('filters changed', e.detail)
70
+ this._reportTemplate(e.detail.filters)
71
+ }}
72
+ url-params-sensitive
73
+ ></ox-filters-form-base>
74
+ <iframe id="container"></iframe>
75
+ `
76
+ }
77
+
78
+ get searchForm() {
79
+ return this.renderRoot.querySelector('ox-filters-form-base')
80
+ }
81
+ async pageUpdated(changes, lifecycle) {
82
+ if (this.active) {
83
+ this.updateContext()
84
+ }
85
+ }
86
+
87
+ async _reportTemplate(filters) {
88
+ const params = filters.reduce((acc,curr) => (acc[curr.name] = curr.value, acc), {})
89
+ Object.assign(params, this.lifecycle.params)
90
+
91
+ const { workDateRange, workShift, template } = params
92
+
93
+ /** ignoring date conditions */
94
+ if (!workDateRange[0] || !workDateRange[1] || (workDateRange[0] > workDateRange[1])) {
95
+ return
96
+ }
97
+
98
+ /** urlencoded params including test values */
99
+ const urlParams = {
100
+ table: 'samples',
101
+ dataSetId: this.lifecycle.resourceId,
102
+ fromWorkDate: workDateRange[0],
103
+ toWorkDate: workDateRange[1],
104
+ workShift,
105
+ template,
106
+ templateType: 'normal'
107
+ }
108
+
109
+ const encodedUrlParams = Object.keys(urlParams).filter(key => {
110
+ // ignore empty
111
+ return !!urlParams[key]
112
+ }).map(key =>
113
+ `${key}=${encodeURIComponent(urlParams[key])}`
114
+ ).join('&')
115
+
116
+ this.shadowRoot.querySelector('#container').src = `/data-report/jasper?${encodedUrlParams}`
117
+ }
118
+ }
119
+
120
+ window.customElements.define('jasper-report-samples-page', JasperReportSamplesPage)